From ec08f86fedafe36b5f41c02487267272e4192687 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Wed, 26 Jul 2023 20:04:15 +0200 Subject: [PATCH] render pipeline tests --- .../world/WorldRendererPipelineTest.kt | 216 ++++++++++++++++++ .../renderer/renderer/RendererManager.kt | 24 +- 2 files changed, 232 insertions(+), 8 deletions(-) create mode 100644 src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/renderer/renderer/pipeline/world/WorldRendererPipelineTest.kt diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/renderer/renderer/pipeline/world/WorldRendererPipelineTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/renderer/renderer/pipeline/world/WorldRendererPipelineTest.kt new file mode 100644 index 000000000..7c1a8d176 --- /dev/null +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/renderer/renderer/pipeline/world/WorldRendererPipelineTest.kt @@ -0,0 +1,216 @@ +package de.bixilon.minosoft.gui.rendering.renderer.renderer.pipeline.world + +import de.bixilon.kotlinglm.vec2.Vec2i +import de.bixilon.kutil.cast.CastUtil.unsafeCast +import de.bixilon.kutil.exception.Broken +import de.bixilon.kutil.reflection.ReflectionUtil.forceSet +import de.bixilon.minosoft.data.registries.identified.Identified +import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft +import de.bixilon.minosoft.gui.rendering.RenderContext +import de.bixilon.minosoft.gui.rendering.font.manager.FontManager +import de.bixilon.minosoft.gui.rendering.font.types.dummy.DummyFontType +import de.bixilon.minosoft.gui.rendering.framebuffer.FramebufferManager +import de.bixilon.minosoft.gui.rendering.framebuffer.world.WorldFramebuffer +import de.bixilon.minosoft.gui.rendering.gui.atlas.CodeTexturePart +import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererManager +import de.bixilon.minosoft.gui.rendering.renderer.renderer.pipeline.RendererPipeline +import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.LayerSettings +import de.bixilon.minosoft.gui.rendering.renderer.renderer.world.WorldRenderer +import de.bixilon.minosoft.gui.rendering.system.base.PolygonModes +import de.bixilon.minosoft.gui.rendering.system.base.layer.OpaqueLayer +import de.bixilon.minosoft.gui.rendering.system.base.layer.RenderLayer +import de.bixilon.minosoft.gui.rendering.system.base.layer.TranslucentLayer +import de.bixilon.minosoft.gui.rendering.system.base.layer.TransparentLayer +import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings +import de.bixilon.minosoft.gui.rendering.system.dummy.DummyRenderSystem +import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTexture +import de.bixilon.minosoft.gui.rendering.system.dummy.texture.DummyTextureManager +import de.bixilon.minosoft.test.IT +import org.testng.Assert.assertEquals +import org.testng.annotations.Test + +@Test(groups = ["rendering"]) +class WorldRendererPipelineTest { + private val elements = WorldRendererPipeline::class.java.getDeclaredField("elements").apply { isAccessible = true } + val WorldRendererPipeline.elements: Array get() = this@WorldRendererPipelineTest.elements.get(this).unsafeCast() + + private val pipeline = RendererManager::class.java.getDeclaredField("pipeline").apply { isAccessible = true } + val RendererManager.pipeline: RendererPipeline get() = this@WorldRendererPipelineTest.pipeline.get(this).unsafeCast() + + + fun construct() { + manager() + renderer() + } + + fun `register single layer`() { + val manager = manager() + val renderer = manager.register(renderer()) + + renderer.layers.register(OpaqueLayer, null, renderer::run) + + manager.pipeline.world.rebuild() + + val elements = manager.pipeline.world.elements + assertEquals(elements.size, 1) + assertEquals(elements[0].layer, OpaqueLayer) + } + + fun `register 2 but same layer, check insertion order`() { + val manager = manager() + val renderer = manager.register(renderer()) + + renderer.layers.register(OpaqueLayer, null, renderer::run) + renderer.layers.register(layer("abc", OpaqueLayer.priority), null, renderer::run) + + manager.pipeline.world.rebuild() + + val elements = manager.pipeline.world.elements + assertEquals(elements.size, 2) + assertEquals(elements[0].layer, OpaqueLayer) + assertEquals(elements[1].layer.priority, OpaqueLayer.priority) + } + + fun `register 2 but different layers priority, inserted correct order`() { + val manager = manager() + val renderer = manager.register(renderer()) + + renderer.layers.register(OpaqueLayer, null, renderer::run) + renderer.layers.register(layer("abc", 10), null, renderer::run) + + manager.pipeline.world.rebuild() + + val elements = manager.pipeline.world.elements + assertEquals(elements.size, 2) + assertEquals(elements[0].layer, OpaqueLayer) + assertEquals(elements[1].layer.priority, 10) + } + + fun `register 2 but different layers priority, wrong insertion order`() { + val manager = manager() + val renderer = manager.register(renderer()) + + renderer.layers.register(layer("abc", 10), null, renderer::run) + renderer.layers.register(OpaqueLayer, null, renderer::run) + + manager.pipeline.world.rebuild() + + val elements = manager.pipeline.world.elements + assertEquals(elements.size, 2) + assertEquals(elements[0].layer, OpaqueLayer) + assertEquals(elements[1].layer.priority, 10) + } + + fun `register 4 but different layers`() { + val manager = manager() + val renderer = manager.register(renderer()) + + renderer.layers.register(layer("a", 10), null, renderer::run) + renderer.layers.register(OpaqueLayer, null, renderer::run) + renderer.layers.register(layer("b", -10), null, renderer::run) + renderer.layers.register(layer("c", -10), null, renderer::run) + + manager.pipeline.world.rebuild() + + val elements = manager.pipeline.world.elements + assertEquals(elements.size, 4) + assertEquals(elements[0].layer.unsafeCast().identifier.path, "b") + assertEquals(elements[1].layer.unsafeCast().identifier.path, "c") + assertEquals(elements[2].layer, OpaqueLayer) + assertEquals(elements[3].layer.unsafeCast().identifier.path, "a") + } + + fun `opaque transparent translucent`() { + val manager = manager() + val renderer = manager.register(renderer()) + + renderer.layers.register(TransparentLayer, null, renderer::run) + renderer.layers.register(TranslucentLayer, null, renderer::run) + renderer.layers.register(OpaqueLayer, null, renderer::run) + + manager.pipeline.world.rebuild() + + val elements = manager.pipeline.world.elements + assertEquals(elements.size, 3) + assertEquals(elements[0].layer, OpaqueLayer) + assertEquals(elements[1].layer, TransparentLayer) + assertEquals(elements[2].layer, TranslucentLayer) + } + + fun `different renderer`() { + val manager = manager() + + val first = manager.register(renderer()) + val second = manager.register(renderer()) + + first.layers.register(OpaqueLayer, null, first::run) + second.layers.register(OpaqueLayer, null, second::run) + second.layers.register(TranslucentLayer, null, second::run) + + manager.pipeline.world.rebuild() + + val elements = manager.pipeline.world.elements + assertEquals(elements.size, 3) + assertEquals(elements[0].layer, OpaqueLayer) + assertEquals(elements[1].layer, OpaqueLayer) + assertEquals(elements[2].layer, TranslucentLayer) + } + + fun `correct render draw order`() { + val manager = manager() + + val list: MutableList = mutableListOf() + + val first = manager.register(renderer()) + val second = manager.register(renderer()) + + first.layers.register(OpaqueLayer, null, { list += "a" }) + second.layers.register(OpaqueLayer, null, { list += "b" }) + second.layers.register(TranslucentLayer, null, { list += "c" }) + + manager.pipeline.world.rebuild() + + manager.pipeline.world.draw() + + assertEquals(list, listOf("a", "b", "c")) + } + + private fun context(): RenderContext { + val context = IT.OBJENESIS.newInstance(RenderContext::class.java) + context.font = FontManager(DummyFontType) + context::system.forceSet(DummyRenderSystem(context)) + context::textures.forceSet(DummyTextureManager(context)) + + val framebuffer = IT.OBJENESIS.newInstance(FramebufferManager::class.java) + framebuffer::world.forceSet(IT.OBJENESIS.newInstance(WorldFramebuffer::class.java).apply { this::polygonMode.forceSet(PolygonModes.FILL) }) + context::framebuffer.forceSet(framebuffer) + + context.textures::whiteTexture.forceSet(CodeTexturePart(DummyTexture(), size = Vec2i(16, 16))) + + return context + } + + + private fun manager(): RendererManager { + val context = context() + return RendererManager(context) + } + + private fun renderer() = object : WorldRenderer { + override val context get() = Broken() + override val framebuffer get() = null + override val renderSystem get() = Broken() + + override val layers = LayerSettings() + override fun registerLayers() = Unit + + + fun run(): Unit = TODO() + } + + private fun layer(name: String, priority: Int) = object : RenderLayer, Identified { + override val identifier = minosoft(name) + override val settings = RenderSettings.DEFAULT + override val priority = priority + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/renderer/renderer/RendererManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/renderer/renderer/RendererManager.kt index 8edd7a90c..aeb02675c 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/renderer/renderer/RendererManager.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/renderer/renderer/RendererManager.kt @@ -31,17 +31,25 @@ import de.bixilon.minosoft.util.logging.LogMessageType class RendererManager( val context: RenderContext, ) : Drawable, Iterable { + private val list: MutableList = mutableListOf() private val renderers: MutableMap, Renderer> = linkedMapOf() private val pipeline = RendererPipeline(this) private val connection = context.connection + fun register(renderer: T): T { + this.list += renderer + + return renderer + } + fun register(builder: RendererBuilder): T? { val renderer = builder.build(connection, context) ?: return null val previous = renderers.put(builder, renderer) if (previous != null) { Log.log(LogMessageType.RENDERING, LogLevels.WARN) { "Renderer $previous ($builder) got replaced by $renderer!" } } + list += renderer pipeline += renderer return renderer } @@ -58,14 +66,14 @@ class RendererManager( val inner = if (latch == null) SimpleLatch(0) else ParentLatch(0, latch) val worker = UnconditionalWorker() - for (renderer in renderers.values) { + for (renderer in list) { worker += { runnable.invoke(renderer, inner) } } worker.work(inner) } fun init(latch: AbstractLatch) { - for (renderer in renderers.values) { + for (renderer in list) { if (renderer !is WorldRenderer) continue renderer.registerLayers() } @@ -73,14 +81,14 @@ class RendererManager( runAsync(latch, Renderer::preAsyncInit) - for (renderer in renderers.values) { + for (renderer in list) { renderer.init(latch) } runAsync(latch, Renderer::asyncInit) } fun postInit(latch: AbstractLatch) { - for (renderer in renderers.values) { + for (renderer in list) { renderer.postInit(latch) } @@ -88,19 +96,19 @@ class RendererManager( } private fun prepare() { - for (renderer in renderers.values) { + for (renderer in list) { renderer.prePrepareDraw() } val latch = SimpleLatch(0) val worker = UnconditionalWorker() - for (renderer in renderers.values) { + for (renderer in list) { if (renderer !is AsyncRenderer) continue worker += UnconditionalTask(priority = ThreadPool.HIGHER) { renderer.prepareDrawAsync() } } worker.work(latch) - for (renderer in renderers.values) { + for (renderer in list) { renderer.postPrepareDraw() } } @@ -111,6 +119,6 @@ class RendererManager( } override fun iterator(): Iterator { - return renderers.values.iterator() + return list.iterator() } }