outsource allocator, section occlusion: reuse memory

This commit is contained in:
Moritz Zwerger 2025-02-08 19:55:48 +01:00
parent d2dfe2b7f6
commit b174638aaf
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
5 changed files with 80 additions and 20 deletions

View File

@ -20,6 +20,7 @@ import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.registries.blocks.state.BlockStateFlags
import de.bixilon.minosoft.data.registries.blocks.types.properties.shape.special.FullOpaqueBlock
import de.bixilon.minosoft.data.registries.blocks.types.properties.shape.special.PotentialFullOpaqueBlock
import de.bixilon.minosoft.gui.rendering.util.allocator.ShortAllocator
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
import java.util.*
@ -29,7 +30,6 @@ class SectionOcclusion(
) {
private var occlusion = EMPTY
private var calculate = false
private val regions by lazy { ShortArray(ProtocolDefinition.BLOCKS_PER_SECTION) }
fun clear(notify: Boolean) {
update(EMPTY, notify)
@ -49,7 +49,13 @@ class SectionOcclusion(
clear(notify)
return
}
update(calculateOcclusion(floodFill()), notify)
val array = ALLOCATOR.allocate(ProtocolDefinition.BLOCKS_PER_SECTION)
try {
val regions = floodFill(array)
update(calculateOcclusion(regions), notify)
} finally {
ALLOCATOR.free(array)
}
}
private inline fun ShortArray.updateRegion(x: Int, y: Int, z: Int, id: Short): Boolean {
@ -85,20 +91,20 @@ class SectionOcclusion(
if (y < ProtocolDefinition.SECTION_MAX_Y) trace(regions, x, y + 1, z, nextId)
}
private fun floodFill(): ShortArray {
private fun floodFill(array: ShortArray): ShortArray {
// mark regions and check direct neighbours
Arrays.fill(regions, 0.toShort())
Arrays.fill(array, 0.toShort())
var next: Short = 0
for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) {
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
startTrace(regions, x, y, z, ++next)
startTrace(array, x, y, z, ++next)
}
}
}
return regions
return array
}
private fun calculateOcclusion(regions: ShortArray): BooleanArray {
@ -198,6 +204,7 @@ class SectionOcclusion(
companion object {
private val EMPTY = BooleanArray(CubeDirections.CUBE_DIRECTION_COMBINATIONS)
private val ALLOCATOR = ShortAllocator()
fun BlockState?._isFullyOpaque(): Boolean {

View File

@ -14,6 +14,7 @@
package de.bixilon.minosoft.data.world.container.palette.data.array
import de.bixilon.minosoft.data.world.container.palette.data.PaletteData
import de.bixilon.minosoft.gui.rendering.util.allocator.LongAllocator
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_1_16
import de.bixilon.minosoft.protocol.protocol.buffers.play.PlayInByteBuffer
@ -39,7 +40,7 @@ class ArrayPaletteData(
} else {
(this.size + valuesPerLong - 1) / valuesPerLong
}
this.data = LongArrayAllocator.claim(this.size)
this.data = ALLOCATOR.allocate(this.size)
if (packetSize != size) {
buffer.pointer += packetSize * Long.SIZE_BYTES // data is ignored
return
@ -69,10 +70,12 @@ class ArrayPaletteData(
}
override fun free() {
LongArrayAllocator.free(data)
ALLOCATOR.free(data)
}
companion object {
private val ALLOCATOR = LongAllocator()
const val LONG_BIT_SPLITTING_VERSION = V_1_16 // ToDo: When did this changed? is just a guess
}
}

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.util.allocator
@Deprecated("kutil 1.27.1")
class LongAllocator : TemporaryAllocator<LongArray>() {
override fun getSize(value: LongArray) = value.size
override fun create(size: Int) = LongArray(size)
}

View File

@ -0,0 +1,22 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.util.allocator
@Deprecated("kutil 1.27.1")
class ShortAllocator : TemporaryAllocator<ShortArray>() {
override fun getSize(value: ShortArray) = value.size
override fun create(size: Int) = ShortArray(size)
}

View File

@ -11,15 +11,16 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.container.palette.data.array
package de.bixilon.minosoft.gui.rendering.util.allocator
import de.bixilon.kutil.concurrent.lock.locks.reentrant.ReentrantLock
import java.lang.ref.WeakReference
object LongArrayAllocator {
@Deprecated("kutil 1.27.1")
abstract class TemporaryAllocator<T> {
private val lock = ReentrantLock()
private val list: ArrayList<WeakReference<LongArray>> = ArrayList()
private val list: ArrayList<WeakReference<T>> = ArrayList()
private fun cleanup() {
lock.lock()
@ -33,36 +34,41 @@ object LongArrayAllocator {
lock.unlock()
}
fun free(array: LongArray) {
fun free(array: T) {
lock.lock()
cleanup()
list.add(0, WeakReference(array))
lock.unlock()
}
fun claim(size: Int): LongArray {
fun allocate(size: Int): T {
lock.lock()
var array: LongArray? = null
var array: T? = null
val iterator = list.iterator()
while (iterator.hasNext()) {
val reference = iterator.next()
array = reference.get()
if (array == null) {
val entry = reference.get()
if (entry == null) {
iterator.remove()
continue
}
if (array.size >= size) {
if (getSize(entry) >= size) {
array = entry
iterator.remove()
break
}
}
lock.unlock()
if (array != null && array.size >= size) return array
if (array != null) return array
// println("Allocating long array of size $size")
// println("Allocating array of size $size")
return LongArray(size)
return create(size)
}
protected abstract fun getSize(value: T): Int
abstract fun create(size: Int): T
}