From a4928205f0f7be1c6b09813e43e2e35bcaa49538 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Fri, 23 Dec 2022 14:26:16 +0100 Subject: [PATCH] world renderer: remove locking hell --- .../gui/rendering/world/LoadedMeshes.kt | 19 +++--- .../gui/rendering/world/WorldRenderer.kt | 67 +++++-------------- .../gui/rendering/world/queue/CulledQueue.kt | 18 ++--- .../world/queue/loading/MeshLoadingQueue.kt | 24 ++++--- .../world/queue/loading/MeshUnloadingQueue.kt | 28 ++++---- .../world/queue/meshing/ChunkMeshingQueue.kt | 34 +++++----- 6 files changed, 84 insertions(+), 106 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/LoadedMeshes.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/LoadedMeshes.kt index 128465e9a..f74d77cd9 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/LoadedMeshes.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/LoadedMeshes.kt @@ -31,7 +31,7 @@ class LoadedMeshes( fun cleanup(lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() val iterator = meshes.iterator() for ((chunkPosition, sections) in iterator) { @@ -42,21 +42,22 @@ class LoadedMeshes( renderer.unloadingQueue.forceQueue(sections.values) } - if (lock) this.lock.unlock() + if (lock) unlock() } fun clear(lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() for (sections in meshes.values) { renderer.unloadingQueue.forceQueue(sections.values, lock) } + this.meshes.clear() - if (lock) this.lock.unlock() + if (lock) unlock() } fun unload(position: ChunkPosition, lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() val meshes = this.meshes.remove(position) @@ -64,11 +65,11 @@ class LoadedMeshes( renderer.unloadingQueue.forceQueue(meshes.values, lock) } - if (lock) this.lock.unlock() + if (lock) unlock() } fun unload(position: ChunkPosition, sectionHeight: Int, lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() val meshes = this.meshes[position] @@ -82,7 +83,7 @@ class LoadedMeshes( } } - if (lock) this.lock.unlock() + if (lock) unlock() } @@ -113,10 +114,12 @@ class LoadedMeshes( } fun lock() { + renderer.lock.acquire() this.lock.lock() } fun unlock() { this.lock.unlock() + renderer.lock.release() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/WorldRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/WorldRenderer.kt index 177fb9008..6306e123c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/WorldRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/WorldRenderer.kt @@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.world import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec3.Vec3 +import de.bixilon.kutil.concurrent.lock.simple.SimpleLock import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.kutil.observer.DataObserver.Companion.observe import de.bixilon.minosoft.config.key.KeyActions @@ -60,6 +61,7 @@ class WorldRenderer( private val shader = renderSystem.createShader(minosoft("world")) { WorldShader(it, false) } private val transparentShader = renderSystem.createShader(minosoft("world")) { WorldShader(it, true) } private val textShader = renderSystem.createShader(minosoft("world/text")) { WorldTextShader(it) } + val lock = SimpleLock() val world: World = connection.world val loaded = LoadedMeshes(this) @@ -111,13 +113,10 @@ class WorldRenderer( } } - renderWindow.inputHandler.registerKeyCallback( - "minosoft:clear_chunk_cache".toResourceLocation(), - KeyBinding( - KeyActions.MODIFIER to setOf(KeyCodes.KEY_F3), - KeyActions.PRESS to setOf(KeyCodes.KEY_A), - ) - ) { clearChunkCache() } + renderWindow.inputHandler.registerKeyCallback("minosoft:clear_chunk_cache".toResourceLocation(), KeyBinding( + KeyActions.MODIFIER to setOf(KeyCodes.KEY_F3), + KeyActions.PRESS to setOf(KeyCodes.KEY_A), + )) { clearChunkCache() } profile.rendering::antiMoirePattern.observe(this) { clearChunkCache() } val rendering = connection.profiles.rendering @@ -127,26 +126,17 @@ class WorldRenderer( val distance = maxOf(viewDistance, profile.simulationDistance) if (distance < this.previousViewDistance) { // Unload all chunks(-sections) that are out of view distance - culledQueue.lock() - meshingQueue.lock() - loadingQueue.lock() - unloadingQueue.lock() - loaded.lock() + lock.lock() loaded.cleanup(false) culledQueue.cleanup(false) - meshingQueue.cleanup() + meshingQueue.cleanup(false) meshingQueue.tasks.cleanup() loadingQueue.cleanup(false) - - loaded.unlock() - meshingQueue.unlock() - culledQueue.unlock() - loadingQueue.unlock() - unloadingQueue.unlock() + lock.unlock() } else { master.tryQueue(world) } @@ -166,16 +156,10 @@ class WorldRenderer( } fun unloadWorld() { - culledQueue.lock() - meshingQueue.lock() - loadingQueue.lock() - loaded.lock() - - + lock.lock() meshingQueue.tasks.interruptAll() - unloadingQueue.lock() loaded.clear(false) culledQueue.clear(false) @@ -184,19 +168,11 @@ class WorldRenderer( clearVisibleNextFrame = true - loaded.unlock() - meshingQueue.unlock() - culledQueue.unlock() - loadingQueue.unlock() + lock.unlock() } fun unloadChunk(chunkPosition: Vec2i) { - culledQueue.lock() - meshingQueue.lock() - loadingQueue.lock() - unloadingQueue.lock() - loaded.lock() - + lock.lock() meshingQueue.tasks.interrupt(chunkPosition) @@ -209,20 +185,13 @@ class WorldRenderer( loaded.unload(chunkPosition, false) - loaded.unlock() - culledQueue.unlock() - loadingQueue.unlock() - unloadingQueue.unlock() - meshingQueue.unlock() + lock.unlock() } fun queueItemUnload(item: WorldQueueItem) { - culledQueue.lock() - meshingQueue.lock() - loadingQueue.lock() - unloadingQueue.lock() - loaded.lock() + lock.lock() + loaded.unload(item.chunkPosition, item.sectionHeight, false) culledQueue.remove(item.chunkPosition, item.sectionHeight, false) @@ -233,11 +202,7 @@ class WorldRenderer( meshingQueue.tasks.interrupt(item.chunkPosition, item.sectionHeight) - loaded.unlock() - meshingQueue.unlock() - culledQueue.unlock() - loadingQueue.unlock() - unloadingQueue.unlock() + lock.unlock() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/CulledQueue.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/CulledQueue.kt index 75f234dd4..d7aee9c96 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/CulledQueue.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/CulledQueue.kt @@ -31,7 +31,7 @@ class CulledQueue( fun cleanup(lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() val iterator = queue.iterator() for ((chunkPosition, _) in iterator) { if (renderer.visibilityGraph.isChunkVisible(chunkPosition)) { @@ -39,34 +39,36 @@ class CulledQueue( } iterator.remove() } - if (lock) this.lock.unlock() + if (lock) unlock() } fun lock() { + renderer.lock.acquire() this.lock.lock() } fun unlock() { this.lock.unlock() + renderer.lock.release() } fun clear(lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() this.queue.clear() - if (lock) this.lock.unlock() + if (lock) unlock() } fun remove(position: ChunkPosition, lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() queue -= position - if (lock) this.lock.unlock() + if (lock) unlock() } fun remove(position: ChunkPosition, height: SectionHeight, lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() queue[position]?.let { if (it.remove(height) && it.isEmpty()) { @@ -74,7 +76,7 @@ class CulledQueue( } } - if (lock) this.lock.unlock() + if (lock) unlock() } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshLoadingQueue.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshLoadingQueue.kt index 398c03e22..d9f3dc907 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshLoadingQueue.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshLoadingQueue.kt @@ -34,9 +34,9 @@ class MeshLoadingQueue( fun work() { - lock.lock() + lock() if (meshes.isEmpty()) { - lock.unlock() + unlock() return } @@ -73,7 +73,7 @@ class MeshLoadingQueue( } renderer.loaded.unlock() - lock.unlock() + unlock() if (count > 0) { renderer.visible.sort() @@ -82,7 +82,7 @@ class MeshLoadingQueue( fun queue(mesh: WorldMesh) { - lock.lock() + lock() if (!this.positions.add(QueuePosition(mesh))) { // already inside, remove meshes.remove(mesh) @@ -93,11 +93,11 @@ class MeshLoadingQueue( } else { meshes += mesh } - lock.unlock() + unlock() } fun abort(position: ChunkPosition, lock: Boolean = true) { - if (lock) this.lock.lock() + if (lock) lock() val positions: MutableSet = mutableSetOf() this.positions.removeAll { if (it.position != position) { @@ -107,14 +107,14 @@ class MeshLoadingQueue( return@removeAll true } this.meshes.removeAll { QueuePosition(it.chunkPosition, it.sectionHeight) in positions } - if (lock) this.lock.unlock() + if (lock) unlock() } fun cleanup(lock: Boolean) { val remove: MutableSet = mutableSetOf() - if (lock) this.lock.lock() + if (lock) lock() this.positions.removeAll { if (renderer.visibilityGraph.isChunkVisible(it.position)) { return@removeAll false @@ -124,22 +124,24 @@ class MeshLoadingQueue( } this.meshes.removeAll { QueuePosition(it) in remove } - if (lock) this.lock.unlock() + if (lock) unlock() } fun clear(lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() this.positions.clear() this.meshes.clear() - if (lock) this.lock.unlock() + if (lock) unlock() } fun lock() { + renderer.lock.acquire() this.lock.lock() } fun unlock() { this.lock.unlock() + renderer.lock.release() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshUnloadingQueue.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshUnloadingQueue.kt index 1e2afa980..e508cac15 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshUnloadingQueue.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/loading/MeshUnloadingQueue.kt @@ -45,16 +45,16 @@ class MeshUnloadingQueue( } fun work() { - lock.lock() + lock() try { forceWork() } finally { - lock.unlock() + unlock() } } fun forceQueue(mesh: WorldMesh, lock: Boolean = true) { - if (lock) this.lock.lock() + if (lock) lock() if (mesh.chunkPosition == renderer.connection.player.positionInfo.chunkPosition) { // TODO: camera this.meshes.add(0, mesh) @@ -63,42 +63,44 @@ class MeshUnloadingQueue( } this.positions += QueuePosition(mesh) - if (lock) this.lock.unlock() + if (lock) unlock() } fun queue(mesh: WorldMesh, lock: Boolean = true) { - if (lock) this.lock.lock() + if (lock) lock() if (QueuePosition(mesh) in this.positions) { // already queued // TODO: maybe camera chunk position changed? - this.lock.unlock() + unlock() return } forceQueue(mesh, false) - if (lock) this.lock.unlock() + if (lock) unlock() } fun forceQueue(meshes: Collection, lock: Boolean = true) { - if (lock) this.lock.lock() + if (lock) lock() for (mesh in meshes) { forceQueue(mesh, false) } - if (lock) this.lock.unlock() + if (lock) unlock() } fun queue(meshes: Collection) { - this.lock.lock() + lock() for (mesh in meshes) { queue(mesh, false) } - this.lock.unlock() + unlock() } fun lock() { - lock.lock() + renderer.lock.acquire() + this.lock.lock() } fun unlock() { - lock.unlock() + this.lock.unlock() + renderer.lock.release() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/meshing/ChunkMeshingQueue.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/meshing/ChunkMeshingQueue.kt index 6ea2d7ea9..2b5c24fdd 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/meshing/ChunkMeshingQueue.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/queue/meshing/ChunkMeshingQueue.kt @@ -33,17 +33,17 @@ class ChunkMeshingQueue( @Volatile private var working = false - private val queue: MutableList = mutableListOf() // queue, that is visible, and should be rendered - private val set: MutableSet = HashSet() // queue, that is visible, and should be rendered + private val queue: MutableList = mutableListOf() + private val set: MutableSet = HashSet() - val lock = SimpleLock() + private val lock = SimpleLock() val size: Int get() = queue.size fun sort() { - lock.lock() + lock() val position = renderer.cameraChunkPosition val height = renderer.cameraSectionHeight val cameraSectionPosition = Vec3i(position.x, height, position.y) @@ -54,7 +54,7 @@ class ChunkMeshingQueue( } (it.sectionPosition - cameraSectionPosition).length2() } - lock.unlock() + unlock() } @@ -68,7 +68,7 @@ class ChunkMeshingQueue( val items: MutableList = mutableListOf() - lock.lock() + lock() for (i in 0 until tasks.max - size) { if (queue.isEmpty()) { break @@ -77,9 +77,9 @@ class ChunkMeshingQueue( set -= item items += item } - lock.unlock() + unlock() for (item in items) { - val runnable = ThreadPoolRunnable(if (item.chunkPosition == renderer.cameraChunkPosition) ThreadPool.HIGH else ThreadPool.LOW, interruptable = true) // Our own chunk is the most important one ToDo: Also make neighbour chunks important + val runnable = ThreadPoolRunnable(if (item.chunkPosition == renderer.cameraChunkPosition) ThreadPool.HIGH else ThreadPool.LOW, interruptable = true) // ToDo: Also make neighbour chunks important val task = MeshPrepareTask(item.chunkPosition, item.sectionHeight, runnable) task.runnable.runnable = Runnable { renderer.mesher.tryMesh(item, task, task.runnable) } tasks += task @@ -100,7 +100,8 @@ class ChunkMeshingQueue( set -= remove } - fun cleanup() { + fun cleanup(lock: Boolean) { + if (lock) lock() val remove: MutableSet = mutableSetOf() queue.removeAll { if (renderer.visibilityGraph.isChunkVisible(it.chunkPosition)) { @@ -110,36 +111,39 @@ class ChunkMeshingQueue( return@removeAll true } set -= remove + if (lock) unlock() } fun lock() { + renderer.lock.acquire() this.lock.lock() } fun unlock() { this.lock.unlock() + renderer.lock.release() } fun clear(lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() this.queue.clear() this.set.clear() - if (lock) this.lock.unlock() + if (lock) unlock() } fun remove(item: WorldQueueItem, lock: Boolean) { - if (lock) this.lock.lock() + if (lock) lock() if (this.set.remove(item)) { this.queue -= item } - if (lock) this.lock.unlock() + if (lock) unlock() } fun queue(item: WorldQueueItem) { - lock.lock() + lock() if (set.remove(item)) { queue -= item } @@ -149,6 +153,6 @@ class ChunkMeshingQueue( queue += item } set += item - lock.unlock() + unlock() } }