world renderer: remove locking hell

This commit is contained in:
Bixilon 2022-12-23 14:26:16 +01:00
parent 5672bacb75
commit a4928205f0
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
6 changed files with 84 additions and 106 deletions

View File

@ -31,7 +31,7 @@ class LoadedMeshes(
fun cleanup(lock: Boolean) { fun cleanup(lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
val iterator = meshes.iterator() val iterator = meshes.iterator()
for ((chunkPosition, sections) in iterator) { for ((chunkPosition, sections) in iterator) {
@ -42,21 +42,22 @@ class LoadedMeshes(
renderer.unloadingQueue.forceQueue(sections.values) renderer.unloadingQueue.forceQueue(sections.values)
} }
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun clear(lock: Boolean) { fun clear(lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
for (sections in meshes.values) { for (sections in meshes.values) {
renderer.unloadingQueue.forceQueue(sections.values, lock) renderer.unloadingQueue.forceQueue(sections.values, lock)
} }
this.meshes.clear()
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun unload(position: ChunkPosition, lock: Boolean) { fun unload(position: ChunkPosition, lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
val meshes = this.meshes.remove(position) val meshes = this.meshes.remove(position)
@ -64,11 +65,11 @@ class LoadedMeshes(
renderer.unloadingQueue.forceQueue(meshes.values, lock) renderer.unloadingQueue.forceQueue(meshes.values, lock)
} }
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun unload(position: ChunkPosition, sectionHeight: Int, lock: Boolean) { fun unload(position: ChunkPosition, sectionHeight: Int, lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
val meshes = this.meshes[position] 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() { fun lock() {
renderer.lock.acquire()
this.lock.lock() this.lock.lock()
} }
fun unlock() { fun unlock() {
this.lock.unlock() this.lock.unlock()
renderer.lock.release()
} }
} }

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.world
import de.bixilon.kotlinglm.vec2.Vec2i import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
import de.bixilon.kutil.latch.CountUpAndDownLatch import de.bixilon.kutil.latch.CountUpAndDownLatch
import de.bixilon.kutil.observer.DataObserver.Companion.observe import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.minosoft.config.key.KeyActions 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 shader = renderSystem.createShader(minosoft("world")) { WorldShader(it, false) }
private val transparentShader = renderSystem.createShader(minosoft("world")) { WorldShader(it, true) } private val transparentShader = renderSystem.createShader(minosoft("world")) { WorldShader(it, true) }
private val textShader = renderSystem.createShader(minosoft("world/text")) { WorldTextShader(it) } private val textShader = renderSystem.createShader(minosoft("world/text")) { WorldTextShader(it) }
val lock = SimpleLock()
val world: World = connection.world val world: World = connection.world
val loaded = LoadedMeshes(this) val loaded = LoadedMeshes(this)
@ -111,13 +113,10 @@ class WorldRenderer(
} }
} }
renderWindow.inputHandler.registerKeyCallback( renderWindow.inputHandler.registerKeyCallback("minosoft:clear_chunk_cache".toResourceLocation(), KeyBinding(
"minosoft:clear_chunk_cache".toResourceLocation(), KeyActions.MODIFIER to setOf(KeyCodes.KEY_F3),
KeyBinding( KeyActions.PRESS to setOf(KeyCodes.KEY_A),
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F3), )) { clearChunkCache() }
KeyActions.PRESS to setOf(KeyCodes.KEY_A),
)
) { clearChunkCache() }
profile.rendering::antiMoirePattern.observe(this) { clearChunkCache() } profile.rendering::antiMoirePattern.observe(this) { clearChunkCache() }
val rendering = connection.profiles.rendering val rendering = connection.profiles.rendering
@ -127,26 +126,17 @@ class WorldRenderer(
val distance = maxOf(viewDistance, profile.simulationDistance) val distance = maxOf(viewDistance, profile.simulationDistance)
if (distance < this.previousViewDistance) { if (distance < this.previousViewDistance) {
// Unload all chunks(-sections) that are out of view distance // Unload all chunks(-sections) that are out of view distance
culledQueue.lock() lock.lock()
meshingQueue.lock()
loadingQueue.lock()
unloadingQueue.lock()
loaded.lock()
loaded.cleanup(false) loaded.cleanup(false)
culledQueue.cleanup(false) culledQueue.cleanup(false)
meshingQueue.cleanup() meshingQueue.cleanup(false)
meshingQueue.tasks.cleanup() meshingQueue.tasks.cleanup()
loadingQueue.cleanup(false) loadingQueue.cleanup(false)
lock.unlock()
loaded.unlock()
meshingQueue.unlock()
culledQueue.unlock()
loadingQueue.unlock()
unloadingQueue.unlock()
} else { } else {
master.tryQueue(world) master.tryQueue(world)
} }
@ -166,16 +156,10 @@ class WorldRenderer(
} }
fun unloadWorld() { fun unloadWorld() {
culledQueue.lock() lock.lock()
meshingQueue.lock()
loadingQueue.lock()
loaded.lock()
meshingQueue.tasks.interruptAll() meshingQueue.tasks.interruptAll()
unloadingQueue.lock()
loaded.clear(false) loaded.clear(false)
culledQueue.clear(false) culledQueue.clear(false)
@ -184,19 +168,11 @@ class WorldRenderer(
clearVisibleNextFrame = true clearVisibleNextFrame = true
loaded.unlock() lock.unlock()
meshingQueue.unlock()
culledQueue.unlock()
loadingQueue.unlock()
} }
fun unloadChunk(chunkPosition: Vec2i) { fun unloadChunk(chunkPosition: Vec2i) {
culledQueue.lock() lock.lock()
meshingQueue.lock()
loadingQueue.lock()
unloadingQueue.lock()
loaded.lock()
meshingQueue.tasks.interrupt(chunkPosition) meshingQueue.tasks.interrupt(chunkPosition)
@ -209,20 +185,13 @@ class WorldRenderer(
loaded.unload(chunkPosition, false) loaded.unload(chunkPosition, false)
loaded.unlock() lock.unlock()
culledQueue.unlock()
loadingQueue.unlock()
unloadingQueue.unlock()
meshingQueue.unlock()
} }
fun queueItemUnload(item: WorldQueueItem) { fun queueItemUnload(item: WorldQueueItem) {
culledQueue.lock() lock.lock()
meshingQueue.lock()
loadingQueue.lock()
unloadingQueue.lock()
loaded.lock()
loaded.unload(item.chunkPosition, item.sectionHeight, false) loaded.unload(item.chunkPosition, item.sectionHeight, false)
culledQueue.remove(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) meshingQueue.tasks.interrupt(item.chunkPosition, item.sectionHeight)
loaded.unlock() lock.unlock()
meshingQueue.unlock()
culledQueue.unlock()
loadingQueue.unlock()
unloadingQueue.unlock()
} }

View File

@ -31,7 +31,7 @@ class CulledQueue(
fun cleanup(lock: Boolean) { fun cleanup(lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
val iterator = queue.iterator() val iterator = queue.iterator()
for ((chunkPosition, _) in iterator) { for ((chunkPosition, _) in iterator) {
if (renderer.visibilityGraph.isChunkVisible(chunkPosition)) { if (renderer.visibilityGraph.isChunkVisible(chunkPosition)) {
@ -39,34 +39,36 @@ class CulledQueue(
} }
iterator.remove() iterator.remove()
} }
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun lock() { fun lock() {
renderer.lock.acquire()
this.lock.lock() this.lock.lock()
} }
fun unlock() { fun unlock() {
this.lock.unlock() this.lock.unlock()
renderer.lock.release()
} }
fun clear(lock: Boolean) { fun clear(lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
this.queue.clear() this.queue.clear()
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun remove(position: ChunkPosition, lock: Boolean) { fun remove(position: ChunkPosition, lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
queue -= position queue -= position
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun remove(position: ChunkPosition, height: SectionHeight, lock: Boolean) { fun remove(position: ChunkPosition, height: SectionHeight, lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
queue[position]?.let { queue[position]?.let {
if (it.remove(height) && it.isEmpty()) { if (it.remove(height) && it.isEmpty()) {
@ -74,7 +76,7 @@ class CulledQueue(
} }
} }
if (lock) this.lock.unlock() if (lock) unlock()
} }

View File

@ -34,9 +34,9 @@ class MeshLoadingQueue(
fun work() { fun work() {
lock.lock() lock()
if (meshes.isEmpty()) { if (meshes.isEmpty()) {
lock.unlock() unlock()
return return
} }
@ -73,7 +73,7 @@ class MeshLoadingQueue(
} }
renderer.loaded.unlock() renderer.loaded.unlock()
lock.unlock() unlock()
if (count > 0) { if (count > 0) {
renderer.visible.sort() renderer.visible.sort()
@ -82,7 +82,7 @@ class MeshLoadingQueue(
fun queue(mesh: WorldMesh) { fun queue(mesh: WorldMesh) {
lock.lock() lock()
if (!this.positions.add(QueuePosition(mesh))) { if (!this.positions.add(QueuePosition(mesh))) {
// already inside, remove // already inside, remove
meshes.remove(mesh) meshes.remove(mesh)
@ -93,11 +93,11 @@ class MeshLoadingQueue(
} else { } else {
meshes += mesh meshes += mesh
} }
lock.unlock() unlock()
} }
fun abort(position: ChunkPosition, lock: Boolean = true) { fun abort(position: ChunkPosition, lock: Boolean = true) {
if (lock) this.lock.lock() if (lock) lock()
val positions: MutableSet<QueuePosition> = mutableSetOf() val positions: MutableSet<QueuePosition> = mutableSetOf()
this.positions.removeAll { this.positions.removeAll {
if (it.position != position) { if (it.position != position) {
@ -107,14 +107,14 @@ class MeshLoadingQueue(
return@removeAll true return@removeAll true
} }
this.meshes.removeAll { QueuePosition(it.chunkPosition, it.sectionHeight) in positions } this.meshes.removeAll { QueuePosition(it.chunkPosition, it.sectionHeight) in positions }
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun cleanup(lock: Boolean) { fun cleanup(lock: Boolean) {
val remove: MutableSet<QueuePosition> = mutableSetOf() val remove: MutableSet<QueuePosition> = mutableSetOf()
if (lock) this.lock.lock() if (lock) lock()
this.positions.removeAll { this.positions.removeAll {
if (renderer.visibilityGraph.isChunkVisible(it.position)) { if (renderer.visibilityGraph.isChunkVisible(it.position)) {
return@removeAll false return@removeAll false
@ -124,22 +124,24 @@ class MeshLoadingQueue(
} }
this.meshes.removeAll { QueuePosition(it) in remove } this.meshes.removeAll { QueuePosition(it) in remove }
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun clear(lock: Boolean) { fun clear(lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
this.positions.clear() this.positions.clear()
this.meshes.clear() this.meshes.clear()
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun lock() { fun lock() {
renderer.lock.acquire()
this.lock.lock() this.lock.lock()
} }
fun unlock() { fun unlock() {
this.lock.unlock() this.lock.unlock()
renderer.lock.release()
} }
} }

View File

@ -45,16 +45,16 @@ class MeshUnloadingQueue(
} }
fun work() { fun work() {
lock.lock() lock()
try { try {
forceWork() forceWork()
} finally { } finally {
lock.unlock() unlock()
} }
} }
fun forceQueue(mesh: WorldMesh, lock: Boolean = true) { 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 if (mesh.chunkPosition == renderer.connection.player.positionInfo.chunkPosition) { // TODO: camera
this.meshes.add(0, mesh) this.meshes.add(0, mesh)
@ -63,42 +63,44 @@ class MeshUnloadingQueue(
} }
this.positions += QueuePosition(mesh) this.positions += QueuePosition(mesh)
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun queue(mesh: WorldMesh, lock: Boolean = true) { fun queue(mesh: WorldMesh, lock: Boolean = true) {
if (lock) this.lock.lock() if (lock) lock()
if (QueuePosition(mesh) in this.positions) { if (QueuePosition(mesh) in this.positions) {
// already queued // already queued
// TODO: maybe camera chunk position changed? // TODO: maybe camera chunk position changed?
this.lock.unlock() unlock()
return return
} }
forceQueue(mesh, false) forceQueue(mesh, false)
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun forceQueue(meshes: Collection<WorldMesh>, lock: Boolean = true) { fun forceQueue(meshes: Collection<WorldMesh>, lock: Boolean = true) {
if (lock) this.lock.lock() if (lock) lock()
for (mesh in meshes) { for (mesh in meshes) {
forceQueue(mesh, false) forceQueue(mesh, false)
} }
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun queue(meshes: Collection<WorldMesh>) { fun queue(meshes: Collection<WorldMesh>) {
this.lock.lock() lock()
for (mesh in meshes) { for (mesh in meshes) {
queue(mesh, false) queue(mesh, false)
} }
this.lock.unlock() unlock()
} }
fun lock() { fun lock() {
lock.lock() renderer.lock.acquire()
this.lock.lock()
} }
fun unlock() { fun unlock() {
lock.unlock() this.lock.unlock()
renderer.lock.release()
} }
} }

View File

@ -33,17 +33,17 @@ class ChunkMeshingQueue(
@Volatile @Volatile
private var working = false private var working = false
private val queue: MutableList<WorldQueueItem> = mutableListOf() // queue, that is visible, and should be rendered private val queue: MutableList<WorldQueueItem> = mutableListOf()
private val set: MutableSet<WorldQueueItem> = HashSet() // queue, that is visible, and should be rendered private val set: MutableSet<WorldQueueItem> = HashSet()
val lock = SimpleLock() private val lock = SimpleLock()
val size: Int get() = queue.size val size: Int get() = queue.size
fun sort() { fun sort() {
lock.lock() lock()
val position = renderer.cameraChunkPosition val position = renderer.cameraChunkPosition
val height = renderer.cameraSectionHeight val height = renderer.cameraSectionHeight
val cameraSectionPosition = Vec3i(position.x, height, position.y) val cameraSectionPosition = Vec3i(position.x, height, position.y)
@ -54,7 +54,7 @@ class ChunkMeshingQueue(
} }
(it.sectionPosition - cameraSectionPosition).length2() (it.sectionPosition - cameraSectionPosition).length2()
} }
lock.unlock() unlock()
} }
@ -68,7 +68,7 @@ class ChunkMeshingQueue(
val items: MutableList<WorldQueueItem> = mutableListOf() val items: MutableList<WorldQueueItem> = mutableListOf()
lock.lock() lock()
for (i in 0 until tasks.max - size) { for (i in 0 until tasks.max - size) {
if (queue.isEmpty()) { if (queue.isEmpty()) {
break break
@ -77,9 +77,9 @@ class ChunkMeshingQueue(
set -= item set -= item
items += item items += item
} }
lock.unlock() unlock()
for (item in items) { 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) val task = MeshPrepareTask(item.chunkPosition, item.sectionHeight, runnable)
task.runnable.runnable = Runnable { renderer.mesher.tryMesh(item, task, task.runnable) } task.runnable.runnable = Runnable { renderer.mesher.tryMesh(item, task, task.runnable) }
tasks += task tasks += task
@ -100,7 +100,8 @@ class ChunkMeshingQueue(
set -= remove set -= remove
} }
fun cleanup() { fun cleanup(lock: Boolean) {
if (lock) lock()
val remove: MutableSet<WorldQueueItem> = mutableSetOf() val remove: MutableSet<WorldQueueItem> = mutableSetOf()
queue.removeAll { queue.removeAll {
if (renderer.visibilityGraph.isChunkVisible(it.chunkPosition)) { if (renderer.visibilityGraph.isChunkVisible(it.chunkPosition)) {
@ -110,36 +111,39 @@ class ChunkMeshingQueue(
return@removeAll true return@removeAll true
} }
set -= remove set -= remove
if (lock) unlock()
} }
fun lock() { fun lock() {
renderer.lock.acquire()
this.lock.lock() this.lock.lock()
} }
fun unlock() { fun unlock() {
this.lock.unlock() this.lock.unlock()
renderer.lock.release()
} }
fun clear(lock: Boolean) { fun clear(lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
this.queue.clear() this.queue.clear()
this.set.clear() this.set.clear()
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun remove(item: WorldQueueItem, lock: Boolean) { fun remove(item: WorldQueueItem, lock: Boolean) {
if (lock) this.lock.lock() if (lock) lock()
if (this.set.remove(item)) { if (this.set.remove(item)) {
this.queue -= item this.queue -= item
} }
if (lock) this.lock.unlock() if (lock) unlock()
} }
fun queue(item: WorldQueueItem) { fun queue(item: WorldQueueItem) {
lock.lock() lock()
if (set.remove(item)) { if (set.remove(item)) {
queue -= item queue -= item
} }
@ -149,6 +153,6 @@ class ChunkMeshingQueue(
queue += item queue += item
} }
set += item set += item
lock.unlock() unlock()
} }
} }