mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 11:24:56 -04:00
world renderer: rework preparing tasks
This commit is contained in:
parent
52cb9b4a57
commit
592aef6bda
@ -92,7 +92,7 @@ class DebugHUDElement(guiRenderer: GUIRenderer) : Element(guiRenderer), Layouted
|
||||
layout += TextElement(guiRenderer, TextComponent(RunConfiguration.APPLICATION_NAME, ChatColors.RED))
|
||||
layout += AutoTextElement(guiRenderer, 1) { "FPS §d${renderWindow.renderStats.smoothAvgFPS.rounded10}" }
|
||||
renderWindow.renderer[WorldRenderer]?.apply {
|
||||
layout += AutoTextElement(guiRenderer, 1) { "C v=$visibleSize, m=${loadedMeshesSize.format()}, cQ=${culledQueuedSize.format()}, q=${queueSize.format()}, pT=${preparingTasksSize.format()}/${maxPreparingTasks.format()}, l=${meshesToLoadSize.format()}/${maxMeshesToLoad.format()}, w=${connection.world.chunks.size.format()}" }
|
||||
layout += AutoTextElement(guiRenderer, 1) { "C v=${visible.sizeString}, m=${loaded.size.format()}, cQ=${culledQueuedSize.format()}, q=${queue.size.format()}, pT=${queue.tasks.size.format()}/${queue.tasks.max.format()}, l=${loadingQueue.size.format()}/${queue.maxMeshesToLoad.format()}, w=${connection.world.chunks.size.format()}" }
|
||||
}
|
||||
|
||||
layout += renderWindow.renderer[EntityRenderer]?.let {
|
||||
|
@ -54,9 +54,10 @@ import de.bixilon.minosoft.gui.rendering.world.preparer.FluidSectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.world.preparer.SolidSectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.world.preparer.cull.FluidCullSectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.world.preparer.cull.SolidCullSectionPreparer
|
||||
import de.bixilon.minosoft.gui.rendering.world.queue.ChunkMeshingQueue
|
||||
import de.bixilon.minosoft.gui.rendering.world.queue.MeshLoadingQueue
|
||||
import de.bixilon.minosoft.gui.rendering.world.queue.MeshUnloadingQueue
|
||||
import de.bixilon.minosoft.gui.rendering.world.queue.meshing.ChunkMeshingQueue
|
||||
import de.bixilon.minosoft.gui.rendering.world.queue.meshing.tasks.MeshPrepareTask
|
||||
import de.bixilon.minosoft.gui.rendering.world.shader.WorldShader
|
||||
import de.bixilon.minosoft.gui.rendering.world.shader.WorldTextShader
|
||||
import de.bixilon.minosoft.modding.event.events.RespawnEvent
|
||||
@ -108,12 +109,12 @@ class WorldRenderer(
|
||||
var cameraChunkPosition = Vec2i.EMPTY
|
||||
var cameraSectionHeight = 0
|
||||
|
||||
val visibleSize: String get() = visible.sizeString
|
||||
val loadedMeshesSize: Int get() = loaded.size
|
||||
val culledQueuedSize: Int get() = culledQueue.size
|
||||
val meshesToLoadSize: Int get() = loadingQueue.size
|
||||
val queueSize: Int get() = queue.size
|
||||
val preparingTasksSize: Int get() = queue.preparingTasks.size
|
||||
@Deprecated("alias?", ReplaceWith("visible.sizeString")) val visibleSize: String get() = visible.sizeString
|
||||
@Deprecated("alias?", ReplaceWith("loaded.size")) val loadedMeshesSize: Int get() = loaded.size
|
||||
@Deprecated("alias?") val culledQueuedSize: Int get() = culledQueue.size
|
||||
@Deprecated("alias?", ReplaceWith("loadingQueue.size")) val meshesToLoadSize: Int get() = loadingQueue.size
|
||||
@Deprecated("alias?", ReplaceWith("queue.size")) val queueSize: Int get() = queue.size
|
||||
@Deprecated("alias?", ReplaceWith("queue.tasks.size")) val preparingTasksSize: Int get() = queue.tasks.size
|
||||
|
||||
override fun init(latch: CountUpAndDownLatch) {
|
||||
renderWindow.modelLoader.load(latch)
|
||||
@ -262,8 +263,8 @@ class WorldRenderer(
|
||||
|
||||
queue.cleanup()
|
||||
|
||||
queue.tasks.cleanup()
|
||||
loadingQueue.cleanup(false)
|
||||
queue.interruptCleanup()
|
||||
|
||||
|
||||
loaded.unlock()
|
||||
@ -305,7 +306,7 @@ class WorldRenderer(
|
||||
|
||||
|
||||
|
||||
queue.interrupt()
|
||||
queue.tasks.interruptAll()
|
||||
|
||||
unloadingQueue.lock()
|
||||
loaded.clear(false)
|
||||
@ -330,7 +331,7 @@ class WorldRenderer(
|
||||
loaded.lock()
|
||||
|
||||
|
||||
queue.interrupt(chunkPosition)
|
||||
queue.tasks.interrupt(chunkPosition)
|
||||
|
||||
culledQueue.remove(chunkPosition)
|
||||
|
||||
@ -348,7 +349,7 @@ class WorldRenderer(
|
||||
queue.unlock()
|
||||
}
|
||||
|
||||
fun prepareItem(item: WorldQueueItem, task: SectionPrepareTask, runnable: ThreadPoolRunnable) {
|
||||
fun prepareItem(item: WorldQueueItem, task: MeshPrepareTask, runnable: ThreadPoolRunnable) {
|
||||
try {
|
||||
val chunk = item.chunk ?: world[item.chunkPosition] ?: return
|
||||
val section = chunk[item.sectionHeight] ?: return
|
||||
@ -378,7 +379,7 @@ class WorldRenderer(
|
||||
} finally {
|
||||
task.runnable.interruptable = false
|
||||
if (Thread.interrupted()) throw InterruptedException()
|
||||
queue -= task
|
||||
queue.tasks -= task
|
||||
queue.work()
|
||||
}
|
||||
}
|
||||
@ -398,11 +399,11 @@ class WorldRenderer(
|
||||
}
|
||||
}
|
||||
|
||||
queue.remove(item.chunkPosition, item.sectionHeight)
|
||||
queue.remove(item)
|
||||
|
||||
loadingQueue.abort(item.chunkPosition, false)
|
||||
|
||||
queue.interrupt(item.chunkPosition, item.sectionHeight)
|
||||
queue.tasks.interrupt(item.chunkPosition, item.sectionHeight)
|
||||
|
||||
loaded.unlock()
|
||||
queue.unlock()
|
||||
|
@ -25,7 +25,6 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
class MeshLoadingQueue(
|
||||
private val renderer: WorldRenderer,
|
||||
) {
|
||||
|
||||
private val meshes: MutableList<WorldMesh> = mutableListOf() // prepared meshes, that can be loaded in the (next) frame
|
||||
private val positions: MutableSet<QueuePosition> = HashSet()
|
||||
private val lock = SimpleLock()
|
||||
|
@ -11,25 +11,24 @@
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.world.queue
|
||||
package de.bixilon.minosoft.gui.rendering.world.queue.meshing
|
||||
|
||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.kutil.concurrent.pool.ThreadPool
|
||||
import de.bixilon.kutil.concurrent.pool.ThreadPoolRunnable
|
||||
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
||||
import de.bixilon.minosoft.data.world.positions.SectionHeight
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.length2
|
||||
import de.bixilon.minosoft.gui.rendering.world.SectionPrepareTask
|
||||
import de.bixilon.minosoft.gui.rendering.world.WorldQueueItem
|
||||
import de.bixilon.minosoft.gui.rendering.world.WorldRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.world.queue.meshing.tasks.MeshPrepareTask
|
||||
import de.bixilon.minosoft.gui.rendering.world.queue.meshing.tasks.MeshPrepareTaskManager
|
||||
import de.bixilon.minosoft.util.SystemInformation
|
||||
|
||||
class ChunkMeshingQueue(
|
||||
private val renderer: WorldRenderer,
|
||||
) {
|
||||
val maxPreparingTasks = maxOf(DefaultThreadPool.threadCount - 2, 1)
|
||||
val tasks = MeshPrepareTaskManager(renderer)
|
||||
val maxMeshesToLoad = if (SystemInformation.RUNTIME.maxMemory() > 1_000_000_000) 150 else 80
|
||||
|
||||
@Volatile
|
||||
@ -39,9 +38,6 @@ class ChunkMeshingQueue(
|
||||
|
||||
val lock = SimpleLock()
|
||||
|
||||
val preparingTasks: MutableSet<SectionPrepareTask> = mutableSetOf() // current running section preparing tasks
|
||||
val preparingTasksLock = SimpleLock()
|
||||
|
||||
|
||||
val size: Int get() = queue.size
|
||||
|
||||
@ -64,8 +60,8 @@ class ChunkMeshingQueue(
|
||||
|
||||
fun work() {
|
||||
if (working) return // do not work twice
|
||||
val size = preparingTasks.size
|
||||
if (queue.isEmpty() || size >= maxPreparingTasks || renderer.loadingQueue.size >= maxMeshesToLoad) {
|
||||
val size = tasks.size
|
||||
if (queue.isEmpty() || size >= tasks.max || renderer.loadingQueue.size >= maxMeshesToLoad) {
|
||||
return
|
||||
}
|
||||
working = true
|
||||
@ -73,24 +69,20 @@ class ChunkMeshingQueue(
|
||||
|
||||
val items: MutableList<WorldQueueItem> = mutableListOf()
|
||||
lock.lock()
|
||||
for (i in 0 until maxPreparingTasks - size) {
|
||||
for (i in 0 until tasks.max - size) {
|
||||
if (queue.isEmpty()) {
|
||||
break
|
||||
}
|
||||
val item = queue.removeFirst()
|
||||
set.remove(item)
|
||||
set -= item
|
||||
items += item
|
||||
}
|
||||
lock.unlock()
|
||||
for (item in items) {
|
||||
val task = SectionPrepareTask(item.chunkPosition, item.sectionHeight, 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
|
||||
task.runnable.runnable = Runnable {
|
||||
renderer.prepareItem(item, task, task.runnable)
|
||||
}
|
||||
preparingTasksLock.lock()
|
||||
preparingTasks += task
|
||||
preparingTasksLock.unlock()
|
||||
DefaultThreadPool += task.runnable
|
||||
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 task = MeshPrepareTask(item.chunkPosition, item.sectionHeight, runnable)
|
||||
task.runnable.runnable = Runnable { renderer.prepareItem(item, task, task.runnable) }
|
||||
tasks += task
|
||||
}
|
||||
working = false
|
||||
}
|
||||
@ -128,66 +120,28 @@ class ChunkMeshingQueue(
|
||||
this.lock.unlock()
|
||||
}
|
||||
|
||||
fun interrupt(position: ChunkPosition) {
|
||||
preparingTasksLock.acquire()
|
||||
for (task in preparingTasks) {
|
||||
if (task.chunkPosition == position) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
}
|
||||
|
||||
fun interrupt() {
|
||||
preparingTasksLock.acquire()
|
||||
for (task in preparingTasks) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
}
|
||||
|
||||
fun interrupt(position: ChunkPosition, height: SectionHeight) {
|
||||
preparingTasksLock.acquire()
|
||||
for (task in preparingTasks) {
|
||||
if (task.chunkPosition == position && task.sectionHeight == height) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
}
|
||||
|
||||
fun interruptCleanup() {
|
||||
preparingTasksLock.acquire()
|
||||
for (task in preparingTasks) {
|
||||
if (!renderer.visibilityGraph.isChunkVisible(task.chunkPosition)) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
|
||||
this.lock.lock()
|
||||
this.queue.clear()
|
||||
this.set.clear()
|
||||
this.lock.unlock()
|
||||
}
|
||||
|
||||
fun remove(task: SectionPrepareTask) {
|
||||
preparingTasksLock.lock()
|
||||
preparingTasks -= task
|
||||
preparingTasksLock.unlock()
|
||||
|
||||
fun remove(item: WorldQueueItem) {
|
||||
this.lock.lock()
|
||||
if (this.set.remove(item)) {
|
||||
this.queue -= item
|
||||
}
|
||||
|
||||
operator fun minusAssign(task: SectionPrepareTask) = remove(task)
|
||||
|
||||
|
||||
fun remove(position: ChunkPosition, height: SectionHeight) {
|
||||
|
||||
this.lock.unlock()
|
||||
}
|
||||
|
||||
|
||||
fun queue(item: WorldQueueItem) {
|
||||
lock.lock()
|
||||
if (set.remove(item)) {
|
||||
queue.remove(item) // Prevent duplicated entries (to not prepare the same chunk twice (if it changed and was not prepared yet or ...)
|
||||
queue -= item
|
||||
}
|
||||
if (item.chunkPosition == renderer.cameraChunkPosition) {
|
||||
queue.add(0, item)
|
@ -11,12 +11,12 @@
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.world
|
||||
package de.bixilon.minosoft.gui.rendering.world.queue.meshing.tasks
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.kutil.concurrent.pool.ThreadPoolRunnable
|
||||
|
||||
class SectionPrepareTask(
|
||||
class MeshPrepareTask(
|
||||
val chunkPosition: Vec2i,
|
||||
val sectionHeight: Int,
|
||||
val runnable: ThreadPoolRunnable,
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Minosoft
|
||||
* Copyright (C) 2020-2022 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 distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.world.queue.meshing.tasks
|
||||
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
|
||||
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
||||
import de.bixilon.minosoft.data.world.positions.SectionHeight
|
||||
import de.bixilon.minosoft.gui.rendering.world.WorldRenderer
|
||||
|
||||
class MeshPrepareTaskManager(
|
||||
private val renderer: WorldRenderer,
|
||||
val max: Int = maxOf(DefaultThreadPool.threadCount - 2, 1),
|
||||
) {
|
||||
private val tasks: MutableSet<MeshPrepareTask> = mutableSetOf() // current running section preparing tasks
|
||||
private val lock = SimpleLock()
|
||||
|
||||
val size: Int get() = tasks.size
|
||||
|
||||
|
||||
fun add(task: MeshPrepareTask) {
|
||||
lock.lock()
|
||||
tasks += task
|
||||
lock.unlock()
|
||||
|
||||
DefaultThreadPool += task.runnable
|
||||
}
|
||||
|
||||
operator fun plusAssign(task: MeshPrepareTask) = add(task)
|
||||
|
||||
fun remove(task: MeshPrepareTask) {
|
||||
lock.lock()
|
||||
tasks -= task
|
||||
lock.unlock()
|
||||
}
|
||||
|
||||
operator fun minusAssign(task: MeshPrepareTask) = remove(task)
|
||||
|
||||
fun interruptAll() {
|
||||
lock.acquire()
|
||||
for (task in tasks) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
lock.release()
|
||||
}
|
||||
|
||||
fun interrupt(position: ChunkPosition) {
|
||||
lock.acquire()
|
||||
for (task in tasks) {
|
||||
if (task.chunkPosition == position) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
}
|
||||
lock.release()
|
||||
}
|
||||
|
||||
fun interrupt(position: ChunkPosition, height: SectionHeight) {
|
||||
lock.acquire()
|
||||
for (task in tasks) {
|
||||
if (task.chunkPosition == position && task.sectionHeight == height) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
}
|
||||
lock.release()
|
||||
}
|
||||
|
||||
|
||||
fun cleanup() {
|
||||
lock.acquire()
|
||||
for (task in tasks) {
|
||||
if (!renderer.visibilityGraph.isChunkVisible(task.chunkPosition)) {
|
||||
task.runnable.interrupt()
|
||||
}
|
||||
}
|
||||
lock.release()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user