Skip to content

Commit

Permalink
Biased towards artistic control
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
Jozsef Augusztiny committed Jul 26, 2024
1 parent eb2ba12 commit 8963238
Show file tree
Hide file tree
Showing 13 changed files with 70 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,38 @@ public interface InstancerProvider {
* <p>Calling this method twice with the same arguments in the
* same frame will return the same instancer.</p>
*
* <p>It is not safe to store instancers between frames. Each
* <p>It is NOT safe to store instancers between frames. Each
* time you need an instancer, you should call this method.</p>
*
* @return An instancer for the given instance type rendering the given model.
* <h2>Render Order</h2>
* <p>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
* <h4>Mesh Order</h4>
* <br>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.
* <h4>Bias Order</h4>
* <br>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.</p>
*
* @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.
*/
<I extends Instance> Instancer<I> instancer(InstanceType<I> 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}.
*/
<I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model);
default <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
return instancer(type, model, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,8 +16,8 @@ public interface Model {
*
* <p>The contents of the returned list will be queried, but never modified.</p>
*
* <p>Meshes will be rendered in the order they appear in this list, though
* no render order guarantees are made for meshes between different models.</p>
* <p>Meshes will be rendered in the order they appear in this list. See
* {@link InstancerProvider#instancer(InstanceType, Model, int)} for a complete explanation</p>
*
* @return A list of meshes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public abstract class DrawManager<N extends AbstractInstancer<?>> {
protected final Queue<UninitializedInstancer<N, ?>> initializationQueue = new ConcurrentLinkedQueue<>();

@SuppressWarnings("unchecked")
public <I extends Instance> Instancer<I> getInstancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType) {
return (Instancer<I>) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType), this::createAndDeferInit);
public <I extends Instance> Instancer<I> getInstancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType, int bias) {
return (Instancer<I>) instancers.computeIfAbsent(new InstancerKey<>(environment, type, model, visualType, bias), this::createAndDeferInit);
}

public void flush(LightStorage lightStorage) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ public void delete() {
lightStorage.delete();
}

public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType) {
return drawManager.getInstancer(environment, type, model, visualType);
public <I extends Instance> Instancer<I> instancer(Environment environment, InstanceType<I> type, Model model, VisualType visualType, int bias) {
return drawManager.getInstancer(environment, type, model, visualType, bias);
}

public EnvironmentStorage environmentStorage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
import dev.engine_room.flywheel.backend.engine.embed.Environment;

public record InstancerKey<I extends Instance>(Environment environment, InstanceType<I> type, Model model,
VisualType visualType) {
VisualType visualType, int bias) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

public record InstancerProviderImpl(EngineImpl engine, VisualType visualType) implements InstancerProvider {
@Override
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType);
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, int bias) {
return engine.instancer(GlobalEnvironment.INSTANCE, type, model, visualType, bias);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ public EmbeddedEnvironment(EngineImpl engine, VisualType visualType, @Nullable E

instancerProvider = new InstancerProvider() {
@Override
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model) {
public <I extends Instance> Instancer<I> instancer(InstanceType<I> 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);
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,6 +32,7 @@

public class IndirectCullingGroup<I extends Instance> {
private static final Comparator<IndirectDraw> DRAW_COMPARATOR = Comparator.comparing(IndirectDraw::visualType)
.thenComparing(IndirectDraw::bias)
.thenComparing(IndirectDraw::indexOfMeshInModel)
.thenComparing(IndirectDraw::material, MaterialRenderState.COMPARATOR);

Expand Down Expand Up @@ -174,16 +176,17 @@ public boolean hasVisualType(VisualType visualType) {
return multiDraws.containsKey(visualType);
}

public void add(IndirectInstancer<I> instancer, Model model, VisualType visualType, MeshPool meshPool) {
public void add(IndirectInstancer<I> instancer, InstancerKey<I> key, MeshPool meshPool) {
instancer.modelIndex = instancers.size();
instancers.add(instancer);

List<Model.ConfiguredMesh> meshes = model.meshes();
List<Model.ConfiguredMesh> 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -52,6 +54,10 @@ public VisualType visualType() {
return visualType;
}

public int bias() {
return bias;
}

public int indexOfMeshInModel() {
return indexOfMeshInModel;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ protected <I extends Instance> IndirectInstancer<?> create(InstancerKey<I> key)
protected <I extends Instance> void initialize(InstancerKey<I> key, IndirectInstancer<?> instancer) {
var groupKey = new GroupKey<>(key.type(), key.environment());
var group = (IndirectCullingGroup<I>) cullingGroups.computeIfAbsent(groupKey, t -> new IndirectCullingGroup<>(t.instanceType(), t.environment(), programs));
group.add((IndirectInstancer<I>) instancer, key.model(), key.visualType(), meshPool);
group.add((IndirectInstancer<I>) instancer, key, meshPool);
}

public boolean hasVisualType(VisualType visualType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ protected <I extends Instance> void initialize(InstancerKey<I> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
import dev.engine_room.flywheel.backend.gl.TextureBuffer;

public class InstancedRenderStage {
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::indexOfMeshInModel)
private static final Comparator<InstancedDraw> DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::bias)
.thenComparing(InstancedDraw::indexOfMeshInModel)
.thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR);

private final Map<GroupKey<?>, DrawGroup> groups = new HashMap<>();
Expand Down

0 comments on commit 8963238

Please sign in to comment.