mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-16 02:45:13 -04:00
limit time for gpu chunk transfers
"Smooth FPS" Adds an option to the config, and limits to time the chunk renderer has to transfer to about 3ms. This works good and feels a lot smoother now.
This commit is contained in:
parent
f20f2b5e0b
commit
055b015390
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Minosoft
|
* Minosoft
|
||||||
* Copyright (C) 2020-2023 Moritz Zwerger
|
* Copyright (C) 2020-2025 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 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.
|
||||||
*
|
*
|
||||||
@ -36,4 +36,11 @@ class PerformanceC(profile: RenderingProfile) {
|
|||||||
* Sleeps 100 ms if the rendering window is not in focus anymore
|
* Sleeps 100 ms if the rendering window is not in focus anymore
|
||||||
*/
|
*/
|
||||||
var slowRendering by BooleanDelegate(profile, true)
|
var slowRendering by BooleanDelegate(profile, true)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limits the time of chunk transfers to the gpu.
|
||||||
|
* Increases time until far chunks are loaded.
|
||||||
|
* If disabled, lag spikes can occur when crossing chunk borders
|
||||||
|
*/
|
||||||
|
var limitChunkTransferTime by BooleanDelegate(profile, true)
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,8 @@ class ChunkRenderer(
|
|||||||
var cameraChunkPosition = Vec2i.EMPTY
|
var cameraChunkPosition = Vec2i.EMPTY
|
||||||
var cameraSectionHeight = 0
|
var cameraSectionHeight = 0
|
||||||
|
|
||||||
|
var limitChunkTransferTime = true
|
||||||
|
|
||||||
override fun registerLayers() {
|
override fun registerLayers() {
|
||||||
layers.register(OpaqueLayer, shader, this::drawBlocksOpaque) { visible.opaque.isEmpty() }
|
layers.register(OpaqueLayer, shader, this::drawBlocksOpaque) { visible.opaque.isEmpty() }
|
||||||
layers.register(TranslucentLayer, shader, this::drawBlocksTranslucent) { visible.translucent.isEmpty() }
|
layers.register(TranslucentLayer, shader, this::drawBlocksTranslucent) { visible.translucent.isEmpty() }
|
||||||
@ -128,6 +130,7 @@ class ChunkRenderer(
|
|||||||
val rendering = session.profiles.rendering
|
val rendering = session.profiles.rendering
|
||||||
rendering.performance::fastBedrock.observe(this) { clearChunkCache() }
|
rendering.performance::fastBedrock.observe(this) { clearChunkCache() }
|
||||||
rendering.light::ambientOcclusion.observe(this) { clearChunkCache() }
|
rendering.light::ambientOcclusion.observe(this) { clearChunkCache() }
|
||||||
|
rendering.performance::limitChunkTransferTime.observe(this) { this.limitChunkTransferTime = it }
|
||||||
|
|
||||||
profile::viewDistance.observe(this) { viewDistance ->
|
profile::viewDistance.observe(this) { viewDistance ->
|
||||||
val distance = maxOf(viewDistance, profile.simulationDistance)
|
val distance = maxOf(viewDistance, profile.simulationDistance)
|
||||||
|
@ -15,13 +15,13 @@ package de.bixilon.minosoft.gui.rendering.chunk.queue.loading
|
|||||||
|
|
||||||
import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
||||||
import de.bixilon.kutil.concurrent.lock.Lock
|
import de.bixilon.kutil.concurrent.lock.Lock
|
||||||
import de.bixilon.kutil.time.TimeUtil
|
|
||||||
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
|
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
|
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.queue.QueuePosition
|
import de.bixilon.minosoft.gui.rendering.chunk.queue.QueuePosition
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.util.ChunkRendererUtil.maxBusyTime
|
import de.bixilon.minosoft.gui.rendering.chunk.util.ChunkRendererUtil.maxBusyTime
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
|
import kotlin.time.TimeSource
|
||||||
|
|
||||||
class MeshLoadingQueue(
|
class MeshLoadingQueue(
|
||||||
private val renderer: ChunkRenderer,
|
private val renderer: ChunkRenderer,
|
||||||
@ -41,14 +41,18 @@ class MeshLoadingQueue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var count = 0
|
var count = 0
|
||||||
val start = TimeUtil.millis()
|
val start = TimeSource.Monotonic.markNow()
|
||||||
val maxTime = renderer.maxBusyTime // If the player is still, then we can load more chunks (to not cause lags)
|
val maxTime = renderer.maxBusyTime
|
||||||
|
|
||||||
var meshes: Int2ObjectOpenHashMap<ChunkMeshes> = unsafeNull()
|
var meshes: Int2ObjectOpenHashMap<ChunkMeshes> = unsafeNull()
|
||||||
var position: ChunkPosition? = null
|
var position: ChunkPosition? = null
|
||||||
|
|
||||||
renderer.loaded.lock()
|
renderer.loaded.lock()
|
||||||
while (this.meshes.isNotEmpty() && (TimeUtil.millis() - start < maxTime)) {
|
var index = 0
|
||||||
|
while (true) {
|
||||||
|
if (this.meshes.isEmpty()) break
|
||||||
|
if (index++ % BATCH_SIZE == 0 && TimeSource.Monotonic.markNow() - start >= maxTime) break
|
||||||
|
|
||||||
val mesh = this.meshes.removeAt(0)
|
val mesh = this.meshes.removeAt(0)
|
||||||
this.positions -= QueuePosition(mesh)
|
this.positions -= QueuePosition(mesh)
|
||||||
|
|
||||||
@ -152,4 +156,8 @@ class MeshLoadingQueue(
|
|||||||
this.lock.unlock()
|
this.lock.unlock()
|
||||||
renderer.lock.release()
|
renderer.lock.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val BATCH_SIZE = 5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,11 @@
|
|||||||
package de.bixilon.minosoft.gui.rendering.chunk.queue.loading
|
package de.bixilon.minosoft.gui.rendering.chunk.queue.loading
|
||||||
|
|
||||||
import de.bixilon.kutil.concurrent.lock.Lock
|
import de.bixilon.kutil.concurrent.lock.Lock
|
||||||
import de.bixilon.kutil.time.TimeUtil
|
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
|
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
|
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.queue.QueuePosition
|
import de.bixilon.minosoft.gui.rendering.chunk.queue.QueuePosition
|
||||||
import de.bixilon.minosoft.gui.rendering.chunk.util.ChunkRendererUtil.maxBusyTime
|
import de.bixilon.minosoft.gui.rendering.chunk.util.ChunkRendererUtil.maxBusyTime
|
||||||
|
import kotlin.time.TimeSource
|
||||||
|
|
||||||
class MeshUnloadingQueue(
|
class MeshUnloadingQueue(
|
||||||
private val renderer: ChunkRenderer,
|
private val renderer: ChunkRenderer,
|
||||||
@ -33,10 +33,14 @@ class MeshUnloadingQueue(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val time = TimeUtil.millis()
|
val start = TimeSource.Monotonic.markNow()
|
||||||
val maxTime = renderer.maxBusyTime
|
val maxTime = renderer.maxBusyTime
|
||||||
|
|
||||||
while (meshes.isNotEmpty() && (TimeUtil.millis() - time < maxTime)) {
|
var index = 0
|
||||||
|
while (true) {
|
||||||
|
if (meshes.isEmpty()) break
|
||||||
|
if (index++ % MeshLoadingQueue.BATCH_SIZE == 0 && TimeSource.Monotonic.markNow() - start >= maxTime) break
|
||||||
|
|
||||||
val mesh = meshes.removeAt(0)
|
val mesh = meshes.removeAt(0)
|
||||||
this.positions -= QueuePosition(mesh)
|
this.positions -= QueuePosition(mesh)
|
||||||
renderer.visible.removeMesh(mesh)
|
renderer.visible.removeMesh(mesh)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Minosoft
|
* Minosoft
|
||||||
* Copyright (C) 2020-2024 Moritz Zwerger
|
* Copyright (C) 2020-2025 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 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.
|
||||||
*
|
*
|
||||||
@ -17,14 +17,23 @@ import de.bixilon.minosoft.data.world.chunk.ChunkSection
|
|||||||
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
|
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
|
||||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.isEmpty
|
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.isEmpty
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
object ChunkRendererUtil {
|
object ChunkRendererUtil {
|
||||||
const val STILL_LOADING_TIME = 50L
|
val STILL_LOADING_TIME = 30.milliseconds
|
||||||
const val MOVING_LOADING_TIME = 20L
|
val MOVING_LOADING_TIME = 3.milliseconds
|
||||||
|
|
||||||
|
|
||||||
// If the player is still, then we can load more chunks (to not cause lags)
|
// If the player is still, then we can load more chunks (to not cause lags)
|
||||||
val ChunkRenderer.maxBusyTime: Long get() = if (session.player.physics.velocity.isEmpty()) STILL_LOADING_TIME else MOVING_LOADING_TIME // TODO: get of camera
|
val ChunkRenderer.maxBusyTime: Duration
|
||||||
|
get() {
|
||||||
|
if (!limitChunkTransferTime) return Duration.INFINITE
|
||||||
|
if (session.camera.entity.physics.velocity.isEmpty()) {
|
||||||
|
return STILL_LOADING_TIME
|
||||||
|
}
|
||||||
|
return MOVING_LOADING_TIME
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val ChunkSection.smallMesh: Boolean get() = blocks.count < ProtocolDefinition.SECTION_MAX_X * ProtocolDefinition.SECTION_MAX_Z
|
val ChunkSection.smallMesh: Boolean get() = blocks.count < ProtocolDefinition.SECTION_MAX_X * ProtocolDefinition.SECTION_MAX_Z
|
||||||
|
Loading…
x
Reference in New Issue
Block a user