Better frame scheduling
This commit is contained in:
parent
a41037826c
commit
1de2b4c167
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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,164 +842,181 @@ 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
|
||||
|
||||
if (GLFW.glfwWindowShouldClose(window)) {
|
||||
close()
|
||||
return false
|
||||
diff = nextRender - System.nanoTime()
|
||||
}
|
||||
|
||||
val world = world
|
||||
val mark = System.nanoTime()
|
||||
|
||||
if (!isRenderingGame) {
|
||||
cleanup()
|
||||
GLFW.glfwPollEvents()
|
||||
try {
|
||||
if (GLFW.glfwWindowShouldClose(window)) {
|
||||
close()
|
||||
return false
|
||||
}
|
||||
|
||||
val world = world
|
||||
|
||||
if (!isRenderingGame) {
|
||||
cleanup()
|
||||
GLFW.glfwPollEvents()
|
||||
|
||||
if (world != null) {
|
||||
if (Starbound.initialized)
|
||||
world.think()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
if (world != null) {
|
||||
updateViewportParams()
|
||||
val layers = LayeredRenderer()
|
||||
|
||||
if (Starbound.initialized)
|
||||
world.think()
|
||||
|
||||
clearColor = RGBAColor.SLATE_GRAY
|
||||
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
|
||||
matrixStack.clear(viewportMatrixWorld)
|
||||
|
||||
matrixStack.push().last()
|
||||
.translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 2f) // центр экрана + координаты отрисовки мира
|
||||
.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера
|
||||
.translateWithMultiplication(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
|
||||
|
||||
for (lambda in onPreDrawWorld) {
|
||||
lambda.invoke(layers)
|
||||
}
|
||||
|
||||
for (i in onPostDrawWorldOnce.size - 1 downTo 0) {
|
||||
onPostDrawWorldOnce[i].invoke(layers)
|
||||
onPostDrawWorldOnce.removeAt(i)
|
||||
}
|
||||
|
||||
viewportLighting.clear()
|
||||
|
||||
world.addLayers(
|
||||
layers = layers,
|
||||
size = viewportRectangle)
|
||||
|
||||
layers.render(matrixStack)
|
||||
|
||||
val viewportLightingMem = viewportLightingMem
|
||||
|
||||
if (viewportLightingMem != null && !fullbright) {
|
||||
viewportLightingMem.position(0)
|
||||
BufferUtils.zeroBuffer(viewportLightingMem)
|
||||
viewportLightingMem.position(0)
|
||||
viewportLighting.calculate(viewportLightingMem, viewportLighting.width.coerceAtMost(4096), viewportLighting.height.coerceAtMost(4096))
|
||||
viewportLightingMem.position(0)
|
||||
|
||||
val old = textureUnpackAlignment
|
||||
textureUnpackAlignment = if (viewportLighting.width.coerceAtMost(4096) % 4 == 0) 4 else 1
|
||||
|
||||
viewportLightingTexture.upload(
|
||||
GL_RGB,
|
||||
viewportLighting.width.coerceAtMost(4096),
|
||||
viewportLighting.height.coerceAtMost(4096),
|
||||
GL_RGB,
|
||||
GL_UNSIGNED_BYTE,
|
||||
viewportLightingMem
|
||||
)
|
||||
|
||||
textureUnpackAlignment = old
|
||||
|
||||
viewportLightingTexture.textureMinFilter = GL_LINEAR
|
||||
//viewportLightingTexture.textureMagFilter = GL_NEAREST
|
||||
|
||||
//viewportLightingTexture.generateMips()
|
||||
|
||||
blendFunc = BlendFunc.MULTIPLY_BY_SRC
|
||||
|
||||
quadTexture(viewportLightingTexture) {
|
||||
it.quad(
|
||||
(viewportCellX).toFloat(),
|
||||
(viewportCellY).toFloat(),
|
||||
(viewportCellX + viewportCellWidth).toFloat(),
|
||||
(viewportCellY + viewportCellHeight).toFloat(),
|
||||
QuadTransformers.uv()
|
||||
)
|
||||
}
|
||||
|
||||
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
||||
}
|
||||
|
||||
world.physics.debugDraw()
|
||||
|
||||
for (lambda in onPostDrawWorld) {
|
||||
lambda.invoke()
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
matrixStack.clear(viewportMatrixScreen)
|
||||
|
||||
val thisTime = System.currentTimeMillis()
|
||||
|
||||
if (startupTextList.isNotEmpty() && thisTime <= finishStartupRendering) {
|
||||
var alpha = 1f
|
||||
|
||||
if (finishStartupRendering - thisTime < 1000L) {
|
||||
alpha = (finishStartupRendering - thisTime) / 1000f
|
||||
}
|
||||
|
||||
matrixStack.push()
|
||||
matrixStack.last().translateWithMultiplication(y = viewportHeight.toFloat())
|
||||
var shade = 255
|
||||
|
||||
for (i in startupTextList.size - 1 downTo 0) {
|
||||
val size = font.render(startupTextList[i], alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
|
||||
matrixStack.last().translateWithMultiplication(y = -size.height * 1.2f)
|
||||
|
||||
if (shade > 120) {
|
||||
shade -= 10
|
||||
}
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
for (fn in onDrawGUI) {
|
||||
fn.invoke()
|
||||
}
|
||||
|
||||
val runtime = Runtime.getRuntime()
|
||||
|
||||
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()
|
||||
input.think()
|
||||
|
||||
camera.think(Starbound.TICK_TIME_ADVANCE)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (world != null) {
|
||||
updateViewportParams()
|
||||
val layers = LayeredRenderer()
|
||||
|
||||
if (Starbound.initialized)
|
||||
world.think()
|
||||
|
||||
clearColor = RGBAColor.SLATE_GRAY
|
||||
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT)
|
||||
matrixStack.clear(viewportMatrixWorld)
|
||||
|
||||
matrixStack.push().last()
|
||||
.translateWithMultiplication(viewportWidth / 2f, viewportHeight / 2f, 2f) // центр экрана + координаты отрисовки мира
|
||||
.scale(x = settings.zoom * PIXELS_IN_STARBOUND_UNITf, y = settings.zoom * PIXELS_IN_STARBOUND_UNITf) // масштабируем до нужного размера
|
||||
.translateWithMultiplication(-camera.pos.x, -camera.pos.y) // перемещаем вид к камере
|
||||
|
||||
for (lambda in onPreDrawWorld) {
|
||||
lambda.invoke(layers)
|
||||
}
|
||||
|
||||
for (i in onPostDrawWorldOnce.size - 1 downTo 0) {
|
||||
onPostDrawWorldOnce[i].invoke(layers)
|
||||
onPostDrawWorldOnce.removeAt(i)
|
||||
}
|
||||
|
||||
viewportLighting.clear()
|
||||
|
||||
world.addLayers(
|
||||
layers = layers,
|
||||
size = viewportRectangle)
|
||||
|
||||
layers.render(matrixStack)
|
||||
|
||||
val viewportLightingMem = viewportLightingMem
|
||||
|
||||
if (viewportLightingMem != null && !fullbright) {
|
||||
viewportLightingMem.position(0)
|
||||
BufferUtils.zeroBuffer(viewportLightingMem)
|
||||
viewportLightingMem.position(0)
|
||||
viewportLighting.calculate(viewportLightingMem, viewportLighting.width.coerceAtMost(4096), viewportLighting.height.coerceAtMost(4096))
|
||||
viewportLightingMem.position(0)
|
||||
|
||||
val old = textureUnpackAlignment
|
||||
textureUnpackAlignment = if (viewportLighting.width.coerceAtMost(4096) % 4 == 0) 4 else 1
|
||||
|
||||
viewportLightingTexture.upload(
|
||||
GL_RGB,
|
||||
viewportLighting.width.coerceAtMost(4096),
|
||||
viewportLighting.height.coerceAtMost(4096),
|
||||
GL_RGB,
|
||||
GL_UNSIGNED_BYTE,
|
||||
viewportLightingMem
|
||||
)
|
||||
|
||||
textureUnpackAlignment = old
|
||||
|
||||
viewportLightingTexture.textureMinFilter = GL_LINEAR
|
||||
//viewportLightingTexture.textureMagFilter = GL_NEAREST
|
||||
|
||||
//viewportLightingTexture.generateMips()
|
||||
|
||||
blendFunc = BlendFunc.MULTIPLY_BY_SRC
|
||||
|
||||
quadTexture(viewportLightingTexture) {
|
||||
it.quad(
|
||||
(viewportCellX).toFloat(),
|
||||
(viewportCellY).toFloat(),
|
||||
(viewportCellX + viewportCellWidth).toFloat(),
|
||||
(viewportCellY + viewportCellHeight).toFloat(),
|
||||
QuadTransformers.uv()
|
||||
)
|
||||
}
|
||||
|
||||
blendFunc = BlendFunc.MULTIPLY_WITH_ALPHA
|
||||
}
|
||||
|
||||
world.physics.debugDraw()
|
||||
|
||||
for (lambda in onPostDrawWorld) {
|
||||
lambda.invoke()
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
matrixStack.clear(viewportMatrixScreen)
|
||||
|
||||
val thisTime = System.currentTimeMillis()
|
||||
|
||||
if (startupTextList.isNotEmpty() && thisTime <= finishStartupRendering) {
|
||||
var alpha = 1f
|
||||
|
||||
if (finishStartupRendering - thisTime < 1000L) {
|
||||
alpha = (finishStartupRendering - thisTime) / 1000f
|
||||
}
|
||||
|
||||
matrixStack.push()
|
||||
matrixStack.last().translateWithMultiplication(y = viewportHeight.toFloat())
|
||||
var shade = 255
|
||||
|
||||
for (i in startupTextList.size - 1 downTo 0) {
|
||||
val size = font.render(startupTextList[i], alignY = TextAlignY.BOTTOM, scale = 0.4f, color = RGBAColor(shade / 255f, shade / 255f, shade / 255f, alpha))
|
||||
matrixStack.last().translateWithMultiplication(y = -size.height * 1.2f)
|
||||
|
||||
if (shade > 120) {
|
||||
shade -= 10
|
||||
}
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
for (fn in onDrawGUI) {
|
||||
fn.invoke()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
GLFW.glfwSwapBuffers(window)
|
||||
GLFW.glfwPollEvents()
|
||||
input.think()
|
||||
|
||||
camera.think(Starbound.TICK_TIME_ADVANCE)
|
||||
|
||||
cleanup()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun onTermination(lambda: () -> Unit) {
|
||||
|
Loading…
Reference in New Issue
Block a user