mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-16 19:05:02 -04:00
mesh unloading queue
This commit is contained in:
parent
4a9e6ffd6f
commit
f3d8b74607
@ -60,6 +60,7 @@ 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.MeshUnloadingQueue
|
||||
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
|
||||
@ -80,7 +81,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
|
||||
|
||||
class WorldRenderer(
|
||||
private val connection: PlayConnection,
|
||||
val connection: PlayConnection,
|
||||
override val renderWindow: RenderWindow,
|
||||
) : Renderer, OpaqueDrawable, TranslucentDrawable, TransparentDrawable {
|
||||
private val profile = connection.profiles.block
|
||||
@ -112,12 +113,11 @@ class WorldRenderer(
|
||||
private val meshesToLoad: MutableList<WorldQueueItem> = mutableListOf() // prepared meshes, that can be loaded in the (next) frame
|
||||
private val meshesToLoadSet: MutableSet<WorldQueueItem> = HashSet()
|
||||
private val meshesToLoadLock = SimpleLock()
|
||||
private val meshesToUnload: MutableList<WorldMesh> = mutableListOf() // prepared meshes, that can be loaded in the (next) frame
|
||||
private val meshesToUnloadLock = SimpleLock()
|
||||
private 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
|
||||
private var visible = VisibleMeshes() // This name might be confusing. Those faces are from blocks.
|
||||
var visible = VisibleMeshes() // This name might be confusing. Those faces are from blocks.
|
||||
|
||||
private var previousViewDistance = connection.world.view.viewDistance
|
||||
|
||||
@ -264,7 +264,7 @@ class WorldRenderer(
|
||||
culledQueueLock.lock()
|
||||
queueLock.lock()
|
||||
meshesToLoadLock.lock()
|
||||
meshesToUnloadLock.lock()
|
||||
unloadingQueue.lock()
|
||||
loadedMeshesLock.lock()
|
||||
|
||||
val loadedMeshesToRemove: MutableSet<Vec2i> = HashSet()
|
||||
@ -273,12 +273,7 @@ class WorldRenderer(
|
||||
continue
|
||||
}
|
||||
loadedMeshesToRemove += chunkPosition
|
||||
for (mesh in sections.values) {
|
||||
if (mesh in meshesToUnload) {
|
||||
continue
|
||||
}
|
||||
meshesToUnload += mesh
|
||||
}
|
||||
unloadingQueue.forceQueue(sections.values)
|
||||
}
|
||||
loadedMeshes -= loadedMeshesToRemove
|
||||
|
||||
@ -309,7 +304,7 @@ class WorldRenderer(
|
||||
queueLock.unlock()
|
||||
culledQueueLock.unlock()
|
||||
meshesToLoadLock.unlock()
|
||||
meshesToUnloadLock.unlock()
|
||||
unloadingQueue.unlock()
|
||||
} else {
|
||||
prepareWorld()
|
||||
}
|
||||
@ -342,13 +337,11 @@ class WorldRenderer(
|
||||
meshesToLoadLock.lock()
|
||||
loadedMeshesLock.lock()
|
||||
|
||||
meshesToUnloadLock.lock()
|
||||
unloadingQueue.lock()
|
||||
for (sections in loadedMeshes.values) {
|
||||
for (mesh in sections.values) {
|
||||
meshesToUnload += mesh
|
||||
}
|
||||
unloadingQueue.forceQueue(sections.values, false)
|
||||
}
|
||||
meshesToUnloadLock.unlock()
|
||||
unloadingQueue.unlock()
|
||||
|
||||
culledQueue.clear()
|
||||
loadedMeshes.clear()
|
||||
@ -375,7 +368,7 @@ class WorldRenderer(
|
||||
culledQueueLock.lock()
|
||||
queueLock.lock()
|
||||
meshesToLoadLock.lock()
|
||||
meshesToUnloadLock.lock()
|
||||
unloadingQueue.lock()
|
||||
loadedMeshesLock.lock()
|
||||
|
||||
val meshes = loadedMeshes.remove(chunkPosition)
|
||||
@ -395,16 +388,12 @@ class WorldRenderer(
|
||||
}
|
||||
}
|
||||
preparingTasksLock.release()
|
||||
if (meshes != null) {
|
||||
for (mesh in meshes.values) {
|
||||
meshesToUnload += mesh
|
||||
}
|
||||
}
|
||||
meshes?.let { unloadingQueue.forceQueue(meshes.values, false) }
|
||||
|
||||
loadedMeshesLock.unlock()
|
||||
culledQueueLock.unlock()
|
||||
meshesToLoadLock.unlock()
|
||||
meshesToUnloadLock.unlock()
|
||||
unloadingQueue.unlock()
|
||||
queueLock.unlock()
|
||||
}
|
||||
|
||||
@ -508,10 +497,10 @@ class WorldRenderer(
|
||||
culledQueueLock.lock()
|
||||
queueLock.lock()
|
||||
meshesToLoadLock.lock()
|
||||
meshesToUnloadLock.lock()
|
||||
unloadingQueue.lock()
|
||||
loadedMeshesLock.lock()
|
||||
loadedMeshes[item.chunkPosition]?.let {
|
||||
meshesToUnload += it.remove(item.sectionHeight) ?: return@let
|
||||
unloadingQueue.forceQueue(it.remove(item.sectionHeight) ?: return@let, false)
|
||||
if (it.isEmpty()) {
|
||||
loadedMeshes -= item.chunkPosition
|
||||
}
|
||||
@ -544,7 +533,7 @@ class WorldRenderer(
|
||||
queueLock.unlock()
|
||||
culledQueueLock.unlock()
|
||||
meshesToLoadLock.unlock()
|
||||
meshesToUnloadLock.unlock()
|
||||
unloadingQueue.unlock()
|
||||
}
|
||||
|
||||
private fun internalQueueSection(chunkPosition: Vec2i, sectionHeight: Int, chunk: Chunk, section: ChunkSection, ignoreFrustum: Boolean, neighbours: Array<Chunk>): Boolean {
|
||||
@ -664,30 +653,14 @@ class WorldRenderer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun unloadMeshes() {
|
||||
meshesToUnloadLock.acquire()
|
||||
if (meshesToUnload.isEmpty()) {
|
||||
meshesToUnloadLock.release()
|
||||
return
|
||||
}
|
||||
|
||||
val time = millis()
|
||||
val maxTime = if (connection.player.velocity.empty) 50L else 20L // If the player is still, then we can load more chunks (to not cause lags)
|
||||
|
||||
while (meshesToUnload.isNotEmpty() && (millis() - time < maxTime)) {
|
||||
val mesh = meshesToUnload.removeAt(0)
|
||||
visible.removeMesh(mesh)
|
||||
mesh.unload()
|
||||
}
|
||||
meshesToUnloadLock.release()
|
||||
}
|
||||
|
||||
override fun postPrepareDraw() {
|
||||
if (clearVisibleNextFrame) {
|
||||
visible.clear()
|
||||
clearVisibleNextFrame = false
|
||||
}
|
||||
unloadMeshes()
|
||||
|
||||
unloadingQueue.work()
|
||||
loadMeshes()
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.minosoft.gui.rendering.util.VecUtil.empty
|
||||
|
||||
object WorldRendererUtil {
|
||||
const val STILL_LOADING_TIME = 50L
|
||||
const val MOVING_LOADING_TIME = 20L
|
||||
|
||||
|
||||
// If the player is still, then we can load more chunks (to not cause lags)
|
||||
val WorldRenderer.loadingTime: Long get() = if (connection.player.velocity.empty) STILL_LOADING_TIME else MOVING_LOADING_TIME // TODO: get of camera
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
|
||||
import de.bixilon.kutil.time.TimeUtil
|
||||
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
||||
import de.bixilon.minosoft.gui.rendering.world.WorldRenderer
|
||||
import de.bixilon.minosoft.gui.rendering.world.WorldRendererUtil.loadingTime
|
||||
import de.bixilon.minosoft.gui.rendering.world.mesh.WorldMesh
|
||||
|
||||
class MeshUnloadingQueue(
|
||||
private val renderer: WorldRenderer,
|
||||
) {
|
||||
private val meshes: MutableList<WorldMesh> = mutableListOf() // prepared meshes, that can be loaded in the (next) frame
|
||||
private val positions: MutableSet<ChunkPosition> = mutableSetOf()
|
||||
private val lock = SimpleLock()
|
||||
|
||||
|
||||
private fun forceWork() {
|
||||
if (meshes.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val time = TimeUtil.millis()
|
||||
val maxTime = renderer.loadingTime
|
||||
|
||||
while (meshes.isNotEmpty() && (TimeUtil.millis() - time < maxTime)) {
|
||||
val mesh = meshes.removeAt(0)
|
||||
this.positions -= mesh.chunkPosition
|
||||
renderer.visible.removeMesh(mesh)
|
||||
mesh.unload()
|
||||
}
|
||||
}
|
||||
|
||||
fun work() {
|
||||
lock.lock()
|
||||
try {
|
||||
forceWork()
|
||||
} finally {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
fun forceQueue(mesh: WorldMesh, lock: Boolean = true) {
|
||||
if (lock) this.lock.lock()
|
||||
|
||||
if (mesh.chunkPosition == renderer.connection.player.positionInfo.chunkPosition) { // TODO: camera
|
||||
this.meshes.add(0, mesh)
|
||||
} else {
|
||||
this.meshes += mesh
|
||||
}
|
||||
this.positions += mesh.chunkPosition
|
||||
|
||||
if (lock) this.lock.unlock()
|
||||
}
|
||||
|
||||
fun queue(mesh: WorldMesh, lock: Boolean = true) {
|
||||
if (lock) this.lock.lock()
|
||||
if (mesh.chunkPosition in this.positions) {
|
||||
this.lock.unlock()
|
||||
return
|
||||
}
|
||||
forceQueue(mesh, false)
|
||||
if (lock) this.lock.unlock()
|
||||
}
|
||||
|
||||
fun forceQueue(meshes: Collection<WorldMesh>, lock: Boolean = true) {
|
||||
if (lock) this.lock.lock()
|
||||
for (mesh in meshes) {
|
||||
forceQueue(mesh, false)
|
||||
}
|
||||
if (lock) this.lock.unlock()
|
||||
}
|
||||
|
||||
fun queue(meshes: Collection<WorldMesh>) {
|
||||
this.lock.lock()
|
||||
for (mesh in meshes) {
|
||||
queue(mesh, false)
|
||||
}
|
||||
this.lock.unlock()
|
||||
}
|
||||
|
||||
fun lock() {
|
||||
lock.lock()
|
||||
}
|
||||
|
||||
fun unlock() {
|
||||
lock.unlock()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user