From 581bec1e7a71f180b0e2dc717d504aea7006662d Mon Sep 17 00:00:00 2001 From: Jozufozu Date: Fri, 24 Nov 2023 23:10:27 -0800 Subject: [PATCH] Reduced to ashes - renderCrumblingInstances accepts a list. - Implement crumbling for InstancingEngine. - It's ugly. - Track what draw calls belong with what instancers. - DrawCalls lazily create a second VAO for one-off rendering. - Bind instance vbo with offset to scratch VAO to emulate a draw with baseInstance. - Ignore discard predicate in crumbling context. - SimpleContext takes a Consumer to set sampler bindings. - Fix debugCrumbling command. - Compile shaders against all contexts. Side note: not sure if Context is the right place for crumbling. It feels like it should be a material, but I don't know how that would work. --- .../jozufozu/flywheel/api/backend/Engine.java | 11 ++++- .../flywheel/backend/compile/FlwPrograms.java | 10 ++-- .../backend/engine/AbstractInstancer.java | 2 +- .../backend/engine/InstanceHandleImpl.java | 6 +-- .../engine/batching/BatchingEngine.java | 2 +- .../engine/indirect/IndirectEngine.java | 4 +- .../backend/engine/instancing/DrawCall.java | 49 ++++++++++++++++--- .../instancing/InstancedDrawManager.java | 36 ++++++++++++-- .../engine/instancing/InstancedInstancer.java | 7 ++- .../engine/instancing/InstancingEngine.java | 42 +++++++++++++++- .../jozufozu/flywheel/config/FlwCommands.java | 3 +- .../VisualizationManagerImpl.java | 9 ++-- .../flywheel/lib/context/Contexts.java | 17 ++++++- .../flywheel/lib/context/SimpleContext.java | 10 ++-- .../flywheel/flywheel/context/crumbling.frag | 3 +- 15 files changed, 168 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/jozufozu/flywheel/api/backend/Engine.java b/src/main/java/com/jozufozu/flywheel/api/backend/Engine.java index d17d54474..78d3202c3 100644 --- a/src/main/java/com/jozufozu/flywheel/api/backend/Engine.java +++ b/src/main/java/com/jozufozu/flywheel/api/backend/Engine.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.api.backend; +import java.util.List; + import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.instance.Instance; @@ -25,7 +27,14 @@ public interface Engine extends InstancerProvider { */ void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage); - void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress); + /** + * Render the given instances as a crumbling overlay. + * @param taskExecutor The task executor running the frame plan. + * @param context The render context for this frame. + * @param instances The instances to render. + * @param progress The progress of the crumbling animation, i.e. which texture to use. + */ + void renderCrumblingInstances(TaskExecutor taskExecutor, RenderContext context, List instances, int progress); /** * Maintain the render origin to be within a certain distance from the camera in all directions, diff --git a/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java b/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java index c42f4a254..97924e208 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java +++ b/src/main/java/com/jozufozu/flywheel/backend/compile/FlwPrograms.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.compile; import com.google.common.collect.ImmutableList; import com.jozufozu.flywheel.Flywheel; +import com.jozufozu.flywheel.api.context.Context; import com.jozufozu.flywheel.api.instance.InstanceType; import com.jozufozu.flywheel.api.uniform.ShaderUniforms; import com.jozufozu.flywheel.api.vertex.VertexType; @@ -76,9 +77,12 @@ public class FlwPrograms { private static ImmutableList createPipelineKeys() { ImmutableList.Builder builder = ImmutableList.builder(); - for (InstanceType instanceType : InstanceType.REGISTRY) { - for (VertexType vertexType : VertexType.REGISTRY) { - builder.add(new PipelineProgramKey(vertexType, instanceType, Contexts.WORLD)); + // TODO: ubershader'd contexts? + for (Context context : Context.REGISTRY) { + for (InstanceType instanceType : InstanceType.REGISTRY) { + for (VertexType vertexType : VertexType.REGISTRY) { + builder.add(new PipelineProgramKey(vertexType, instanceType, context)); + } } } return builder.build(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java index 4d4679c2b..6e294e824 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/AbstractInstancer.java @@ -81,7 +81,7 @@ public abstract class AbstractInstancer implements Instancer handles.set(j, handle); instances.set(j, instance); - handle.setIndex(j); + handle.index = j; changed.set(j); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/InstanceHandleImpl.java b/src/main/java/com/jozufozu/flywheel/backend/engine/InstanceHandleImpl.java index 9e22fc28c..be214c39a 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/InstanceHandleImpl.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/InstanceHandleImpl.java @@ -4,7 +4,7 @@ import com.jozufozu.flywheel.api.instance.InstanceHandle; public class InstanceHandleImpl implements InstanceHandle { public final AbstractInstancer instancer; - private int index; + public int index; public InstanceHandleImpl(AbstractInstancer instancer, int index) { this.instancer = instancer; @@ -21,10 +21,6 @@ public class InstanceHandleImpl implements InstanceHandle { instancer.notifyRemoval(index); } - public void setIndex(int i) { - index = i; - } - public void clear() { index = -1; } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java index 3ac290390..ec707bf33 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/batching/BatchingEngine.java @@ -94,7 +94,7 @@ public class BatchingEngine extends AbstractEngine implements SimplyComposedPlan } @Override - public void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress) { + public void renderCrumblingInstances(TaskExecutor taskExecutor, RenderContext context, List instances, int progress) { // TODO: implement } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java index f1a7077d9..2c66dee57 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/indirect/IndirectEngine.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.backend.engine.indirect; +import java.util.List; + import org.lwjgl.opengl.GL32; import com.jozufozu.flywheel.api.event.RenderContext; @@ -69,7 +71,7 @@ public class IndirectEngine extends AbstractEngine { } @Override - public void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress) { + public void renderCrumblingInstances(TaskExecutor taskExecutor, RenderContext context, List instances, int progress) { // TODO: implement } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/DrawCall.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/DrawCall.java index 2e5905104..f1af864b2 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/DrawCall.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/DrawCall.java @@ -3,15 +3,18 @@ package com.jozufozu.flywheel.backend.engine.instancing; import com.jozufozu.flywheel.gl.array.GlVertexArray; public class DrawCall { + public final ShaderState shaderState; private final InstancedInstancer instancer; private final InstancedMeshPool.BufferedMesh mesh; private final int meshAttributes; private GlVertexArray vao; + private GlVertexArray vaoScratch; - public DrawCall(InstancedInstancer instancer, InstancedMeshPool.BufferedMesh mesh) { + public DrawCall(InstancedInstancer instancer, InstancedMeshPool.BufferedMesh mesh, ShaderState shaderState) { this.instancer = instancer; this.mesh = mesh; + this.shaderState = shaderState; meshAttributes = this.mesh.getAttributeCount(); vao = GlVertexArray.create(); @@ -22,14 +25,14 @@ public class DrawCall { } public void render() { - if (isInvalid()) { + if (isInvalid() || mesh.isEmpty()) { return; } instancer.update(); int instanceCount = instancer.getInstanceCount(); - if (instanceCount <= 0 || mesh.isEmpty()) { + if (instanceCount <= 0) { return; } @@ -41,12 +44,44 @@ public class DrawCall { mesh.draw(instanceCount); } - public void delete() { - if (vao == null) { + public void renderOne(int index) { + if (isInvalid() || mesh.isEmpty()) { return; } - vao.delete(); - vao = null; + instancer.update(); + + int instanceCount = instancer.getInstanceCount(); + if (instanceCount <= 0 || index >= instanceCount) { + return; + } + + var vao = lazyScratchVao(); + + instancer.bindRaw(vao, meshAttributes, index); + mesh.setup(vao); + + vao.bindForDraw(); + + mesh.draw(1); } + + private GlVertexArray lazyScratchVao() { + if (vaoScratch == null) { + vaoScratch = GlVertexArray.create(); + } + return vaoScratch; + } + + public void delete() { + if (vao != null) { + vao.delete(); + vao = null; + } + + if (vaoScratch != null) { + vaoScratch.delete(); + vaoScratch = null; + } + } } 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 20d13290a..abcb61897 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 @@ -25,7 +25,7 @@ import com.jozufozu.flywheel.backend.engine.InstancerKey; public class InstancedDrawManager { private final Map, InstancedInstancer> instancers = new HashMap<>(); private final List uninitializedInstancers = new ArrayList<>(); - private final List> initializedInstancers = new ArrayList<>(); + private final List initializedInstancers = new ArrayList<>(); private final Map drawSets = new EnumMap<>(RenderStage.class); private final Map meshPools = new HashMap<>(); private final EBOCache eboCache = new EBOCache(); @@ -68,27 +68,33 @@ public class InstancedDrawManager { .forEach(DrawSet::delete); drawSets.clear(); - initializedInstancers.forEach(InstancedInstancer::delete); + initializedInstancers.forEach(InitializedInstancer::deleteInstancer); initializedInstancers.clear(); eboCache.invalidate(); } public void clearInstancers() { - initializedInstancers.forEach(InstancedInstancer::clear); + initializedInstancers.forEach(InitializedInstancer::clear); } private void add(InstancedInstancer instancer, Model model, RenderStage stage) { instancer.init(); + DrawSet drawSet = drawSets.computeIfAbsent(stage, DrawSet::new); + List drawCalls = new ArrayList<>(); + var meshes = model.getMeshes(); for (var entry : meshes.entrySet()) { var mesh = alloc(entry.getValue()); + ShaderState shaderState = new ShaderState(entry.getKey(), mesh.getVertexType(), instancer.type); - DrawCall drawCall = new DrawCall(instancer, mesh); + DrawCall drawCall = new DrawCall(instancer, mesh, shaderState); + drawSet.put(shaderState, drawCall); + drawCalls.add(drawCall); } - initializedInstancers.add(instancer); + initializedInstancers.add(new InitializedInstancer(instancer, drawCalls)); } private InstancedMeshPool.BufferedMesh alloc(Mesh mesh) { @@ -96,6 +102,16 @@ public class InstancedDrawManager { .alloc(mesh, eboCache); } + public List drawCallsForInstancer(InstancedInstancer instancer) { + for (InitializedInstancer initializedInstancer : initializedInstancers) { + if (initializedInstancer.instancer == instancer) { + return initializedInstancer.drawCalls; + } + } + + return List.of(); + } + public static class DrawSet implements Iterable>> { public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of()); @@ -134,4 +150,14 @@ public class InstancedDrawManager { private record UninitializedInstancer(InstancedInstancer instancer, Model model, RenderStage stage) { } + + private record InitializedInstancer(InstancedInstancer instancer, List drawCalls) { + public void deleteInstancer() { + instancer.delete(); + } + + public void clear() { + instancer.clear(); + } + } } 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 341bc9ef6..ce2ac49ae 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 @@ -84,7 +84,12 @@ public class InstancedInstancer extends AbstractInstancer return; } - vao.bindVertexBuffer(1, vbo.handle(), 0L, instanceStride); + bindRaw(vao, startAttrib, 0); + } + + public void bindRaw(GlVertexArray vao, int startAttrib, int baseInstance) { + long offset = (long) baseInstance * instanceStride; + vao.bindVertexBuffer(1, vbo.handle(), offset, instanceStride); vao.setBindingDivisor(1, 1); vao.bindAttributes(1, startAttrib, instanceFormat.attributes()); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java index 980d46fa2..90153e448 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/engine/instancing/InstancingEngine.java @@ -1,5 +1,7 @@ package com.jozufozu.flywheel.backend.engine.instancing; +import java.util.List; + import org.lwjgl.opengl.GL32; import com.jozufozu.flywheel.api.context.Context; @@ -13,9 +15,12 @@ import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.backend.compile.InstancingPrograms; import com.jozufozu.flywheel.backend.engine.AbstractEngine; +import com.jozufozu.flywheel.backend.engine.InstanceHandleImpl; import com.jozufozu.flywheel.backend.engine.UniformBuffer; +import com.jozufozu.flywheel.backend.engine.indirect.Textures; import com.jozufozu.flywheel.gl.GlStateTracker; import com.jozufozu.flywheel.gl.GlTextureUnit; +import com.jozufozu.flywheel.lib.context.Contexts; import com.jozufozu.flywheel.lib.material.MaterialIndices; import com.jozufozu.flywheel.lib.task.Flag; import com.jozufozu.flywheel.lib.task.NamedFlag; @@ -24,6 +29,8 @@ import com.jozufozu.flywheel.lib.task.SyncedPlan; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.resources.model.ModelBakery; public class InstancingEngine extends AbstractEngine { private final Context context; @@ -76,8 +83,39 @@ public class InstancingEngine extends AbstractEngine { } @Override - public void renderCrumblingInstance(TaskExecutor taskExecutor, RenderContext context, Instance instance, int progress) { - // TODO: implement + public void renderCrumblingInstances(TaskExecutor taskExecutor, RenderContext context, List instances, int progress) { + // TODO: optimize + + taskExecutor.syncUntil(flushFlag::isRaised); + + var type = ModelBakery.DESTROY_TYPES.get(progress); + + try (var state = GlStateTracker.getRestoreState()) { + type.setupRenderState(); + + Textures.bindActiveTextures(); + + for (Instance instance : instances) { + if (!(instance.handle() instanceof InstanceHandleImpl impl)) { + continue; + } + if (!(impl.instancer instanceof InstancedInstancer instancer)) { + continue; + } + + List draws = drawManager.drawCallsForInstancer(instancer); + + draws.removeIf(DrawCall::isInvalid); + + for (DrawCall draw : draws) { + var shader = draw.shaderState; + + setup(shader, Contexts.CRUMBLING); + + draw.renderOne(impl.index); + } + } + } } private void setup() { diff --git a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java index 0318068fc..bfd1079a8 100644 --- a/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java +++ b/src/main/java/com/jozufozu/flywheel/config/FlwCommands.java @@ -116,7 +116,7 @@ public class FlwCommands { .then(Commands.argument("pos", BlockPosArgument.blockPos()) .then(Commands.argument("stage", IntegerArgumentType.integer(0, 9)) .executes(context -> { - BlockPos pos = BlockPosArgument.getLoadedBlockPos(context, "pos"); + BlockPos pos = BlockPosArgument.getBlockPos(context, "pos"); int value = IntegerArgumentType.getInteger(context, "stage"); Entity executor = context.getSource() @@ -145,6 +145,7 @@ public class FlwCommands { })) .then(Commands.literal("capture") .executes(context -> { + FlwShaderUniforms.FRUSTUM_PAUSED = true; FlwShaderUniforms.FRUSTUM_CAPTURE = true; return 1; }))); diff --git a/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java b/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java index 195bff687..6925bc11b 100644 --- a/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java +++ b/src/main/java/com/jozufozu/flywheel/impl/visualization/VisualizationManagerImpl.java @@ -8,7 +8,6 @@ import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.api.backend.Engine; import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.api.event.RenderStage; -import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.task.Plan; import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.api.visual.DynamicVisual; @@ -220,9 +219,9 @@ public class VisualizationManagerImpl implements VisualizationManager { continue; } - var instanceList = visual.getCrumblingInstances(); + var instances = visual.getCrumblingInstances(); - if (instanceList.isEmpty()) { + if (instances.isEmpty()) { // The visual doesn't want to render anything crumbling. continue; } @@ -232,9 +231,7 @@ public class VisualizationManagerImpl implements VisualizationManager { int progress = set.last() .getProgress(); - for (Instance instance : instanceList) { - engine.renderCrumblingInstance(taskExecutor, context, instance, progress); - } + engine.renderCrumblingInstances(taskExecutor, context, instances, progress); } } diff --git a/src/main/java/com/jozufozu/flywheel/lib/context/Contexts.java b/src/main/java/com/jozufozu/flywheel/lib/context/Contexts.java index 2a1a29976..cb502d33b 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/context/Contexts.java +++ b/src/main/java/com/jozufozu/flywheel/lib/context/Contexts.java @@ -4,13 +4,26 @@ import org.jetbrains.annotations.ApiStatus; import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.api.context.Context; +import com.jozufozu.flywheel.gl.shader.GlProgram; import com.jozufozu.flywheel.lib.util.ResourceUtil; import net.minecraft.resources.ResourceLocation; public final class Contexts { - public static final SimpleContext WORLD = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.WORLD_FRAGMENT)); - public static final SimpleContext CRUMBLING = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT)); + public static final SimpleContext WORLD = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.WORLD_FRAGMENT, program -> { + program.bind(); + program.setSamplerBinding("flw_diffuseTex", 0); + program.setSamplerBinding("flw_overlayTex", 1); + program.setSamplerBinding("flw_lightTex", 2); + GlProgram.unbind(); + })); + + // TODO: can we make crumbling a fragment material? + public static final SimpleContext CRUMBLING = Context.REGISTRY.registerAndGet(new SimpleContext(Files.WORLD_VERTEX, Files.CRUMBLING_FRAGMENT, program -> { + program.bind(); + program.setSamplerBinding("flw_diffuseTex", 0); + GlProgram.unbind(); + })); private Contexts() { } diff --git a/src/main/java/com/jozufozu/flywheel/lib/context/SimpleContext.java b/src/main/java/com/jozufozu/flywheel/lib/context/SimpleContext.java index 31c8becc4..c3603149c 100644 --- a/src/main/java/com/jozufozu/flywheel/lib/context/SimpleContext.java +++ b/src/main/java/com/jozufozu/flywheel/lib/context/SimpleContext.java @@ -1,18 +1,16 @@ package com.jozufozu.flywheel.lib.context; +import java.util.function.Consumer; + import com.jozufozu.flywheel.api.context.Context; import com.jozufozu.flywheel.gl.shader.GlProgram; import net.minecraft.resources.ResourceLocation; -public record SimpleContext(ResourceLocation vertexShader, ResourceLocation fragmentShader) implements Context { +public record SimpleContext(ResourceLocation vertexShader, ResourceLocation fragmentShader, Consumer onLink) implements Context { @Override public void onProgramLink(GlProgram program) { - program.bind(); - program.setSamplerBinding("flw_diffuseTex", 0); - program.setSamplerBinding("flw_overlayTex", 1); - program.setSamplerBinding("flw_lightTex", 2); - GlProgram.unbind(); + onLink.accept(program); } @Override diff --git a/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag b/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag index 233fb6676..b098be8a1 100644 --- a/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag +++ b/src/main/resources/assets/flywheel/flywheel/context/crumbling.frag @@ -34,7 +34,8 @@ void flw_initFragment() { void flw_contextFragment() { vec4 color = flw_fragColor; - if (flw_discardPredicate(color)) { + // Ignore the discard predicate since we control the texture. + if (color.a < 0.01) { discard; }