WeightedBlockRender: unpack if total weight < 20

This makes it a bit faster to lookup the model. It can even be more enhanced by unpacking the whole section once (every model) and then running culling and stuff afterwards. In the future...
This commit is contained in:
Moritz Zwerger 2025-02-04 21:47:49 +01:00
parent a6cf950e29
commit c21e1cb852
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
3 changed files with 125 additions and 1 deletions

View File

@ -79,6 +79,7 @@ val architecture = properties["architecture"]?.let { Architectures[it] } ?: Plat
logger.info("Building for ${os.name.lowercase()}, ${architecture.name.lowercase()}")
repositories {
mavenLocal()
mavenCentral()
maven(url = "https://s01.oss.sonatype.org/content/repositories/releases/")
}

View File

@ -0,0 +1,97 @@
/*
* 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.models.block.state.render
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
import de.bixilon.kutil.unsafe.UnsafeUtil.setUnsafeAccessible
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.gui.rendering.models.block.state.baked.BakedModel
import de.bixilon.minosoft.gui.rendering.models.block.state.render.WeightedBlockRender.WeightedEntry
import de.bixilon.minosoft.test.ITUtil.allocate
import org.testng.Assert.assertSame
import org.testng.annotations.Test
import java.util.*
@Test(groups = ["rendering"])
class WeightedBlockRenderTest {
private val get = WeightedBlockRender::class.java.getDeclaredMethod("getModel", Random::class.java, BlockPosition::class.java).apply { setUnsafeAccessible() }
private val modelA = BakedModel::class.java.allocate().apply { this::properties.forceSet(arrayOfNulls(Directions.SIZE)) }
private val modelB = BakedModel::class.java.allocate().apply { this::properties.forceSet(arrayOfNulls(Directions.SIZE)) }
private val position = BlockPosition(1, 2, 3)
private fun WeightedBlockRender.getModel(random: Random?, position: BlockPosition): BakedModel {
return get.invoke(this, random, position) as BakedModel
}
private fun create(models: Array<WeightedEntry>): WeightedBlockRender {
return WeightedBlockRender(models, models.sumOf { it.weight })
}
fun `single model no random`() {
val render = create(arrayOf(WeightedEntry(3, modelA)))
assertSame(render.getModel(null, position), modelA)
}
fun `single model random`() {
val render = create(arrayOf(WeightedEntry(3, modelA)))
assertSame(render.getModel(Random(), position), modelA)
}
fun `two models no random`() {
val render = create(arrayOf(WeightedEntry(3, modelA), WeightedEntry(3, modelB)))
assertSame(render.getModel(null, position), modelA)
}
fun `two models random`() {
val render = create(arrayOf(WeightedEntry(3, modelA), WeightedEntry(3, modelB)))
assertSame(render.getModel(Random(0L), position), modelB)
}
fun `two models high weight random`() {
val render = create(arrayOf(WeightedEntry(100, modelA), WeightedEntry(100, modelB)))
assertSame(render.getModel(Random(0L), position), modelB)
}
fun `two models random 2`() {
val render = create(arrayOf(WeightedEntry(3, modelA), WeightedEntry(3, modelB)))
assertSame(render.getModel(Random(0L), BlockPosition(3, 1, 1)), modelA)
}
fun `two models high weight random 2`() {
val render = create(arrayOf(WeightedEntry(100, modelA), WeightedEntry(100, modelB)))
assertSame(render.getModel(Random(0L), BlockPosition(1, 1, 1)), modelA)
}
/* fun `benchmark models random`() {
val render = create(arrayOf(WeightedEntry(1, modelA), WeightedEntry(1, modelB), WeightedEntry(1, modelA), WeightedEntry(1, modelB)))
val random = Random(0L)
val time = measureTime {
for (i in 0 until 499999999) {
render.getModel(random, position)
}
}
println("Took: ${time.inWholeNanoseconds.formatNanos()}")
}
*/
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* 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.
*
@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.models.block.state.render
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.kutil.array.ArrayUtil.cast
import de.bixilon.kutil.exception.Broken
import de.bixilon.minosoft.data.container.stack.ItemStack
import de.bixilon.minosoft.data.direction.Directions
@ -39,6 +40,7 @@ class WeightedBlockRender(
val totalWeight: Int,
) : BlockRender {
private val properties = models.getProperties()
private val unpacked = unpack(totalWeight, models)
override fun getProperties(direction: Directions): SideProperties? {
return properties[direction.ordinal] // TODO: get random block model
@ -50,6 +52,10 @@ class WeightedBlockRender(
var weightLeft = abs(random.nextLong().toInt() % totalWeight)
if (unpacked != null) {
return unpacked[weightLeft]
}
for ((weight, model) in models) {
weightLeft -= weight
if (weightLeft >= 0) continue
@ -118,4 +124,24 @@ class WeightedBlockRender(
return sizes
}
companion object {
const val UNPACK_LIMIT = 20
private fun unpack(total: Int, models: Array<WeightedEntry>): Array<BakedModel>? {
if (total >= UNPACK_LIMIT) return null
val unpacked = arrayOfNulls<BakedModel>(total)
var index = 0
for ((weight, model) in models) {
for (i in 0 until weight) {
unpacked[index] = model
index++
}
}
return unpacked.cast()
}
}
}