From b8f6bf841d462012dcd178a71229ee32de6cdbb4 Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 26 Jul 2024 16:32:05 -0700 Subject: [PATCH] Biased towards artistic control - Extend InstancerProvider to allow visuals to bias the render order of their instancers - Keep the old InstancerProvider#instancer method with a bias of 0 - Add an explanation of render order in InstancerProvider --- .../api/instance/InstancerProvider.java | 34 +++++++++++++++++-- .../engine_room/flywheel/api/model/Model.java | 8 +++-- .../flywheel/backend/engine/DrawManager.java | 4 +-- .../flywheel/backend/engine/EngineImpl.java | 4 +-- .../flywheel/backend/engine/InstancerKey.java | 2 +- .../backend/engine/InstancerProviderImpl.java | 4 +-- .../engine/embed/EmbeddedEnvironment.java | 4 +-- .../engine/indirect/IndirectCullingGroup.java | 9 +++-- .../backend/engine/indirect/IndirectDraw.java | 8 ++++- .../engine/indirect/IndirectDrawManager.java | 2 +- .../engine/instancing/InstancedDraw.java | 8 ++++- .../instancing/InstancedDrawManager.java | 2 +- .../instancing/InstancedRenderStage.java | 3 +- 13 files changed, 70 insertions(+), 22 deletions(-) diff --git a/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java b/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java index 8feb72ca3..a921ca61c 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/instance/InstancerProvider.java @@ -11,10 +11,38 @@ public interface InstancerProvider { *

Calling this method twice with the same arguments in the * same frame will return the same instancer.

* - *

It is not safe to store instancers between frames. Each + *

It is NOT safe to store instancers between frames. Each * time you need an instancer, you should call this method.

* - * @return An instancer for the given instance type rendering the given model. + *

Render Order

+ *

In general, you can assume all instances in the same instancer will be rendered in a single draw call. + * Backends are free to optimize the ordering of draw calls to a certain extent, but utilities are provided to let + * you control the order of draw calls + *

Mesh Order

+ *
For one, Meshes within a Model are guaranteed to render in the order they appear in their containing list. + * This lets you e.g. preserve (or break!) vanilla's chunk RenderType order guarantees or control which Meshes of + * your Model render over others. + *

Bias Order

+ *
The other method is via the {@code bias} parameter to this method. An instancer with a lower bias will have + * its instances draw BEFORE an instancer with a higher bias. This allows you to control the render order between + * your instances to e.g. create an "overlay" instance to selectively color or apply decals to another instance.

+ * + * @param type The instance type to parameterize your instances by. + * @param model The Model to instance. + * @param bias A weight to control render order between instancers. + * Instancers are rendered in ascending order by bias. + * @return An instancer. + */ + Instancer instancer(InstanceType type, Model model, int bias); + + /** + * Get an instancer with no bias for the given instance type rendering the given model with. + * + * @param type The instance type to parameterize your instances by. + * @param model The model to instance. + * @return An instancer with {@code bias == 0}. */ - Instancer instancer(InstanceType type, Model model); + default Instancer instancer(InstanceType type, Model model) { + return instancer(type, model, 0); + } } diff --git a/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java b/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java index 64b240d4b..9ad458193 100644 --- a/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java +++ b/common/src/api/java/dev/engine_room/flywheel/api/model/Model.java @@ -2,6 +2,10 @@ import java.util.List; +import dev.engine_room.flywheel.api.instance.InstanceType; + +import dev.engine_room.flywheel.api.instance.InstancerProvider; + import org.joml.Vector4fc; import dev.engine_room.flywheel.api.material.Material; @@ -12,8 +16,8 @@ public interface Model { * *

The contents of the returned list will be queried, but never modified.

* - *

Meshes will be rendered in the order they appear in this list, though - * no render order guarantees are made for meshes between different models.

+ *

Meshes will be rendered in the order they appear in this list. See + * {@link InstancerProvider#instancer(InstanceType, Model, int)} for a complete explanation

* * @return A list of meshes. */ diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java index 4f74b1fc1..cf07a4597 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/DrawManager.java @@ -36,8 +36,8 @@ public abstract class DrawManager> { protected final Queue> initializationQueue = new ConcurrentLinkedQueue<>(); @SuppressWarnings("unchecked") - public Instancer getInstancer(Environment environment, InstanceType type, Model model, VisualType visualType) { - return (Instancer) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType), this::createAndDeferInit); + public Instancer getInstancer(Environment environment, InstanceType type, Model model, VisualType visualType, int bias) { + return (Instancer) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType, bias), this::createAndDeferInit); } public void flush(LightStorage lightStorage) { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java index f3a644883..cdec9eece 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/EngineImpl.java @@ -109,8 +109,8 @@ public void delete() { lightStorage.delete(); } - public Instancer instancer(Environment environment, InstanceType type, Model model, VisualType visualType) { - return drawManager.getInstancer(environment, type, model, visualType); + public Instancer instancer(Environment environment, InstanceType type, Model model, VisualType visualType, int bias) { + return drawManager.getInstancer(environment, type, model, visualType, bias); } public EnvironmentStorage environmentStorage() { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java index ecdbc29e2..1910c9fbf 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerKey.java @@ -7,5 +7,5 @@ import dev.engine_room.flywheel.backend.engine.embed.Environment; public record InstancerKey(Environment environment, InstanceType type, Model model, - VisualType visualType) { + VisualType visualType, int bias) { } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java index 3a297b76a..064a5388a 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/InstancerProviderImpl.java @@ -10,7 +10,7 @@ public record InstancerProviderImpl(EngineImpl engine, VisualType visualType) implements InstancerProvider { @Override - public Instancer instancer(InstanceType type, Model model) { - return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType); + public Instancer instancer(InstanceType type, Model model, int bias) { + return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType, bias); } } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java index 42deacf6a..bea81b45c 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/embed/EmbeddedEnvironment.java @@ -39,9 +39,9 @@ public EmbeddedEnvironment(EngineImpl engine, VisualType visualType, @Nullable E instancerProvider = new InstancerProvider() { @Override - public Instancer instancer(InstanceType type, Model model) { + public Instancer instancer(InstanceType type, Model model, int bias) { // Kinda cursed usage of anonymous classes here, but it does the job. - return engine.instancer(EmbeddedEnvironment.this, type, model, visualType); + return engine.instancer(EmbeddedEnvironment.this, type, model, visualType, bias); } }; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 5cc8702c9..5dbcbe3c8 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -21,6 +21,7 @@ import dev.engine_room.flywheel.api.visualization.VisualType; import dev.engine_room.flywheel.backend.compile.ContextShader; import dev.engine_room.flywheel.backend.compile.IndirectPrograms; +import dev.engine_room.flywheel.backend.engine.InstancerKey; import dev.engine_room.flywheel.backend.engine.MaterialRenderState; import dev.engine_room.flywheel.backend.engine.MeshPool; import dev.engine_room.flywheel.backend.engine.embed.Environment; @@ -31,6 +32,7 @@ public class IndirectCullingGroup { private static final Comparator DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::visualType) + .thenComparing(IndirectDraw::bias) .thenComparing(IndirectDraw::indexOfMeshInModel) .thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR); @@ -174,16 +176,17 @@ public boolean hasVisualType(VisualType visualType) { return multiDraws.containsKey(visualType); } - public void add(IndirectInstancer instancer, Model model, VisualType visualType, MeshPool meshPool) { + public void add(IndirectInstancer instancer, InstancerKey key, MeshPool meshPool) { instancer.modelIndex = instancers.size(); instancers.add(instancer); - List meshes = model.meshes(); + List meshes = key.model() + .meshes(); for (int i = 0; i < meshes.size(); i++) { var entry = meshes.get(i); MeshPool.PooledMesh mesh = meshPool.alloc(entry.mesh()); - var draw = new IndirectDraw(instancer, entry.material(), mesh, visualType, i); + var draw = new IndirectDraw(instancer, entry.material(), mesh, key.visualType(), key.bias(), i); indirectDraws.add(draw); instancer.addDraw(draw); } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java index 2ad217f1a..093616337 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDraw.java @@ -13,6 +13,7 @@ public class IndirectDraw { private final Material material; private final MeshPool.PooledMesh mesh; private final VisualType visualType; + private final int bias; private final int indexOfMeshInModel; private final int materialVertexIndex; @@ -21,11 +22,12 @@ public class IndirectDraw { private final int packedMaterialProperties; private boolean deleted; - public IndirectDraw(IndirectInstancer instancer, Material material, MeshPool.PooledMesh mesh, VisualType visualType, int indexOfMeshInModel) { + public IndirectDraw(IndirectInstancer instancer, Material material, MeshPool.PooledMesh mesh, VisualType visualType, int bias, int indexOfMeshInModel) { this.instancer = instancer; this.material = material; this.mesh = mesh; this.visualType = visualType; + this.bias = bias; this.indexOfMeshInModel = indexOfMeshInModel; mesh.acquire(); @@ -52,6 +54,10 @@ public VisualType visualType() { return visualType; } + public int bias() { + return bias; + } + public int indexOfMeshInModel() { return indexOfMeshInModel; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java index 204da6bb2..2066554f0 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -63,7 +63,7 @@ protected IndirectInstancer create(InstancerKey key) protected void initialize(InstancerKey key, IndirectInstancer instancer) { var groupKey = new GroupKey<>(key.type(), key.environment()); var group = (IndirectCullingGroup) cullingGroups.computeIfAbsent(groupKey, t -> new IndirectCullingGroup<>(t.instanceType(), t.environment(), programs)); - group.add((IndirectInstancer) instancer, key.model(), key.visualType(), meshPool); + group.add((IndirectInstancer) instancer, key, meshPool); } public boolean hasVisualType(VisualType visualType) { diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java index 91b8d782c..d01e916e5 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDraw.java @@ -10,20 +10,26 @@ public class InstancedDraw { private final InstancedInstancer instancer; private final MeshPool.PooledMesh mesh; private final Material material; + private final int bias; private final int indexOfMeshInModel; private boolean deleted; - public InstancedDraw(InstancedInstancer instancer, MeshPool.PooledMesh mesh, GroupKey groupKey, Material material, int indexOfMeshInModel) { + public InstancedDraw(InstancedInstancer instancer, MeshPool.PooledMesh mesh, GroupKey groupKey, Material material, int bias, int indexOfMeshInModel) { this.instancer = instancer; this.mesh = mesh; this.groupKey = groupKey; this.material = material; + this.bias = bias; this.indexOfMeshInModel = indexOfMeshInModel; mesh.acquire(); } + public int bias() { + return bias; + } + public int indexOfMeshInModel() { return indexOfMeshInModel; } diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java index 549463c37..bc7610871 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedDrawManager.java @@ -142,7 +142,7 @@ protected void initialize(InstancerKey key, InstancedIns var mesh = meshPool.alloc(entry.mesh()); GroupKey groupKey = new GroupKey<>(key.type(), key.environment()); - InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), i); + InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), key.bias(), i); stage.put(groupKey, instancedDraw); instancer.addDrawCall(instancedDraw); diff --git a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java index 94dee9230..5e62e9718 100644 --- a/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java +++ b/common/src/backend/java/dev/engine_room/flywheel/backend/engine/instancing/InstancedRenderStage.java @@ -15,7 +15,8 @@ import dev.engine_room.flywheel.backend.gl.TextureBuffer; public class InstancedRenderStage { - private static final Comparator DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::indexOfMeshInModel) + private static final Comparator DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias) + .thenComparing(InstancedDraw::indexOfMeshInModel) .thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR); private final Map, DrawGroup> groups = new HashMap<>();