Better frame scheduling

This commit is contained in:
DBotThePony 2023-09-18 00:02:16 +07:00
parent a41037826c
commit 1de2b4c167
Signed by: DBot
GPG Key ID: DCC23B5715498507
3 changed files with 192 additions and 158 deletions

View File

@ -242,8 +242,8 @@ fun main() {
//client.camera.pos.y = ent.position.y.toFloat()
client.camera.pos += Vector2f(
(if (client.input.KEY_LEFT_DOWN || client.input.KEY_A_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f) + (if (client.input.KEY_RIGHT_DOWN || client.input.KEY_D_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f),
(if (client.input.KEY_UP_DOWN || client.input.KEY_W_DOWN) client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f) + (if (client.input.KEY_DOWN_DOWN || client.input.KEY_S_DOWN) -client.frameRenderTime.toFloat() * 32f / client.settings.zoom else 0f)
(if (client.input.KEY_LEFT_DOWN || client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f) + (if (client.input.KEY_RIGHT_DOWN || client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f),
(if (client.input.KEY_UP_DOWN || client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f) + (if (client.input.KEY_DOWN_DOWN || client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE.toFloat() * 32f / client.settings.zoom else 0f)
)
//println(client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1)

View File

@ -85,6 +85,8 @@ import ru.dbotthepony.kstarbound.util.set
import ru.dbotthepony.kstarbound.util.traverseJsonPath
import java.io.*
import java.text.DateFormat
import java.time.Duration
import java.time.temporal.ChronoUnit
import java.util.function.BiConsumer
import java.util.function.BinaryOperator
import java.util.function.Function
@ -95,7 +97,8 @@ import kotlin.collections.ArrayList
import kotlin.random.Random
object Starbound : ISBFileLocator {
const val TICK_TIME_ADVANCE = 1.0 / 60.0
const val TICK_TIME_ADVANCE = 0.01666666666666664
const val TICK_TIME_ADVANCE_NANOS = 16_666_666L
// currently it saves only 4 megabytes of ram on pretty big modpack
// Hrm.

View File

@ -49,7 +49,6 @@ import ru.dbotthepony.kstarbound.client.world.ClientWorld
import ru.dbotthepony.kstarbound.defs.image.Image
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
import ru.dbotthepony.kstarbound.util.JVMTimeSource
import ru.dbotthepony.kstarbound.util.formatBytesShort
import ru.dbotthepony.kstarbound.world.LightCalculator
import ru.dbotthepony.kstarbound.world.api.ICellAccess
@ -742,26 +741,41 @@ class StarboundClient : Closeable {
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
}
var frameRenderTime = 0.0
// nanoseconds
var frameRenderTime = 0L
private set
private var lastRender = JVMTimeSource.INSTANCE.seconds
val framesPerSecond get() = if (frameRenderTime == 0.0) 1.0 else 1.0 / frameRenderTime
private val frameRenderTimes = DoubleArray(60) { 1.0 }
private var nextRender = System.nanoTime()
private val frameRenderTimes = LongArray(60) { 1L }
private var frameRenderIndex = 0
private val renderWaitTimes = LongArray(60) { 1L }
private var renderWaitIndex = 0
private var lastRender = System.nanoTime()
val averageFramesPerSecond: Double get() {
val averageRenderWait: Double get() {
var sum = 0.0
for (value in frameRenderTimes) {
for (value in renderWaitTimes)
sum += value
}
if (sum == 0.0)
return 0.0
return frameRenderTimes.size / sum
sum /= 1_000_000_000.0
return sum / renderWaitTimes.size
}
val averageRenderTime: Double get() {
var sum = 0.0
for (value in frameRenderTimes)
sum += value
if (sum == 0.0)
return 0.0
sum /= 1_000_000_000.0
return sum / frameRenderTimes.size
}
val settings = ClientSettings()
@ -828,15 +842,24 @@ class StarboundClient : Closeable {
fun renderFrame(): Boolean {
ensureSameThread()
val diff = JVMTimeSource.INSTANCE.seconds - lastRender
var diff = nextRender - System.nanoTime()
var yields = 0
if (diff < Starbound.TICK_TIME_ADVANCE)
LockSupport.parkNanos(((Starbound.TICK_TIME_ADVANCE - diff) * 1_000_000_000.0).toLong())
// try to sleep until next frame as precise as possible
while (diff > 0L) {
if (diff >= 1_500_000L) {
LockSupport.parkNanos(1_000_000L)
} else {
Thread.yield()
yields++
}
frameRenderTime = JVMTimeSource.INSTANCE.seconds - lastRender
frameRenderTimes[++frameRenderIndex % frameRenderTimes.size] = frameRenderTime
lastRender = JVMTimeSource.INSTANCE.seconds
diff = nextRender - System.nanoTime()
}
val mark = System.nanoTime()
try {
if (GLFW.glfwWindowShouldClose(window)) {
close()
return false
@ -974,8 +997,9 @@ class StarboundClient : Closeable {
val runtime = Runtime.getRuntime()
font.render("FPS: ${(averageFramesPerSecond * 100f).toInt() / 100f}", scale = 0.4f)
font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = font.lineHeight * 0.5f, scale = 0.4f)
font.render("Latency: ${(averageRenderWait * 1_00000.0).toInt() / 100f}ms", scale = 0.4f)
font.render("Frame: ${(averageRenderTime * 1_00000.0).toInt() / 100f}ms", y = font.lineHeight * 0.6f, scale = 0.4f)
font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = font.lineHeight * 1.2f, scale = 0.4f)
GLFW.glfwSwapBuffers(window)
GLFW.glfwPollEvents()
@ -986,6 +1010,13 @@ class StarboundClient : Closeable {
cleanup()
return true
} finally {
frameRenderTime = System.nanoTime() - mark
frameRenderTimes[++frameRenderIndex % frameRenderTimes.size] = frameRenderTime
renderWaitTimes[++renderWaitIndex % renderWaitTimes.size] = System.nanoTime() - lastRender
lastRender = System.nanoTime()
nextRender = mark + Starbound.TICK_TIME_ADVANCE_NANOS
}
}
fun onTermination(lambda: () -> Unit) {