mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-17 03:15:35 -04:00
world renderer: loaded meshes
This commit is contained in:
parent
50dc60d3f1
commit
0e45237ce4
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
||||
import de.bixilon.minosoft.gui.rendering.world.mesh.VisibleMeshes
|
||||
import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
|
||||
class LoadedMeshes(
|
||||
private val renderer: WorldRenderer,
|
||||
) {
|
||||
val meshes: MutableMap<Vec2i, Int2ObjectOpenHashMap<WorldMesh>> = mutableMapOf() // all prepared (and up to date) meshes
|
||||
private val lock = SimpleLock()
|
||||
|
||||
|
||||
val size: Int get() = meshes.size
|
||||
|
||||
|
||||
fun cleanup(lock: Boolean) {
|
||||
val remove: MutableSet<Vec2i> = HashSet()
|
||||
if (lock) this.lock.lock()
|
||||
|
||||
for ((chunkPosition, sections) in meshes) {
|
||||
if (renderer.visibilityGraph.isInViewDistance(chunkPosition)) {
|
||||
continue
|
||||
}
|
||||
remove += chunkPosition
|
||||
renderer.unloadingQueue.forceQueue(sections.values)
|
||||
}
|
||||
meshes -= remove
|
||||
|
||||
if (lock) this.lock.unlock()
|
||||
}
|
||||
|
||||
fun clear(lock: Boolean) {
|
||||
if (lock) this.lock.lock()
|
||||
|
||||
for (sections in meshes.values) {
|
||||
renderer.unloadingQueue.forceQueue(sections.values, lock)
|
||||
}
|
||||
|
||||
if (lock) this.lock.unlock()
|
||||
}
|
||||
|
||||
fun unload(position: ChunkPosition, lock: Boolean) {
|
||||
if (lock) this.lock.lock()
|
||||
|
||||
val meshes = this.meshes.remove(position)
|
||||
|
||||
if (meshes != null) {
|
||||
renderer.unloadingQueue.forceQueue(meshes.values, lock)
|
||||
}
|
||||
|
||||
if (lock) this.lock.unlock()
|
||||
}
|
||||
|
||||
fun unload(position: ChunkPosition, sectionHeight: Int, lock: Boolean) {
|
||||
if (lock) this.lock.lock()
|
||||
|
||||
val meshes = this.meshes[position]
|
||||
|
||||
if (meshes != null) {
|
||||
meshes.remove(sectionHeight)?.let {
|
||||
renderer.unloadingQueue.forceQueue(it, lock)
|
||||
|
||||
if (meshes.isEmpty()) {
|
||||
this.meshes.remove(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lock) this.lock.unlock()
|
||||
}
|
||||
|
||||
|
||||
operator fun contains(position: ChunkPosition): Boolean {
|
||||
lock.acquire()
|
||||
val contains = position in this.meshes
|
||||
lock.release()
|
||||
return contains
|
||||
}
|
||||
|
||||
|
||||
fun collect(visible: VisibleMeshes) {
|
||||
lock.acquire()
|
||||
for ((chunkPosition, meshes) in this.meshes) {
|
||||
if (!renderer.visibilityGraph.isChunkVisible(chunkPosition)) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (entry in meshes.int2ObjectEntrySet()) {
|
||||
val mesh = entry.value
|
||||
if (!renderer.visibilityGraph.isSectionVisible(chunkPosition, entry.intKey, mesh.minPosition, mesh.maxPosition, false)) {
|
||||
continue
|
||||
}
|
||||
visible.addMesh(mesh)
|
||||
}
|
||||
}
|
||||
lock.release()
|
||||
}
|
||||
|
||||
fun lock() {
|
||||
this.lock.lock()
|
||||
}
|
||||
|
||||
fun unlock() {
|
||||
this.lock.unlock()
|
||||
}
|
||||
}
|
@ -93,8 +93,7 @@ class WorldRenderer(
|
||||
private val solidSectionPreparer: SolidSectionPreparer = SolidCullSectionPreparer(renderWindow)
|
||||
private val fluidSectionPreparer: FluidSectionPreparer = FluidCullSectionPreparer(renderWindow)
|
||||
|
||||
val loadedMeshes: MutableMap<Vec2i, Int2ObjectOpenHashMap<WorldMesh>> = mutableMapOf() // all prepared (and up to date) meshes
|
||||
val loadedMeshesLock = SimpleLock()
|
||||
val loaded = LoadedMeshes(this)
|
||||
|
||||
val maxPreparingTasks = maxOf(DefaultThreadPool.threadCount - 2, 1)
|
||||
private val preparingTasks: MutableSet<SectionPrepareTask> = mutableSetOf() // current running section preparing tasks
|
||||
@ -110,7 +109,7 @@ class WorldRenderer(
|
||||
|
||||
val maxMeshesToLoad = if (SystemInformation.RUNTIME.maxMemory() > 1_000_000_000) 150 else 80
|
||||
private val loadingQueue = MeshLoadingQueue(this)
|
||||
private val unloadingQueue = MeshUnloadingQueue(this)
|
||||
val unloadingQueue = MeshUnloadingQueue(this)
|
||||
|
||||
// all meshes that will be rendered in the next frame (might be changed, when the frustum changes or a chunk gets loaded, ...)
|
||||
private var clearVisibleNextFrame = false
|
||||
@ -122,13 +121,12 @@ class WorldRenderer(
|
||||
var cameraChunkPosition = Vec2i.EMPTY
|
||||
private var cameraSectionHeight = 0
|
||||
|
||||
val visibleSize: String
|
||||
get() = visible.sizeString
|
||||
val loadedMeshesSize: Int by loadedMeshes::size
|
||||
val culledQueuedSize: Int by culledQueue::size
|
||||
val meshesToLoadSize: Int by loadingQueue::size
|
||||
val queueSize: Int by queue::size
|
||||
val preparingTasksSize: Int by preparingTasks::size
|
||||
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() = preparingTasks.size
|
||||
|
||||
override fun init(latch: CountUpAndDownLatch) {
|
||||
renderWindow.modelLoader.load(latch)
|
||||
@ -262,17 +260,9 @@ class WorldRenderer(
|
||||
queueLock.lock()
|
||||
loadingQueue.lock()
|
||||
unloadingQueue.lock()
|
||||
loadedMeshesLock.lock()
|
||||
loaded.lock()
|
||||
|
||||
val loadedMeshesToRemove: MutableSet<Vec2i> = HashSet()
|
||||
for ((chunkPosition, sections) in loadedMeshes) {
|
||||
if (visibilityGraph.isInViewDistance(chunkPosition)) {
|
||||
continue
|
||||
}
|
||||
loadedMeshesToRemove += chunkPosition
|
||||
unloadingQueue.forceQueue(sections.values)
|
||||
}
|
||||
loadedMeshes -= loadedMeshesToRemove
|
||||
loaded.cleanup(false)
|
||||
|
||||
val toRemove: MutableSet<Vec2i> = HashSet()
|
||||
for (chunkPosition in culledQueue.keys) {
|
||||
@ -296,7 +286,7 @@ class WorldRenderer(
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
|
||||
loadedMeshesLock.unlock()
|
||||
loaded.unlock()
|
||||
queueLock.unlock()
|
||||
culledQueueLock.unlock()
|
||||
loadingQueue.unlock()
|
||||
@ -331,21 +321,9 @@ class WorldRenderer(
|
||||
culledQueueLock.lock()
|
||||
queueLock.lock()
|
||||
loadingQueue.lock()
|
||||
loadedMeshesLock.lock()
|
||||
loaded.lock()
|
||||
|
||||
unloadingQueue.lock()
|
||||
for (sections in loadedMeshes.values) {
|
||||
unloadingQueue.forceQueue(sections.values, false)
|
||||
}
|
||||
unloadingQueue.unlock()
|
||||
|
||||
culledQueue.clear()
|
||||
loadedMeshes.clear()
|
||||
queue.clear()
|
||||
queueSet.clear()
|
||||
loadingQueue.clear()
|
||||
|
||||
clearVisibleNextFrame = true
|
||||
|
||||
preparingTasksLock.acquire()
|
||||
for (task in preparingTasks) {
|
||||
@ -353,7 +331,17 @@ class WorldRenderer(
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
|
||||
loadedMeshesLock.unlock()
|
||||
unloadingQueue.lock()
|
||||
loaded.clear(false)
|
||||
|
||||
culledQueue.clear()
|
||||
queue.clear()
|
||||
queueSet.clear()
|
||||
loadingQueue.clear(false)
|
||||
|
||||
clearVisibleNextFrame = true
|
||||
|
||||
loaded.unlock()
|
||||
queueLock.unlock()
|
||||
culledQueueLock.unlock()
|
||||
loadingQueue.unlock()
|
||||
@ -364,16 +352,8 @@ class WorldRenderer(
|
||||
queueLock.lock()
|
||||
loadingQueue.lock()
|
||||
unloadingQueue.lock()
|
||||
loadedMeshesLock.lock()
|
||||
loaded.lock()
|
||||
|
||||
val meshes = loadedMeshes.remove(chunkPosition)
|
||||
|
||||
culledQueue.remove(chunkPosition)
|
||||
|
||||
queue.removeAll { it.chunkPosition == chunkPosition }
|
||||
queueSet.removeAll { it.chunkPosition == chunkPosition }
|
||||
|
||||
loadingQueue.abort(chunkPosition, false)
|
||||
|
||||
preparingTasksLock.acquire()
|
||||
for (task in preparingTasks) {
|
||||
@ -382,9 +362,18 @@ class WorldRenderer(
|
||||
}
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
meshes?.let { unloadingQueue.forceQueue(meshes.values, false) }
|
||||
|
||||
loadedMeshesLock.unlock()
|
||||
culledQueue.remove(chunkPosition)
|
||||
|
||||
queue.removeAll { it.chunkPosition == chunkPosition }
|
||||
queueSet.removeAll { it.chunkPosition == chunkPosition }
|
||||
|
||||
loadingQueue.abort(chunkPosition, false)
|
||||
|
||||
|
||||
val meshes = loaded.unload(chunkPosition, false)
|
||||
|
||||
loaded.unlock()
|
||||
culledQueueLock.unlock()
|
||||
loadingQueue.unlock()
|
||||
unloadingQueue.unlock()
|
||||
@ -481,13 +470,8 @@ class WorldRenderer(
|
||||
queueLock.lock()
|
||||
loadingQueue.lock()
|
||||
unloadingQueue.lock()
|
||||
loadedMeshesLock.lock()
|
||||
loadedMeshes[item.chunkPosition]?.let {
|
||||
unloadingQueue.forceQueue(it.remove(item.sectionHeight) ?: return@let, false)
|
||||
if (it.isEmpty()) {
|
||||
loadedMeshes -= item.chunkPosition
|
||||
}
|
||||
}
|
||||
loaded.lock()
|
||||
loaded.unload(item.chunkPosition, item.sectionHeight, false)
|
||||
|
||||
culledQueue[item.chunkPosition]?.let {
|
||||
it.remove(item.sectionHeight)
|
||||
@ -510,7 +494,7 @@ class WorldRenderer(
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
|
||||
loadedMeshesLock.unlock()
|
||||
loaded.unlock()
|
||||
queueLock.unlock()
|
||||
culledQueueLock.unlock()
|
||||
loadingQueue.unlock()
|
||||
@ -565,18 +549,16 @@ class WorldRenderer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun queueChunk(chunkPosition: Vec2i, chunk: Chunk = world.chunks[chunkPosition]!!) {
|
||||
private fun queueChunk(chunkPosition: Vec2i, chunk: Chunk) {
|
||||
val neighbours = chunk.neighbours.get()
|
||||
if (neighbours == null || !chunk.isFullyLoaded || renderWindow.state == RenderingStates.PAUSED) {
|
||||
return
|
||||
}
|
||||
this.loadedMeshesLock.acquire()
|
||||
if (this.loadedMeshes.containsKey(chunkPosition)) {
|
||||
// ToDo: this also ignores light updates
|
||||
this.loadedMeshesLock.release()
|
||||
|
||||
// should not queue, it is already loaded
|
||||
if (chunkPosition in loaded) {
|
||||
return
|
||||
}
|
||||
this.loadedMeshesLock.release()
|
||||
|
||||
|
||||
// ToDo: Check if chunk is visible (not section, chunk)
|
||||
@ -669,20 +651,7 @@ class WorldRenderer(
|
||||
|
||||
val visible = VisibleMeshes(cameraPosition, this.visible)
|
||||
|
||||
loadedMeshesLock.acquire()
|
||||
for ((chunkPosition, meshes) in this.loadedMeshes) {
|
||||
if (!visibilityGraph.isChunkVisible(chunkPosition)) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (entry in meshes.int2ObjectEntrySet()) {
|
||||
val mesh = entry.value
|
||||
if (visibilityGraph.isSectionVisible(chunkPosition, entry.intKey, mesh.minPosition, mesh.maxPosition, false)) {
|
||||
visible.addMesh(mesh)
|
||||
}
|
||||
}
|
||||
}
|
||||
loadedMeshesLock.release()
|
||||
loaded.collect(visible)
|
||||
|
||||
culledQueueLock.acquire() // The queue method needs the full lock of the culledQueue
|
||||
val nextQueue: MutableMap<Vec2i, Pair<Chunk, IntOpenHashSet>> = mutableMapOf()
|
||||
|
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
class ChunkMeshingQueue
|
@ -13,6 +13,7 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.world.queue
|
||||
|
||||
import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.kutil.time.TimeUtil
|
||||
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
||||
@ -44,14 +45,20 @@ class MeshLoadingQueue(
|
||||
val maxTime = renderer.maxBusyTime // If the player is still, then we can load more chunks (to not cause lags)
|
||||
|
||||
|
||||
renderer.loadedMeshesLock.lock()
|
||||
while (meshes.isNotEmpty() && (TimeUtil.millis() - start < maxTime)) {
|
||||
val mesh = meshes.removeAt(0)
|
||||
renderer.loaded.lock()
|
||||
var meshes: Int2ObjectOpenHashMap<WorldMesh> = unsafeNull()
|
||||
var position: ChunkPosition? = null
|
||||
while (this.meshes.isNotEmpty() && (TimeUtil.millis() - start < maxTime)) {
|
||||
val mesh = this.meshes.removeAt(0)
|
||||
this.positions -= mesh.chunkPosition
|
||||
|
||||
mesh.load()
|
||||
|
||||
val meshes = renderer.loadedMeshes.getOrPut(mesh.chunkPosition) { Int2ObjectOpenHashMap() }
|
||||
if (position != mesh.chunkPosition) {
|
||||
meshes = renderer.loaded.meshes.getOrPut(mesh.chunkPosition) { Int2ObjectOpenHashMap() }
|
||||
position = mesh.chunkPosition
|
||||
}
|
||||
|
||||
|
||||
meshes.put(mesh.sectionHeight, mesh)?.let {
|
||||
renderer.visible.removeMesh(it)
|
||||
@ -64,7 +71,7 @@ class MeshLoadingQueue(
|
||||
renderer.visible.addMesh(mesh)
|
||||
}
|
||||
}
|
||||
renderer.loadedMeshesLock.unlock()
|
||||
renderer.loaded.unlock()
|
||||
|
||||
lock.unlock()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user