From c21e1cb8522e044d1c37eb77d07f5a350cfd99b9 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Tue, 4 Feb 2025 21:47:49 +0100 Subject: [PATCH] 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... --- build.gradle.kts | 1 + .../state/render/WeightedBlockRenderTest.kt | 97 +++++++++++++++++++ .../block/state/render/WeightedBlockRender.kt | 28 +++++- 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRenderTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 6838e1d60..03144f2d4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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/") } diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRenderTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRenderTest.kt new file mode 100644 index 000000000..d3e7f7f45 --- /dev/null +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRenderTest.kt @@ -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 . + * + * 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): 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()}") + } + */ +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRender.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRender.kt index ae0cbf6c7..8adc2296f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRender.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/models/block/state/render/WeightedBlockRender.kt @@ -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): Array? { + if (total >= UNPACK_LIMIT) return null + + val unpacked = arrayOfNulls(total) + + var index = 0 + for ((weight, model) in models) { + for (i in 0 until weight) { + unpacked[index] = model + index++ + } + } + + return unpacked.cast() + } + } }