diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java index ae0969004..78f5aa65e 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/DrawManager.java @@ -1,6 +1,7 @@ package com.jozufozu.flywheel.backend.engine; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -13,6 +14,11 @@ import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.api.instance.Instancer; import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.backend.engine.embed.Environment; +import com.jozufozu.flywheel.lib.util.Pair; + +import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.client.resources.model.ModelBakery; public abstract class DrawManager> { /** @@ -93,4 +99,36 @@ public abstract class DrawManager> { return false; } + + protected static > Map, Int2ObjectMap>>> doCrumblingSort(Class clazz, List crumblingBlocks) { + Map, Int2ObjectMap>>> byType = new HashMap<>(); + for (Engine.CrumblingBlock block : crumblingBlocks) { + int progress = block.progress(); + + if (progress < 0 || progress >= ModelBakery.DESTROY_TYPES.size()) { + continue; + } + + for (Instance instance : block.instances()) { + // Filter out instances that weren't created by this engine. + // If all is well, we probably shouldn't take the `continue` + // branches but better to do checked casts. + if (!(instance.handle() instanceof InstanceHandleImpl impl)) { + continue; + } + + AbstractInstancer abstractInstancer = impl.instancer; + if (!clazz.isInstance(abstractInstancer)) { + continue; + } + + var instancer = clazz.cast(abstractInstancer); + + byType.computeIfAbsent(new GroupKey<>(instancer.type, instancer.environment), $ -> new Int2ObjectArrayMap<>()) + .computeIfAbsent(progress, $ -> new ArrayList<>()) + .add(Pair.of(instancer, impl)); + } + } + return byType; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/GroupKey.java b/src/main/java/com/jozufozu/flywheel/backend/engine/GroupKey.java new file mode 100644 index 000000000..a978c59b8 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/GroupKey.java @@ -0,0 +1,8 @@ +package com.jozufozu.flywheel.backend.engine; + +import com.jozufozu.flywheel.api.instance.Instance; +import com.jozufozu.flywheel.api.instance.InstanceType; +import com.jozufozu.flywheel.backend.engine.embed.Environment; + +public record GroupKey(InstanceType instanceType, Environment environment) { +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java index 9c2347d8b..e705771bb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectCullingGroup.java @@ -42,7 +42,7 @@ public class IndirectCullingGroup { private final Environment environment; private final long instanceStride; private final IndirectBuffers buffers; - private final List> instancers = new ArrayList<>(); + private final List> instancers = new ArrayList<>(); private final List indirectDraws = new ArrayList<>(); private final Map> multiDraws = new EnumMap<>(RenderStage.class); @@ -216,7 +216,7 @@ public class IndirectCullingGroup { } } - public GlProgram bindWithContextShader(ContextShader override) { + public void bindWithContextShader(ContextShader override) { var program = programs.getIndirectProgram(instanceType, override); program.bind(); @@ -227,8 +227,6 @@ public class IndirectCullingGroup { var flwBaseDraw = drawProgram.getUniformLocation("_flw_baseDraw"); glUniform1ui(flwBaseDraw, 0); - - return program; } private void drawBarrier() { diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java index e97f99a96..5a6cf0f85 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectDrawManager.java @@ -6,7 +6,6 @@ import static org.lwjgl.opengl.GL30.glBindBufferRange; import static org.lwjgl.opengl.GL40.glDrawElementsIndirect; import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -14,18 +13,16 @@ import java.util.Map; import com.jozufozu.flywheel.api.backend.Engine; import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.instance.Instance; -import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.backend.Samplers; import com.jozufozu.flywheel.backend.compile.ContextShader; import com.jozufozu.flywheel.backend.compile.IndirectPrograms; import com.jozufozu.flywheel.backend.engine.CommonCrumbling; import com.jozufozu.flywheel.backend.engine.DrawManager; -import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl; +import com.jozufozu.flywheel.backend.engine.GroupKey; import com.jozufozu.flywheel.backend.engine.InstancerKey; import com.jozufozu.flywheel.backend.engine.MaterialRenderState; import com.jozufozu.flywheel.backend.engine.MeshPool; import com.jozufozu.flywheel.backend.engine.TextureBinder; -import com.jozufozu.flywheel.backend.engine.embed.Environment; import com.jozufozu.flywheel.backend.engine.uniform.Uniforms; import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; @@ -33,10 +30,7 @@ import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.lib.material.SimpleMaterial; import com.jozufozu.flywheel.lib.memory.MemoryBlock; -import com.jozufozu.flywheel.lib.util.Pair; -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.client.resources.model.ModelBakery; public class IndirectDrawManager extends DrawManager> { @@ -68,7 +62,7 @@ public class IndirectDrawManager extends DrawManager> { @Override 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.type, t.environment, programs)); + var group = (IndirectCullingGroup) cullingGroups.computeIfAbsent(groupKey, t -> new IndirectCullingGroup<>(t.instanceType(), t.environment(), programs)); group.add((IndirectInstancer) instancer, key.model(), key.stage(), meshPool); } @@ -148,7 +142,7 @@ public class IndirectDrawManager extends DrawManager> { } public void renderCrumbling(List crumblingBlocks) { - var byType = doCrumblingSort(crumblingBlocks); + var byType = doCrumblingSort(IndirectInstancer.class, crumblingBlocks); if (byType.isEmpty()) { return; @@ -168,15 +162,13 @@ public class IndirectDrawManager extends DrawManager> { GlBufferType.DRAW_INDIRECT_BUFFER.bind(crumblingDrawBuffer.handle()); glBindBufferRange(GL_SHADER_STORAGE_BUFFER, IndirectBuffers.DRAW_INDEX, crumblingDrawBuffer.handle(), 0, IndirectBuffers.DRAW_COMMAND_STRIDE); - for (var instanceTypeEntry : byType.entrySet()) { - var byProgress = instanceTypeEntry.getValue(); + for (var groupEntry : byType.entrySet()) { + var byProgress = groupEntry.getValue(); // Set up the crumbling program buffers. Nothing changes here between draws. - var program = cullingGroups.get(instanceTypeEntry.getKey()) + cullingGroups.get(groupEntry.getKey()) .bindWithContextShader(ContextShader.CRUMBLING); - program.setSamplerBinding("crumblingTex", Samplers.CRUMBLING); - for (var progressEntry : byProgress.int2ObjectEntrySet()) { Samplers.CRUMBLING.makeActive(); TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey())); @@ -186,7 +178,6 @@ public class IndirectDrawManager extends DrawManager> { int instanceIndex = instanceHandlePair.second().index; for (IndirectDraw draw : instancer.draws()) { - // Transform the material to be suited for crumbling. CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material()); @@ -210,35 +201,4 @@ public class IndirectDrawManager extends DrawManager> { block.free(); } } - - private static Map, Int2ObjectMap, InstanceHandleImpl>>>> doCrumblingSort(List crumblingBlocks) { - Map, Int2ObjectMap, InstanceHandleImpl>>>> byType = new HashMap<>(); - for (Engine.CrumblingBlock block : crumblingBlocks) { - int progress = block.progress(); - - if (progress < 0 || progress >= ModelBakery.DESTROY_TYPES.size()) { - continue; - } - - for (Instance instance : block.instances()) { - // Filter out instances that weren't created by this engine. - // If all is well, we probably shouldn't take the `continue` - // branches but better to do checked casts. - if (!(instance.handle() instanceof InstanceHandleImpl impl)) { - continue; - } - if (!(impl.instancer instanceof IndirectInstancer instancer)) { - continue; - } - - byType.computeIfAbsent(new GroupKey<>(instancer.type, instancer.environment), $ -> new Int2ObjectArrayMap<>()) - .computeIfAbsent(progress, $ -> new ArrayList<>()) - .add(Pair.of(instancer, impl)); - } - } - return byType; - } - - public record GroupKey(InstanceType type, Environment environment) { - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/DrawCall.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDraw.java similarity index 62% rename from src/main/java/com/jozufozu/flywheel/backend/engine/instancing/DrawCall.java rename to src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDraw.java index 0cafc89db..955144cb8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/DrawCall.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDraw.java @@ -1,24 +1,38 @@ package com.jozufozu.flywheel.backend.engine.instancing; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.backend.engine.GroupKey; import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl; import com.jozufozu.flywheel.backend.engine.MeshPool; import com.jozufozu.flywheel.backend.gl.TextureBuffer; -public class DrawCall { - public final ShaderState shaderState; +public class InstancedDraw { + public final GroupKey groupKey; private final InstancedInstancer instancer; private final MeshPool.PooledMesh mesh; + private final Material material; + private final int indexOfMeshInModel; private boolean deleted; - public DrawCall(InstancedInstancer instancer, MeshPool.PooledMesh mesh, ShaderState shaderState) { + public InstancedDraw(InstancedInstancer instancer, MeshPool.PooledMesh mesh, GroupKey groupKey, Material material, int indexOfMeshInModel) { this.instancer = instancer; this.mesh = mesh; - this.shaderState = shaderState; + this.groupKey = groupKey; + this.material = material; + this.indexOfMeshInModel = indexOfMeshInModel; mesh.acquire(); } + public int indexOfMeshInModel() { + return indexOfMeshInModel; + } + + public Material material() { + return material; + } + public boolean deleted() { return deleted; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java index 44d7b9f35..38f651999 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedDrawManager.java @@ -1,19 +1,11 @@ package com.jozufozu.flywheel.backend.engine.instancing; -import java.util.ArrayList; -import java.util.Collection; import java.util.EnumMap; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.function.Consumer; import org.lwjgl.opengl.GL32; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; import com.jozufozu.flywheel.api.backend.Engine; import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.instance.Instance; @@ -24,7 +16,7 @@ import com.jozufozu.flywheel.backend.compile.ContextShader; import com.jozufozu.flywheel.backend.compile.InstancingPrograms; import com.jozufozu.flywheel.backend.engine.CommonCrumbling; import com.jozufozu.flywheel.backend.engine.DrawManager; -import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl; +import com.jozufozu.flywheel.backend.engine.GroupKey; import com.jozufozu.flywheel.backend.engine.InstancerKey; import com.jozufozu.flywheel.backend.engine.MaterialEncoder; import com.jozufozu.flywheel.backend.engine.MaterialRenderState; @@ -37,15 +29,13 @@ import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.shader.GlProgram; import com.jozufozu.flywheel.lib.material.SimpleMaterial; -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import net.minecraft.client.resources.model.ModelBakery; public class InstancedDrawManager extends DrawManager> { /** * The set of draw calls to make in each {@link RenderStage}. */ - private final Map drawSets = new EnumMap<>(RenderStage.class); + private final Map stages = new EnumMap<>(RenderStage.class); private final InstancingPrograms programs; /** * A map of vertex types to their mesh pools. @@ -65,6 +55,7 @@ public class InstancedDrawManager extends DrawManager> { meshPool.bind(vao); } + @Override public void flush() { super.flush(); @@ -81,33 +72,42 @@ public class InstancedDrawManager extends DrawManager> { } }); - for (DrawSet drawSet : drawSets.values()) { + for (InstancedRenderStage instancedRenderStage : stages.values()) { // Remove the draw calls for any instancers we deleted. - drawSet.prune(); + instancedRenderStage.flush(); } meshPool.flush(); } + @Override public void renderStage(RenderStage stage) { - var drawSet = drawSets.getOrDefault(stage, DrawSet.EMPTY); + var drawSet = stages.get(stage); - if (drawSet.isEmpty()) { + if (drawSet == null || drawSet.isEmpty()) { return; } try (var state = GlStateTracker.getRestoreState()) { - render(drawSet); + Uniforms.bindForDraw(); + vao.bindForDraw(); + TextureBinder.bindLightAndOverlay(); + + drawSet.draw(instanceTexture, programs); + + MaterialRenderState.reset(); + TextureBinder.resetLightAndOverlay(); } } + @Override public void delete() { instancers.values() .forEach(InstancedInstancer::delete); - drawSets.values() - .forEach(DrawSet::delete); - drawSets.clear(); + stages.values() + .forEach(InstancedRenderStage::delete); + stages.clear(); meshPool.delete(); instanceTexture.delete(); @@ -117,42 +117,6 @@ public class InstancedDrawManager extends DrawManager> { super.delete(); } - private void render(InstancedDrawManager.DrawSet drawSet) { - Uniforms.bindForDraw(); - vao.bindForDraw(); - TextureBinder.bindLightAndOverlay(); - - for (var entry : drawSet) { - var shader = entry.getKey(); - var drawCalls = entry.getValue(); - - if (drawCalls.isEmpty()) { - continue; - } - - var environment = shader.environment(); - var material = shader.material(); - - var program = programs.get(shader.instanceType(), environment.contextShader()); - program.bind(); - - environment.setupDraw(program); - - uploadMaterialUniform(program, material); - - MaterialRenderState.setup(material); - - Samplers.INSTANCE_BUFFER.makeActive(); - - for (var drawCall : drawCalls) { - drawCall.render(instanceTexture); - } - } - - MaterialRenderState.reset(); - TextureBinder.resetLightAndOverlay(); - } - @Override protected InstancedInstancer create(InstancerKey key) { return new InstancedInstancer<>(key.type(), key.environment()); @@ -162,26 +126,28 @@ public class InstancedDrawManager extends DrawManager> { protected void initialize(InstancerKey key, InstancedInstancer instancer) { instancer.init(); - DrawSet drawSet = drawSets.computeIfAbsent(key.stage(), DrawSet::new); + InstancedRenderStage instancedRenderStage = stages.computeIfAbsent(key.stage(), $ -> new InstancedRenderStage()); var meshes = key.model() .meshes(); - for (var entry : meshes) { + for (int i = 0; i < meshes.size(); i++) { + var entry = meshes.get(i); var mesh = meshPool.alloc(entry.mesh()); - ShaderState shaderState = new ShaderState(entry.material(), key.type(), key.environment()); - DrawCall drawCall = new DrawCall(instancer, mesh, shaderState); + GroupKey groupKey = new GroupKey<>(key.type(), key.environment()); + InstancedDraw instancedDraw = new InstancedDraw(instancer, mesh, groupKey, entry.material(), i); - drawSet.put(shaderState, drawCall); - instancer.addDrawCall(drawCall); + instancedRenderStage.put(groupKey, instancedDraw); + instancer.addDrawCall(instancedDraw); } } + @Override public void renderCrumbling(List crumblingBlocks) { // Sort draw calls into buckets, so we don't have to do as many shader binds. - var byShaderState = doCrumblingSort(crumblingBlocks); + var byType = doCrumblingSort(InstancedInstancer.class, crumblingBlocks); - if (byShaderState.isEmpty()) { + if (byType.isEmpty()) { return; } @@ -192,38 +158,32 @@ public class InstancedDrawManager extends DrawManager> { vao.bindForDraw(); TextureBinder.bindLightAndOverlay(); - for (var shaderStateEntry : byShaderState.entrySet()) { - var byProgress = shaderStateEntry.getValue(); + for (var groupEntry : byType.entrySet()) { + var byProgress = groupEntry.getValue(); - if (byProgress.isEmpty()) { - continue; - } - - ShaderState shader = shaderStateEntry.getKey(); - - CommonCrumbling.applyCrumblingProperties(crumblingMaterial, shader.material()); + GroupKey shader = groupEntry.getKey(); var program = programs.get(shader.instanceType(), ContextShader.CRUMBLING); program.bind(); - uploadMaterialUniform(program, crumblingMaterial); - - MaterialRenderState.setup(crumblingMaterial); - for (var progressEntry : byProgress.int2ObjectEntrySet()) { - var drawCalls = progressEntry.getValue(); - - if (drawCalls.isEmpty()) { - continue; - } - Samplers.CRUMBLING.makeActive(); TextureBinder.bind(ModelBakery.BREAKING_LOCATIONS.get(progressEntry.getIntKey())); - Samplers.INSTANCE_BUFFER.makeActive(); + for (var instanceHandlePair : progressEntry.getValue()) { + InstancedInstancer instancer = instanceHandlePair.first(); + var handle = instanceHandlePair.second(); - for (Consumer drawCall : drawCalls) { - drawCall.accept(instanceTexture); + for (InstancedDraw draw : instancer.draws()) { + CommonCrumbling.applyCrumblingProperties(crumblingMaterial, draw.material()); + uploadMaterialUniform(program, crumblingMaterial); + + MaterialRenderState.setup(crumblingMaterial); + + Samplers.INSTANCE_BUFFER.makeActive(); + + draw.renderOne(instanceTexture, handle); + } } } } @@ -233,38 +193,6 @@ public class InstancedDrawManager extends DrawManager> { } } - private static Map>>> doCrumblingSort(List instances) { - Map>>> out = new HashMap<>(); - - for (Engine.CrumblingBlock triple : instances) { - int progress = triple.progress(); - - if (progress < 0 || progress >= ModelBakery.DESTROY_TYPES.size()) { - continue; - } - - for (Instance instance : triple.instances()) { - // Filter out instances that weren't created by this engine. - // If all is well, we probably shouldn't take the `continue` - // branches but better to do checked casts. - if (!(instance.handle() instanceof InstanceHandleImpl impl)) { - continue; - } - if (!(impl.instancer instanceof InstancedInstancer instancer)) { - continue; - } - - for (DrawCall draw : instancer.drawCalls()) { - out.computeIfAbsent(draw.shaderState, $ -> new Int2ObjectArrayMap<>()) - .computeIfAbsent(progress, $ -> new ArrayList<>()) - .add(buf -> draw.renderOne(buf, impl)); - } - } - } - - return out; - } - public static void uploadMaterialUniform(GlProgram program, Material material) { int uniformLocation = program.getUniformLocation("_flw_packedMaterial"); int vertexIndex = ShaderIndices.getVertexShaderIndex(material.shaders()); @@ -273,44 +201,4 @@ public class InstancedDrawManager extends DrawManager> { int packedMaterialProperties = MaterialEncoder.packProperties(material); GL32.glUniform4ui(uniformLocation, vertexIndex, fragmentIndex, packedFogAndCutout, packedMaterialProperties); } - - public static class DrawSet implements Iterable>> { - public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of()); - - private final ListMultimap drawCalls; - - public DrawSet(RenderStage renderStage) { - drawCalls = ArrayListMultimap.create(); - } - - public DrawSet(ListMultimap drawCalls) { - this.drawCalls = drawCalls; - } - - private void delete() { - drawCalls.values() - .forEach(DrawCall::delete); - drawCalls.clear(); - } - - public void put(ShaderState shaderState, DrawCall drawCall) { - drawCalls.put(shaderState, drawCall); - } - - public boolean isEmpty() { - return drawCalls.isEmpty(); - } - - @Override - public Iterator>> iterator() { - return drawCalls.asMap() - .entrySet() - .iterator(); - } - - public void prune() { - drawCalls.values() - .removeIf(DrawCall::deleted); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java index da7481f60..ae148a137 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedInstancer.java @@ -23,7 +23,7 @@ public class InstancedInstancer extends AbstractInstancer @Nullable private GlBuffer vbo; - private final List drawCalls = new ArrayList<>(); + private final List draws = new ArrayList<>(); public InstancedInstancer(InstanceType type, Environment environment) { super(type, environment); @@ -33,6 +33,10 @@ public class InstancedInstancer extends AbstractInstancer writer = type.writer(); } + public List draws() { + return draws; + } + public void init() { if (vbo != null) { return; @@ -115,17 +119,13 @@ public class InstancedInstancer extends AbstractInstancer vbo.delete(); vbo = null; - for (DrawCall drawCall : drawCalls) { - drawCall.delete(); + for (InstancedDraw instancedDraw : draws) { + instancedDraw.delete(); } } - public void addDrawCall(DrawCall drawCall) { - drawCalls.add(drawCall); - } - - public List drawCalls() { - return drawCalls; + public void addDrawCall(InstancedDraw instancedDraw) { + draws.add(instancedDraw); } public void bind(TextureBuffer buffer) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedRenderStage.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedRenderStage.java new file mode 100644 index 000000000..a8abc66c5 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancedRenderStage.java @@ -0,0 +1,102 @@ +package com.jozufozu.flywheel.backend.engine.instancing; + +import static com.jozufozu.flywheel.backend.engine.instancing.InstancedDrawManager.uploadMaterialUniform; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.jozufozu.flywheel.backend.Samplers; +import com.jozufozu.flywheel.backend.compile.InstancingPrograms; +import com.jozufozu.flywheel.backend.engine.GroupKey; +import com.jozufozu.flywheel.backend.engine.MaterialRenderState; +import com.jozufozu.flywheel.backend.gl.TextureBuffer; + +public class InstancedRenderStage { + private static final Comparator DRAW_COMPARATOR = Comparator.comparing(InstancedDraw::indexOfMeshInModel) + .thenComparing(InstancedDraw::material, MaterialRenderState.COMPARATOR); + + private final Map, DrawGroup> groups = new HashMap<>(); + + public InstancedRenderStage() { + } + + public void delete() { + groups.values() + .forEach(DrawGroup::delete); + groups.clear(); + } + + public void put(GroupKey groupKey, InstancedDraw instancedDraw) { + groups.computeIfAbsent(groupKey, $ -> new DrawGroup()) + .put(instancedDraw); + } + + public boolean isEmpty() { + return groups.isEmpty(); + } + + public void flush() { + groups.values() + .forEach(DrawGroup::flush); + + groups.values() + .removeIf(DrawGroup::isEmpty); + } + + public void draw(TextureBuffer instanceTexture, InstancingPrograms programs) { + for (var entry : groups.entrySet()) { + var shader = entry.getKey(); + var drawCalls = entry.getValue(); + + var environment = shader.environment(); + + var program = programs.get(shader.instanceType(), environment.contextShader()); + program.bind(); + + environment.setupDraw(program); + + for (var drawCall : drawCalls.draws) { + var material = drawCall.material(); + + uploadMaterialUniform(program, material); + + MaterialRenderState.setup(material); + + Samplers.INSTANCE_BUFFER.makeActive(); + + drawCall.render(instanceTexture); + } + } + } + + public static class DrawGroup { + private final List draws = new ArrayList<>(); + private boolean needSort = false; + + public void put(InstancedDraw instancedDraw) { + draws.add(instancedDraw); + needSort = true; + } + + public void delete() { + draws.forEach(InstancedDraw::delete); + draws.clear(); + } + + public void flush() { + needSort |= draws.removeIf(InstancedDraw::deleted); + + if (needSort) { + draws.sort(DRAW_COMPARATOR); + needSort = false; + } + } + + public boolean isEmpty() { + return draws.isEmpty(); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/ShaderState.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/ShaderState.java deleted file mode 100644 index 185f1688e..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/ShaderState.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.jozufozu.flywheel.backend.engine.instancing; - -import com.jozufozu.flywheel.api.instance.InstanceType; -import com.jozufozu.flywheel.api.material.Material; -import com.jozufozu.flywheel.backend.engine.embed.Environment; - -public record ShaderState(Material material, InstanceType instanceType, Environment environment) { -}