render pipeline tests

This commit is contained in:
Moritz Zwerger 2023-07-26 20:04:15 +02:00
parent 0cb1ab58f9
commit ec08f86fed
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
2 changed files with 232 additions and 8 deletions

View File

@ -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<PipelineElement> 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<Identified>().identifier.path, "b")
assertEquals(elements[1].layer.unsafeCast<Identified>().identifier.path, "c")
assertEquals(elements[2].layer, OpaqueLayer)
assertEquals(elements[3].layer.unsafeCast<Identified>().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<String> = 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
}
}

View File

@ -31,17 +31,25 @@ import de.bixilon.minosoft.util.logging.LogMessageType
class RendererManager( class RendererManager(
val context: RenderContext, val context: RenderContext,
) : Drawable, Iterable<Renderer> { ) : Drawable, Iterable<Renderer> {
private val list: MutableList<Renderer> = mutableListOf()
private val renderers: MutableMap<RendererBuilder<*>, Renderer> = linkedMapOf() private val renderers: MutableMap<RendererBuilder<*>, Renderer> = linkedMapOf()
private val pipeline = RendererPipeline(this) private val pipeline = RendererPipeline(this)
private val connection = context.connection private val connection = context.connection
fun <T : Renderer> register(renderer: T): T {
this.list += renderer
return renderer
}
fun <T : Renderer> register(builder: RendererBuilder<T>): T? { fun <T : Renderer> register(builder: RendererBuilder<T>): T? {
val renderer = builder.build(connection, context) ?: return null val renderer = builder.build(connection, context) ?: return null
val previous = renderers.put(builder, renderer) val previous = renderers.put(builder, renderer)
if (previous != null) { if (previous != null) {
Log.log(LogMessageType.RENDERING, LogLevels.WARN) { "Renderer $previous ($builder) got replaced by $renderer!" } Log.log(LogMessageType.RENDERING, LogLevels.WARN) { "Renderer $previous ($builder) got replaced by $renderer!" }
} }
list += renderer
pipeline += renderer pipeline += renderer
return renderer return renderer
} }
@ -58,14 +66,14 @@ class RendererManager(
val inner = if (latch == null) SimpleLatch(0) else ParentLatch(0, latch) val inner = if (latch == null) SimpleLatch(0) else ParentLatch(0, latch)
val worker = UnconditionalWorker() val worker = UnconditionalWorker()
for (renderer in renderers.values) { for (renderer in list) {
worker += { runnable.invoke(renderer, inner) } worker += { runnable.invoke(renderer, inner) }
} }
worker.work(inner) worker.work(inner)
} }
fun init(latch: AbstractLatch) { fun init(latch: AbstractLatch) {
for (renderer in renderers.values) { for (renderer in list) {
if (renderer !is WorldRenderer) continue if (renderer !is WorldRenderer) continue
renderer.registerLayers() renderer.registerLayers()
} }
@ -73,14 +81,14 @@ class RendererManager(
runAsync(latch, Renderer::preAsyncInit) runAsync(latch, Renderer::preAsyncInit)
for (renderer in renderers.values) { for (renderer in list) {
renderer.init(latch) renderer.init(latch)
} }
runAsync(latch, Renderer::asyncInit) runAsync(latch, Renderer::asyncInit)
} }
fun postInit(latch: AbstractLatch) { fun postInit(latch: AbstractLatch) {
for (renderer in renderers.values) { for (renderer in list) {
renderer.postInit(latch) renderer.postInit(latch)
} }
@ -88,19 +96,19 @@ class RendererManager(
} }
private fun prepare() { private fun prepare() {
for (renderer in renderers.values) { for (renderer in list) {
renderer.prePrepareDraw() renderer.prePrepareDraw()
} }
val latch = SimpleLatch(0) val latch = SimpleLatch(0)
val worker = UnconditionalWorker() val worker = UnconditionalWorker()
for (renderer in renderers.values) { for (renderer in list) {
if (renderer !is AsyncRenderer) continue if (renderer !is AsyncRenderer) continue
worker += UnconditionalTask(priority = ThreadPool.HIGHER) { renderer.prepareDrawAsync() } worker += UnconditionalTask(priority = ThreadPool.HIGHER) { renderer.prepareDrawAsync() }
} }
worker.work(latch) worker.work(latch)
for (renderer in renderers.values) { for (renderer in list) {
renderer.postPrepareDraw() renderer.postPrepareDraw()
} }
} }
@ -111,6 +119,6 @@ class RendererManager(
} }
override fun iterator(): Iterator<Renderer> { override fun iterator(): Iterator<Renderer> {
return renderers.values.iterator() return list.iterator()
} }
} }