far improved experimental fps

This commit is contained in:
Bixilon 2021-08-31 19:29:04 +02:00
parent 8bf7246bb7
commit 390a7e7f99
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
10 changed files with 283 additions and 81 deletions

View File

@ -19,6 +19,6 @@ data class OtherGameConfig(
@Json(name = "anti_moire_pattern") var antiMoirePattern: Boolean = true,
@Json(name = "flower_random_offset") var flowerRandomOffset: Boolean = true,
@Json(name = "block_outline") var blockOutline: BlockOutline = BlockOutline(),
@Json(name = "magic_fps") var magicFPS: Boolean = false,
@Json(name = "experimental_fps") var experimentalFPS: Boolean = false,
@Json(name = "super_dumb_advanced_setting_leave_at_1") var swapInterval: Int = 1,
)

View File

@ -1,78 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.util.SystemInformation
import java.util.concurrent.ThreadLocalRandom
class RenderStats {
var fpsLastSecond = -1
private set
var minFrameTime = Long.MAX_VALUE
private set
var maxFrameTime = 0L
private set
var avgFrameTime = 0L
private set
var frames = 0L
private set
private var lastFPSCalcTime = 0L
private var framesLastSecond = 0
private var frameTime = 0L
private var frameStartTime = 0L
fun startFrame() {
frameStartTime = System.nanoTime()
}
fun endDraw() {
}
fun endFrame() {
val frameEndTime = System.nanoTime()
val frameTime = frameEndTime - frameStartTime
if (frameTime < minFrameTime) {
minFrameTime = frameTime
}
if (frameTime > maxFrameTime) {
maxFrameTime = frameTime
}
if (frameEndTime - lastFPSCalcTime > 1E9) {
// 1 second
fpsLastSecond = if (Minosoft.config.config.game.other.magicFPS) {
ThreadLocalRandom.current().nextInt(MAGIC_FPS - 10, MAGIC_FPS)
} else {
framesLastSecond
}
framesLastSecond = 0
lastFPSCalcTime = frameEndTime
this.frameTime = 0
}
frames++
framesLastSecond++
this.frameTime += frameTime
this.avgFrameTime = this.frameTime / framesLastSecond
}
companion object {
private var MAGIC_FPS = ThreadLocalRandom.current().nextLong(SystemInformation.PROCESSOR_SPEED / 10000000 - 100, SystemInformation.PROCESSOR_SPEED / 10000000 + 100).toInt()
}
}

View File

@ -22,6 +22,7 @@ import de.bixilon.minosoft.gui.rendering.gui.hud.atlas.TextureLike
import de.bixilon.minosoft.gui.rendering.gui.hud.atlas.TextureLikeTexture
import de.bixilon.minosoft.gui.rendering.input.key.RenderWindowInputHandler
import de.bixilon.minosoft.gui.rendering.modding.events.*
import de.bixilon.minosoft.gui.rendering.stats.AbstractRenderStats
import de.bixilon.minosoft.gui.rendering.system.base.IntegratedBufferTypes
import de.bixilon.minosoft.gui.rendering.system.base.PolygonModes
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
@ -37,6 +38,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.KUtil.decide
import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
import de.bixilon.minosoft.util.MMath.round10
import de.bixilon.minosoft.util.Queue
import de.bixilon.minosoft.util.Stopwatch
import de.bixilon.minosoft.util.logging.Log
@ -53,7 +55,7 @@ class RenderWindow(
var initialized = false
private set
private lateinit var renderThread: Thread
val renderStats = RenderStats()
val renderStats: AbstractRenderStats = AbstractRenderStats.createInstance()
val inputHandler = RenderWindowInputHandler(this)
@ -293,7 +295,7 @@ class RenderWindow(
renderStats.endFrame()
if (RenderConstants.SHOW_FPS_IN_WINDOW_TITLE) {
window.title = "Minosoft | FPS: ${renderStats.fpsLastSecond}"
window.title = "Minosoft | FPS: ${renderStats.smoothAvgFPS.round10}"
}
}

View File

@ -0,0 +1,42 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.stats
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.util.avg.Average
interface AbstractRenderStats {
val avgFrameTime: Average<Long>
val smoothAvgFPS: Double
val avgFPS: Double
val totalFrames: Long
fun startFrame() {}
fun endDraw() {}
fun endFrame() {}
companion object {
fun createInstance(): AbstractRenderStats {
if (Minosoft.config.config.game.other.experimentalFPS) {
return ExperimentalRenderStats()
}
return RenderStats()
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.stats
import de.bixilon.minosoft.util.KUtil.nextFloat
import de.bixilon.minosoft.util.avg.Average
import de.bixilon.minosoft.util.avg.LongAverage
import glm_.func.common.clamp
import kotlin.random.Random
class ExperimentalRenderStats : AbstractRenderStats {
private val renderStats = RenderStats()
private val baseMultiplier = Random.nextFloat(1.0f, 1.5f)
private val baseJitter = Random.nextInt(0, 20)
override val avgFrameTime: Average<Long> = LongAverage(Long.MAX_VALUE)
private var lastSmoothFPSCalculationTime = 0L
override var smoothAvgFPS: Double = 0.0
get() {
val time = System.currentTimeMillis()
if (time - lastSmoothFPSCalculationTime > 100) {
field = avgFPS
lastSmoothFPSCalculationTime = time
}
return field
}
private set
override val avgFPS: Double
get() {
val avgFPS = renderStats.avgFPS
val multiplier = 3.0f * baseMultiplier * Random.nextFloat(0.9f, 1.1f)
var fps = avgFPS * multiplier
fps += baseJitter
fps += Random.nextInt(-10, 10)
return fps.clamp(0.0, 10000.0)
}
init {
avgFrameTime.add(5000000L) // ToDo: Add real stats
}
override val totalFrames: Long
get() = renderStats.totalFrames
override fun startFrame() {
renderStats.startFrame()
}
override fun endDraw() {
renderStats.endDraw()
}
override fun endFrame() {
renderStats.endFrame()
}
}

View File

@ -0,0 +1,60 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.stats
import de.bixilon.minosoft.util.avg.LongAverage
class RenderStats : AbstractRenderStats {
override val avgFrameTime: LongAverage = LongAverage(1L * 1000000000L) // 1 second * SECOND_SCALE
override var totalFrames: Long = 0L
private set
private var lastFrameStartTime = -1L
private var lastSmoothFPSCalculationTime = 0L
override var smoothAvgFPS: Double = 0.0
get() {
val time = System.currentTimeMillis()
if (time - lastSmoothFPSCalculationTime > 100) {
field = avgFPS
lastSmoothFPSCalculationTime = time
}
return field
}
private set
override val avgFPS: Double
get() {
val avgFrameTime = avgFrameTime.avg
return 1000000000L / avgFrameTime.toDouble() // SECOND_SCALE
}
override fun startFrame() {
lastFrameStartTime = System.nanoTime()
}
override fun endFrame() {
val time = System.nanoTime()
val delta = time - lastFrameStartTime
avgFrameTime += delta
totalFrames++
}
}

View File

@ -76,6 +76,8 @@ object MMath {
val Float.round10: Float get() = (this * 10).toInt().toFloat() / 10f
val Double.round10: Double get() = (this * 10).toInt().toDouble() / 10.0
fun round10Up(value: Float): Int {
val intValue = value.toInt()
val rest = value / intValue

View File

@ -16,6 +16,7 @@ package de.bixilon.minosoft.util
import de.bixilon.minosoft.util.UnitFormatter.formatBytes
import oshi.SystemInfo
@Deprecated(message = "Will be refactored")
object SystemInformation {
val RUNTIME = Runtime.getRuntime()
val SYSTEM_INFO = SystemInfo()

View File

@ -0,0 +1,28 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.avg
interface Average<T> {
val nanos: Long
val avg: T
fun cleanup()
fun add(value: T)
operator fun plusAssign(value: T) {
add(value)
}
}

View File

@ -0,0 +1,69 @@
/*
* Minosoft
* Copyright (C) 2021 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.util.avg
import de.bixilon.minosoft.util.KUtil.synchronizedListOf
import de.bixilon.minosoft.util.KUtil.toSynchronizedList
class LongAverage(override val nanos: Long) : Average<Long> {
/**
* List of <Add Time (nanos), Value>
*/
private val data: MutableList<Pair<Long, Long>> = synchronizedListOf()
private var updated = false
private var lastAVG = 0L
override val avg: Long
@Synchronized get() {
if (!updated) {
return lastAVG
}
cleanup()
val data = data.toSynchronizedList()
var total = 0L
for ((_, value) in data) {
total += value
}
lastAVG = total / data.size
updated = false
return lastAVG
}
override fun cleanup() {
val time = System.nanoTime()
var indexOffset = 0
for ((index, pair) in data.toSynchronizedList().withIndex()) {
val (addTime, _) = pair
val addDelta = time - addTime
if (addDelta - nanos >= 0L) {
// remove
data.removeAt(index - indexOffset)
indexOffset++
updated = true
} else {
break
}
}
}
override fun add(value: Long) {
cleanup()
val time = System.nanoTime()
data += Pair(time, value)
updated = true
}
}