diff --git a/src/main/java/com/jozufozu/flywheel/api/MaterialGroup.java b/src/main/java/com/jozufozu/flywheel/api/MaterialGroup.java deleted file mode 100644 index 380e6f892..000000000 --- a/src/main/java/com/jozufozu/flywheel/api/MaterialGroup.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.jozufozu.flywheel.api; - -import com.jozufozu.flywheel.api.struct.StructType; - -@Deprecated -public interface MaterialGroup { - /** - * Get the material as defined by the given {@link StructType type}. - * - * @param spec The material you want to create instances with. - * @param The type representing the per instance data. - * @return A material you can use to render models. - */ - InstancerFactory material(StructType spec); -} diff --git a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java index 7c7b4bd43..cf994e2df 100644 --- a/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java +++ b/src/main/java/com/jozufozu/flywheel/api/struct/StructType.java @@ -2,6 +2,7 @@ package com.jozufozu.flywheel.api.struct; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.model.ModelTransformer; import com.jozufozu.flywheel.core.source.FileResolution; @@ -10,7 +11,7 @@ import com.jozufozu.flywheel.core.source.FileResolution; * A StructType contains metadata for a specific instance struct that Flywheel can interface with. * @param The java representation of the instance struct. */ -public interface StructType { +public interface StructType { /** * @return A new, zeroed instance of S. diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java index e39e3bb0d..bc656df99 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstancer.java @@ -9,7 +9,7 @@ import com.jozufozu.flywheel.api.struct.StructType; public abstract class AbstractInstancer implements Instancer { - protected final StructType type; + public final StructType type; protected final ArrayList data = new ArrayList<>(); protected boolean anyToRemove; @@ -104,6 +104,8 @@ public abstract class AbstractInstancer implements Inst return instanceData; } + public abstract void delete(); + @Override public String toString() { return "Instancer[" + getInstanceCount() + ']'; diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java index a9759c368..9fa6d9470 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/DrawBuffer.java @@ -16,12 +16,11 @@ import net.minecraft.client.renderer.RenderType; * * The number of vertices needs to be known ahead of time. */ -public class DrawBuffer implements AutoCloseable { +public class DrawBuffer { private final RenderType parent; private ByteBuffer backingBuffer; private int expectedVertices; - private Cleaner.Cleanable cleanable; public DrawBuffer(RenderType parent) { this.parent = parent; @@ -46,12 +45,8 @@ public class DrawBuffer implements AutoCloseable { if (backingBuffer == null) { backingBuffer = MemoryTracker.create(byteSize); - cleanable = FlywheelMemory.track(this, backingBuffer); - } - if (byteSize > backingBuffer.capacity()) { - cleanable.clean(); + } else if (byteSize > backingBuffer.capacity()) { backingBuffer = MemoryTracker.resize(backingBuffer, byteSize); - cleanable = FlywheelMemory.track(this, backingBuffer); } return new DirectVertexConsumer(backingBuffer, format, vertexCount); @@ -80,11 +75,4 @@ public class DrawBuffer implements AutoCloseable { public void reset() { this.expectedVertices = 0; } - - @Override - public void close() { - if (cleanable != null) { - cleanable.clean(); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 5764819c6..a2364ccbe 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -13,7 +13,6 @@ import com.jozufozu.flywheel.api.InstancerManager; import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; import com.jozufozu.flywheel.backend.instancing.ratelimit.BandedPrimeLimiter; import com.jozufozu.flywheel.backend.instancing.ratelimit.DistanceUpdateLimiter; import com.jozufozu.flywheel.backend.instancing.ratelimit.NonLimiter; @@ -25,7 +24,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Camera; import net.minecraft.core.BlockPos; -public abstract class InstanceManager implements InstancingEngine.OriginShiftListener { +public abstract class InstanceManager { public final InstancerManager instancerManager; @@ -350,7 +349,6 @@ public abstract class InstanceManager implements InstancingEngine.OriginShift } } - @Override public void onOriginShift() { dynamicInstances.clear(); tickableInstances.clear(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java index 82addca0c..286a3726d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceWorld.java @@ -13,6 +13,7 @@ import com.jozufozu.flywheel.core.shader.WorldProgram; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.util.ClientLevelExtension; +import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.RenderType; @@ -36,21 +37,21 @@ public class InstanceWorld { public static InstanceWorld create(LevelAccessor level) { return switch (Backend.getBackendType()) { case INSTANCING -> { - InstancingEngine manager = new InstancingEngine<>(Contexts.WORLD); + InstancingEngine engine = new InstancingEngine<>(Contexts.WORLD); - var entityInstanceManager = new EntityInstanceManager(manager); - var blockEntityInstanceManager = new BlockEntityInstanceManager(manager); + var entityInstanceManager = new EntityInstanceManager(engine); + var blockEntityInstanceManager = new BlockEntityInstanceManager(engine); - manager.addListener(entityInstanceManager); - manager.addListener(blockEntityInstanceManager); - yield new InstanceWorld(manager, entityInstanceManager, blockEntityInstanceManager); + engine.attachManager(entityInstanceManager); + engine.attachManager(blockEntityInstanceManager); + yield new InstanceWorld(engine, entityInstanceManager, blockEntityInstanceManager); } case BATCHING -> { - var manager = new BatchingEngine(); - var entityInstanceManager = new EntityInstanceManager(manager); - var blockEntityInstanceManager = new BlockEntityInstanceManager(manager); + var engine = new BatchingEngine(); + var entityInstanceManager = new EntityInstanceManager(engine); + var blockEntityInstanceManager = new BlockEntityInstanceManager(engine); - yield new InstanceWorld(manager, entityInstanceManager, blockEntityInstanceManager); + yield new InstanceWorld(engine, entityInstanceManager, blockEntityInstanceManager); } default -> throw new IllegalArgumentException("Unknown engine type"); }; @@ -89,12 +90,17 @@ public class InstanceWorld { *

*/ public void beginFrame(BeginFrameEvent event) { - engine.beginFrame(event.getCamera()); + Camera camera = event.getCamera(); + boolean shifted = engine.maintainOriginCoordinate(camera); taskEngine.syncPoint(); - blockEntityInstanceManager.beginFrame(taskEngine, event.getCamera()); - entityInstanceManager.beginFrame(taskEngine, event.getCamera()); + if (!shifted) { + blockEntityInstanceManager.beginFrame(taskEngine, camera); + entityInstanceManager.beginFrame(taskEngine, camera); + } + + engine.beginFrame(taskEngine, camera); } /** diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java index f3d6657f5..d53c29a58 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/RenderDispatcher.java @@ -12,11 +12,13 @@ public interface RenderDispatcher { void renderSpecificType(TaskEngine taskEngine, RenderContext context, RenderType type); /** - * Maintain the integer origin coordinate to be within a certain distance from the camera in all directions. - *

- * This prevents floating point precision issues at high coordinates. + * Maintain the integer origin coordinate to be within a certain distance from the camera in all directions, + * preventing floating point precision issues at high coordinates. + * @return {@code true} if the origin coordinate was changed, {@code false} otherwise. */ - void beginFrame(Camera info); + boolean maintainOriginCoordinate(Camera camera); + + void beginFrame(TaskEngine taskEngine, Camera info); void delete(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchLists.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchLists.java new file mode 100644 index 000000000..c5ec268b4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchLists.java @@ -0,0 +1,17 @@ +package com.jozufozu.flywheel.backend.instancing.batching; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import net.minecraft.client.renderer.RenderType; + +public class BatchLists { + + public final Map>> renderLists = new HashMap<>(); + + public void add(TransformSet set) { + renderLists.computeIfAbsent(set.material.renderType(), k -> new ArrayList<>()).add(set); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java deleted file mode 100644 index 77b48809d..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedMaterialGroup.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.batching; - -import java.util.HashMap; -import java.util.Map; - -import com.jozufozu.flywheel.api.InstancedPart; -import com.jozufozu.flywheel.api.MaterialGroup; -import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker; -import com.jozufozu.flywheel.backend.instancing.TaskEngine; -import com.mojang.blaze3d.vertex.PoseStack; - -import net.minecraft.client.renderer.RenderType; - -public class BatchedMaterialGroup implements MaterialGroup { - - protected final RenderType state; - - private final Map, CPUInstancerFactory> materials = new HashMap<>(); - private int vertexCount; - private int instanceCount; - - public BatchedMaterialGroup(RenderType state) { - this.state = state; - } - - @SuppressWarnings("unchecked") - @Override - public CPUInstancerFactory material(StructType type) { - return (CPUInstancerFactory) materials.computeIfAbsent(type, CPUInstancerFactory::new); - } - - public void render(PoseStack stack, BatchDrawingTracker source, TaskEngine pool) { - -// vertexCount = 0; -// instanceCount = 0; -// for (BatchedMaterial material : materials.values()) { -// for (CPUInstancer instancer : material.models.values()) { -// instancer.setup(); -// vertexCount += instancer.getVertexCount(); -// instanceCount += instancer.getInstanceCount(); -// } -// } -// -// DirectVertexConsumer consumer = source.getDirectConsumer(state, vertexCount); -// -// // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered -// consumer.memSetZero(); -// -// for (BatchedMaterial material : materials.values()) { -// for (CPUInstancer instancer : material.models.values()) { -// instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.isUsingShaders(); -// instancer.submitTasks(stack, pool, consumer); -// } -// } - } - - public void clear() { - materials.values().forEach(CPUInstancerFactory::clear); - } - - public void delete() { - materials.clear(); - } - - /** - * Get the number of instances drawn last frame. - * @return The instance count. - */ - public int getInstanceCount() { - return instanceCount; - } - - /** - * Get the number of vertices drawn last frame. - * @return The vertex count. - */ - public int getVertexCount() { - return vertexCount; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedModel.java new file mode 100644 index 000000000..16fca800d --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchedModel.java @@ -0,0 +1,38 @@ +package com.jozufozu.flywheel.backend.instancing.batching; + +import java.util.List; + +import com.jozufozu.flywheel.api.InstancedPart; +import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.core.model.ModelSupplier; + +public class BatchedModel { + + CPUInstancer instancer; + ModelSupplier model; + StructType type; + private List> layers; + + public BatchedModel(StructType type, ModelSupplier model) { + this.type = type; + this.model = model; + this.instancer = new CPUInstancer<>(type); + } + + public void init(BatchLists batchLists) { + layers = model.get() + .entrySet() + .stream() + .map(entry -> new TransformSet<>(instancer, entry.getKey(), entry.getValue())) + .toList(); + + for (TransformSet layer : layers) { + batchLists.add(layer); + } + } + + public void clear() { + instancer.clear(); + } + +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java index d5044f97c..5ac277a17 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/BatchingEngine.java @@ -1,32 +1,45 @@ package com.jozufozu.flywheel.backend.instancing.batching; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.OptifineHandler; import com.jozufozu.flywheel.backend.instancing.BatchDrawingTracker; import com.jozufozu.flywheel.backend.instancing.Engine; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.util.FlwUtil; import com.mojang.blaze3d.platform.Lighting; +import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Matrix4f; import net.minecraft.client.Camera; import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; +import net.minecraft.world.phys.Vec3; public class BatchingEngine implements Engine { - private final Map, CPUInstancerFactory> factories = new HashMap<>(); + private final Map, CPUInstancerFactory> factories = new HashMap<>(); private final BatchDrawingTracker batchTracker = new BatchDrawingTracker(); + private final BatchLists batchLists = new BatchLists(); + + protected final List> uninitializedModels = new ArrayList<>(); + @SuppressWarnings("unchecked") @Override public CPUInstancerFactory factory(StructType type) { - return (CPUInstancerFactory) factories.computeIfAbsent(type, CPUInstancerFactory::new); + return (CPUInstancerFactory) factories.computeIfAbsent(type, this::createFactory); + } + + public CPUInstancerFactory createFactory(StructType type) { + return new CPUInstancerFactory<>(type, uninitializedModels::add); } @Override @@ -34,6 +47,25 @@ public class BatchingEngine implements Engine { return BlockPos.ZERO; } + public void submitTasks(PoseStack stack, TaskEngine taskEngine) { + batchLists.renderLists.forEach((renderType, renderList) -> { + int vertices = 0; + for (var transformSet : renderList) { + vertices += transformSet.getTotalVertexCount(); + } + + var consumer = batchTracker.getDirectConsumer(renderType, vertices); + consumer.memSetZero(); + + var outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.isUsingShaders(); + + for (var transformSet : renderList) { + transformSet.setOutputColorDiffuse(outputColorDiffuse); + transformSet.submitTasks(stack, taskEngine, consumer); + } + }); + } + @Override public void renderSpecificType(TaskEngine taskEngine, RenderContext context, RenderType type) { @@ -41,27 +73,6 @@ public class BatchingEngine implements Engine { @Override public void renderAllRemaining(TaskEngine taskEngine, RenderContext context) { -// vertexCount = 0; -// instanceCount = 0; -// for (BatchedMaterial material : materials.values()) { -// for (CPUInstancer instancer : material.models.values()) { -// instancer.setup(); -// vertexCount += instancer.getVertexCount(); -// instanceCount += instancer.getInstanceCount(); -// } -// } -// -// DirectVertexConsumer consumer = batchTracker.getDirectConsumer(state, vertexCount); -// -// // avoids rendering garbage, but doesn't fix the issue of some instances not being buffered -// consumer.memSetZero(); -// -// for (BatchedMaterial material : materials.values()) { -// for (CPUInstancer instancer : material.models.values()) { -// instancer.sbb.context.outputColorDiffuse = !consumer.hasOverlay() && !OptifineHandler.isUsingShaders(); -// instancer.submitTasks(stack, pool, consumer); -// } -// } // FIXME: this probably breaks some vanilla stuff but it works much better for flywheel Matrix4f mat = new Matrix4f(); @@ -81,8 +92,24 @@ public class BatchingEngine implements Engine { } @Override - public void beginFrame(Camera info) { + public boolean maintainOriginCoordinate(Camera camera) { + // do nothing + return false; + } + @Override + public void beginFrame(TaskEngine taskEngine, Camera info) { + for (var model : uninitializedModels) { + model.init(batchLists); + } + + uninitializedModels.clear(); + + Vec3 cameraPos = info.getPosition(); + var stack = FlwUtil.copyPoseStack(RenderContext.CURRENT.stack()); + stack.translate(-cameraPos.x, -cameraPos.y, -cameraPos.z); + + submitTasks(stack, taskEngine); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java index d03d5ffce..4166ef078 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancer.java @@ -1,74 +1,39 @@ package com.jozufozu.flywheel.backend.instancing.batching; +import java.util.List; + import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; -import com.jozufozu.flywheel.backend.instancing.TaskEngine; -import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; public class CPUInstancer extends AbstractInstancer { -// private final Batched batchingType; -// -// final ModelTransformer sbb; - public CPUInstancer(StructType type) { super(type); -// batchingType = type; -// -// sbb = new ModelTransformer(modelData.get()); - } - - void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) { -// int instances = getInstanceCount(); -// -// while (instances > 0) { -// int end = instances; -// instances -= 512; -// int start = Math.max(instances, 0); -// -// int verts = getModelVertexCount() * (end - start); -// -// DirectVertexConsumer sub = consumer.split(verts); -// -// pool.submit(() -> drawRange(stack, sub, start, end)); -// } - } - - private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) { -// ModelTransformer.Params params = new ModelTransformer.Params(); -// -// for (D d : data.subList(from, to)) { -// params.loadDefault(); -// -// batchingType.transform(d, params); -// -// sbb.renderInto(params, stack, buffer); -// } - } - - void drawAll(PoseStack stack, VertexConsumer buffer) { -// ModelTransformer.Params params = new ModelTransformer.Params(); -// for (D d : data) { -// params.loadDefault(); -// -// batchingType.transform(d, params); -// -// sbb.renderInto(params, stack, buffer); -// } } void setup() { -// if (anyToRemove) { -// data.removeIf(InstanceData::isRemoved); -// anyToRemove = false; -// } + if (anyToRemove) { + data.removeIf(InstancedPart::isRemoved); + anyToRemove = false; + } + } + + public List getRange(int start, int end) { + return data.subList(start, end); + } + + public List getAll() { + return data; } @Override public void notifyDirty() { // noop } + + @Override + public void delete() { + // noop + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancerFactory.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancerFactory.java index 11965c7da..005e044f1 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancerFactory.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/CPUInstancerFactory.java @@ -2,36 +2,31 @@ package com.jozufozu.flywheel.backend.instancing.batching; import java.util.HashMap; import java.util.Map; +import java.util.function.Consumer; import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.InstancerFactory; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.core.model.ModelSupplier; -import com.mojang.blaze3d.vertex.PoseStack; -import com.mojang.blaze3d.vertex.VertexConsumer; public class CPUInstancerFactory implements InstancerFactory { - protected final Map> models; + protected final Map> models; private final StructType type; + private final Consumer> creationListener; - public CPUInstancerFactory(StructType type) { + public CPUInstancerFactory(StructType type, Consumer> creationListener) { this.type = type; + this.creationListener = creationListener; + this.models = new HashMap<>(); } @Override public Instancer model(ModelSupplier modelKey) { - return models.computeIfAbsent(modelKey, k -> new CPUInstancer<>(type)); - } - - public void setupAndRenderInto(PoseStack stack, VertexConsumer buffer) { - for (CPUInstancer instancer : models.values()) { - instancer.setup(); - instancer.drawAll(stack, buffer); - } + return models.computeIfAbsent(modelKey, this::createModel).instancer; } /** @@ -39,6 +34,12 @@ public class CPUInstancerFactory implements InstancerFa */ public void clear() { models.values() - .forEach(CPUInstancer::clear); + .forEach(BatchedModel::clear); + } + + private BatchedModel createModel(ModelSupplier k) { + var out = new BatchedModel<>(type, k); + creationListener.accept(out); + return out; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/TransformSet.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/TransformSet.java new file mode 100644 index 000000000..8e4d52b7e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/batching/TransformSet.java @@ -0,0 +1,74 @@ +package com.jozufozu.flywheel.backend.instancing.batching; + +import java.util.List; + +import com.jozufozu.flywheel.api.InstancedPart; +import com.jozufozu.flywheel.api.material.Material; +import com.jozufozu.flywheel.backend.instancing.TaskEngine; +import com.jozufozu.flywheel.backend.model.DirectVertexConsumer; +import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.model.ModelTransformer; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; + +public class TransformSet { + + public final Material material; + public final Mesh mesh; + private final CPUInstancer instancer; + private final ModelTransformer modelTransformer; + + public TransformSet(CPUInstancer instancer, Material material, Mesh mesh) { + this.instancer = instancer; + this.material = material; + this.mesh = mesh; + + modelTransformer = new ModelTransformer(mesh); + } + + void submitTasks(PoseStack stack, TaskEngine pool, DirectVertexConsumer consumer) { + instancer.setup(); + + int instances = instancer.getInstanceCount(); + + while (instances > 0) { + int end = instances; + instances -= 512; + int start = Math.max(instances, 0); + + int verts = mesh.getVertexCount() * (end - start); + + DirectVertexConsumer sub = consumer.split(verts); + + pool.submit(() -> drawRange(stack, sub, start, end)); + } + } + + private void drawRange(PoseStack stack, VertexConsumer buffer, int from, int to) { + drawList(stack, buffer, instancer.getRange(from, to)); + } + + void drawAll(PoseStack stack, VertexConsumer buffer) { + drawList(stack, buffer, instancer.getAll()); + } + + private void drawList(PoseStack stack, VertexConsumer buffer, List list) { + ModelTransformer.Params params = new ModelTransformer.Params(); + + for (D d : list) { + params.loadDefault(); + + instancer.type.transform(d, params); + + modelTransformer.renderInto(params, stack, buffer); + } + } + + public int getTotalVertexCount() { + return mesh.getVertexCount() * instancer.getInstanceCount(); + } + + public void setOutputColorDiffuse(boolean outputColorDiffuse) { + modelTransformer.context.outputColorDiffuse = outputColorDiffuse; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java index d1ba46260..1b5d7425c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancer.java @@ -129,4 +129,10 @@ public class GPUInstancer extends AbstractInstancer vao.setAttributeDivisor(this.attributeBaseIndex + i, 1); } } + + @Override + public void delete() { + vbo.delete(); + vbo = null; + } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancerFactory.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancerFactory.java index 843d91316..ff20915db 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancerFactory.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/GPUInstancerFactory.java @@ -8,6 +8,7 @@ import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.Instancer; import com.jozufozu.flywheel.api.InstancerFactory; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.core.model.ModelSupplier; import net.minecraft.client.renderer.RenderType; @@ -36,7 +37,7 @@ public class GPUInstancerFactory implements InstancerFa return models.values() .stream() .map(InstancedModel::getInstancer) - .mapToInt(GPUInstancer::getInstanceCount) + .mapToInt(AbstractInstancer::getInstanceCount) .sum(); } @@ -59,7 +60,7 @@ public class GPUInstancerFactory implements InstancerFa models.values() .stream() .map(InstancedModel::getInstancer) - .forEach(GPUInstancer::clear); + .forEach(AbstractInstancer::clear); } private InstancedModel createInstancer(ModelSupplier model) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java index f94f345ef..bd5abab4c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java @@ -4,13 +4,14 @@ import java.util.List; import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; +import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.core.model.ModelSupplier; public class InstancedModel { - public final GPUInstancer instancer; - public final ModelSupplier model; + private final ModelSupplier model; private final StructType type; + private final GPUInstancer instancer; private List layers; public InstancedModel(StructType type, ModelSupplier model) { @@ -48,8 +49,7 @@ public class InstancedModel { public void delete() { if (instancer.vbo == null) return; - instancer.vbo.delete(); - instancer.vbo = null; + instancer.delete(); for (var layer : layers) { layer.delete(); diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java index f5b306129..706ffe3e8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingEngine.java @@ -20,6 +20,7 @@ import com.jozufozu.flywheel.backend.gl.GlVertexArray; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.instancing.Engine; +import com.jozufozu.flywheel.backend.instancing.InstanceManager; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; @@ -65,19 +66,22 @@ public class InstancingEngine

implements Engine { protected final ProgramCompiler

context; - protected final Map, GPUInstancerFactory> factories = new HashMap<>(); + protected final Map, GPUInstancerFactory> factories = new HashMap<>(); protected final List> uninitializedModels = new ArrayList<>(); protected final RenderLists renderLists = new RenderLists(); - private final WeakHashSet listeners; + /** + * The set of instance managers that are attached to this engine. + */ + private final WeakHashSet> instanceManagers; private int vertexCount; private int instanceCount; public InstancingEngine(ProgramCompiler

context) { this.context = context; - this.listeners = new WeakHashSet<>(); + this.instanceManagers = new WeakHashSet<>(); } @SuppressWarnings("unchecked") @@ -198,21 +202,29 @@ public class InstancingEngine

implements Engine { return originCoordinate; } - public void addListener(OriginShiftListener listener) { - listeners.add(listener); + public void attachManager(InstanceManager listener) { + instanceManagers.add(listener); } - /** - * Maintain the integer origin coordinate to be within a certain distance from the camera in all directions. - * - * This prevents floating point precision issues at high coordinates. - */ @Override - public void beginFrame(Camera info) { - checkOriginDistance(info); + public boolean maintainOriginCoordinate(Camera camera) { + Vec3 cameraPos = camera.getPosition(); - for (var factory : uninitializedModels) { - factory.init(renderLists); + double distanceSqr = Vec3.atLowerCornerOf(originCoordinate) + .subtract(cameraPos) + .lengthSqr(); + + if (distanceSqr > MAX_ORIGIN_DISTANCE * MAX_ORIGIN_DISTANCE) { + shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z)); + return true; + } + return false; + } + + @Override + public void beginFrame(TaskEngine taskEngine, Camera info) { + for (var model : uninitializedModels) { + model.init(renderLists); } uninitializedModels.clear(); @@ -222,26 +234,12 @@ public class InstancingEngine

implements Engine { .flush(); } - private void checkOriginDistance(Camera info) { - int cX = Mth.floor(info.getPosition().x); - int cY = Mth.floor(info.getPosition().y); - int cZ = Mth.floor(info.getPosition().z); - - int dX = cX - originCoordinate.getX(); - int dY = cY - originCoordinate.getY(); - int dZ = cZ - originCoordinate.getZ(); - - if (Math.abs(dX) > MAX_ORIGIN_DISTANCE || Math.abs(dY) > MAX_ORIGIN_DISTANCE || Math.abs(dZ) > MAX_ORIGIN_DISTANCE) { - shiftListeners(cX, cY, cZ); - } - } - private void shiftListeners(int cX, int cY, int cZ) { originCoordinate = new BlockPos(cX, cY, cZ); factories.values().forEach(GPUInstancerFactory::clear); - listeners.forEach(OriginShiftListener::onOriginShift); + instanceManagers.forEach(InstanceManager::onOriginShift); } @Override @@ -336,7 +334,7 @@ public class InstancingEngine

implements Engine { if (part.getOwner() instanceof GPUInstancer instancer) { // queue the instances for copying to the crumbling instance buffer - map.computeIfAbsent(instancer.parent.model, k -> new ArrayList<>()).add(part); + map.computeIfAbsent(instancer.parent.getModel(), k -> new ArrayList<>()).add(part); } } } @@ -378,8 +376,4 @@ public class InstancingEngine

implements Engine { return dataByStage; } - @FunctionalInterface - public interface OriginShiftListener { - void onOriginShift(); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/RenderLists.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/RenderLists.java index a3f003ed6..b5aa12258 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/RenderLists.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/RenderLists.java @@ -12,7 +12,7 @@ import net.minecraft.client.renderer.RenderType; public class RenderLists { - private Map> renderLists = new HashMap<>(); + private final Map> renderLists = new HashMap<>(); public final Set layersToProcess = new HashSet<>(); public ListMultimap get(RenderType type) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java index 5a0bc8fb1..943559acc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/BufferWriter.java @@ -2,10 +2,11 @@ package com.jozufozu.flywheel.backend.struct; import java.nio.ByteBuffer; +import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructWriter; -public abstract class BufferWriter implements StructWriter { +public abstract class BufferWriter implements StructWriter { protected final ByteBuffer backingBuffer; protected final int stride; diff --git a/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java b/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java index 40d489d54..96e675bbd 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/struct/UnsafeBufferWriter.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import org.lwjgl.system.MemoryUtil; +import com.jozufozu.flywheel.api.InstancedPart; import com.jozufozu.flywheel.api.struct.StructType; /** @@ -14,7 +15,7 @@ import com.jozufozu.flywheel.api.struct.StructType; * better optimized code than other implementations. The implementation does not check for invalid memory accesses, * meaning that errors can corrupt process memory. */ -public abstract class UnsafeBufferWriter extends BufferWriter { +public abstract class UnsafeBufferWriter extends BufferWriter { /** * The write pointer into the buffer storage. This is advanced by the stride every time * {@link UnsafeBufferWriter#advance()} is called. diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java index dd5b8e192..126082776 100644 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java @@ -44,7 +44,8 @@ public class CrumblingRenderer { } public static void renderCrumbling(LevelRenderer levelRenderer, ClientLevel level, PoseStack poseStack, Camera camera, Matrix4f projectionMatrix) { - if (!Backend.canUseInstancing(level)) return; + // TODO: one pass base/crumbling + if (true) return; Int2ObjectMap> activeStages = getActiveStageBlockEntities(levelRenderer, level); if (activeStages.isEmpty()) return; @@ -78,7 +79,7 @@ public class CrumblingRenderer { stage.getValue().forEach(instanceManager::add); instanceManager.beginFrame(SerialTaskEngine.INSTANCE, camera); - engine.beginFrame(camera); + engine.beginFrame(SerialTaskEngine.INSTANCE, camera); engine.renderAllRemaining(SerialTaskEngine.INSTANCE, ctx); @@ -143,7 +144,7 @@ public class CrumblingRenderer { private State() { instancerManager = new CrumblingEngine(); instanceManager = new CrumblingInstanceManager(instancerManager); - instancerManager.addListener(instanceManager); + instancerManager.attachManager(instanceManager); } private void kill() { diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererDispatchMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererDispatchMixin.java new file mode 100644 index 000000000..aac392311 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererDispatchMixin.java @@ -0,0 +1,183 @@ +package com.jozufozu.flywheel.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.gl.GlStateTracker; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.jozufozu.flywheel.core.RenderContext; +import com.jozufozu.flywheel.event.RenderLayerEvent; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Matrix4f; + +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.Sheets; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraftforge.common.MinecraftForge; + +@Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after sodium +public class LevelRendererDispatchMixin { + + @Inject(at = @At("TAIL"), method = "renderChunkLayer") + private void renderChunkLayer(RenderType pRenderType, PoseStack pPoseStack, double pCamX, double pCamY, double pCamZ, Matrix4f pProjectionMatrix, CallbackInfo ci) { + try (var restoreState = GlStateTracker.getRestoreState()) { + + // TODO: Is this necessary? + InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, pRenderType); + MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(RenderContext.CURRENT, pRenderType)); + } + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", ordinal = 0, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch()V")) + private void endBatch(CallbackInfo ci) { + if (RenderContext.CURRENT != null && Backend.isGameActive() && Backend.isOn()) { + try (var restoreState = GlStateTracker.getRestoreState()) { + InstancedRenderDispatcher.renderAllRemaining(RenderContext.CURRENT); + } + } + } + + @Unique + private void flywheel$dispatch(RenderType pRenderType) { + if (RenderContext.CURRENT != null && Backend.isGameActive() && Backend.isOn()) { + try (var restoreState = GlStateTracker.getRestoreState()) { + InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, pRenderType); + } + } + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 0, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$entitySolid(CallbackInfo ci) { + flywheel$dispatch(RenderType.entitySolid(TextureAtlas.LOCATION_BLOCKS)); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 1, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$entityCutout(CallbackInfo ci) { + flywheel$dispatch(RenderType.entityCutout(TextureAtlas.LOCATION_BLOCKS)); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 2, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$entityCutoutNoCull(CallbackInfo ci) { + flywheel$dispatch(RenderType.entityCutoutNoCull(TextureAtlas.LOCATION_BLOCKS)); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 3, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$entitySmoothCutout(CallbackInfo ci) { + flywheel$dispatch(RenderType.entitySmoothCutout(TextureAtlas.LOCATION_BLOCKS)); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 4, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$solid(CallbackInfo ci) { + flywheel$dispatch(RenderType.solid()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 5, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$endPortal(CallbackInfo ci) { + flywheel$dispatch(RenderType.endPortal()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 6, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$endGateway(CallbackInfo ci) { + flywheel$dispatch(RenderType.endGateway()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 7, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$solidBlockSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.solidBlockSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 8, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$cutoutBlockSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.cutoutBlockSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 9, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$bedSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.bedSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 10, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$shulkerBoxSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.shulkerBoxSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 11, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$signSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.signSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 12, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$chestSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.chestSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 13, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$translucentCullBlockSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.translucentCullBlockSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 14, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$bannerSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.bannerSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 15, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$shieldSheet(CallbackInfo ci) { + flywheel$dispatch(Sheets.shieldSheet()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 16, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$armorGlint(CallbackInfo ci) { + flywheel$dispatch(RenderType.armorGlint()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 17, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$armorEntityGlint(CallbackInfo ci) { + flywheel$dispatch(RenderType.armorEntityGlint()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 18, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$glint(CallbackInfo ci) { + flywheel$dispatch(RenderType.glint()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 19, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$glintDirect(CallbackInfo ci) { + flywheel$dispatch(RenderType.glintDirect()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 20, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$glintTranslucent(CallbackInfo ci) { + flywheel$dispatch(RenderType.glintTranslucent()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 21, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$entityGlint(CallbackInfo ci) { + flywheel$dispatch(RenderType.entityGlint()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 22, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$entityGlintDirect(CallbackInfo ci) { + flywheel$dispatch(RenderType.entityGlintDirect()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 23, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$waterMask(CallbackInfo ci) { + flywheel$dispatch(RenderType.waterMask()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 24, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$lines1(CallbackInfo ci) { + flywheel$dispatch(RenderType.lines()); + } + + @Inject(method = "renderLevel", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 25, target = "Lnet/minecraft/client/renderer/MultiBufferSource$BufferSource;endBatch(Lnet/minecraft/client/renderer/RenderType;)V")) + private void renderLayer$lines2(CallbackInfo ci) { + flywheel$dispatch(RenderType.lines()); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererInstanceUpdateMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererInstanceUpdateMixin.java new file mode 100644 index 000000000..bb781b29e --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererInstanceUpdateMixin.java @@ -0,0 +1,32 @@ +package com.jozufozu.flywheel.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.jozufozu.flywheel.backend.Backend; +import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; + +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; + +@Mixin(LevelRenderer.class) +public class LevelRendererInstanceUpdateMixin { + @Shadow + private ClientLevel level; + + /** + * This gets called when a block is marked for rerender by vanilla. + */ + @Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V") + private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) { + if (Backend.isOn()) { + InstancedRenderDispatcher.getBlockEntities(level) + .update(level.getBlockEntity(pos)); + } + } +} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java index 93fc64158..12fa21441 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java @@ -3,6 +3,7 @@ package com.jozufozu.flywheel.mixin; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At.Shift; import org.spongepowered.asm.mixin.injection.Inject; @@ -26,8 +27,6 @@ import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.RenderBuffers; import net.minecraft.client.renderer.RenderType; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.MinecraftForge; @@ -51,16 +50,6 @@ public class LevelRendererMixin { } } - @Inject(at = @At("TAIL"), method = "renderChunkLayer") - private void renderChunkLayer(RenderType pRenderType, PoseStack pPoseStack, double pCamX, double pCamY, double pCamZ, Matrix4f pProjectionMatrix, CallbackInfo ci) { - try (var restoreState = GlStateTracker.getRestoreState()) { - - // TODO: Is this necessary? - InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, pRenderType); - MinecraftForge.EVENT_BUS.post(new RenderLayerEvent(RenderContext.CURRENT, pRenderType)); - } - } - @Inject(at = @At("TAIL"), method = "renderLevel") private void endRender(PoseStack pPoseStack, float pPartialTick, long pFinishNanoTime, boolean pRenderBlockOutline, Camera pCamera, GameRenderer pGameRenderer, LightTexture pLightTexture, Matrix4f pProjectionMatrix, CallbackInfo ci) { RenderContext.CURRENT = null; @@ -78,17 +67,4 @@ public class LevelRendererMixin { private void renderBlockBreaking(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) { CrumblingRenderer.renderCrumbling((LevelRenderer) (Object) this, level, poseStack, camera, projectionMatrix); } - - // Instancing - - /** - * This gets called when a block is marked for rerender by vanilla. - */ - @Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V") - private void checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) { - if (Backend.isOn()) { - InstancedRenderDispatcher.getBlockEntities(level) - .update(level.getBlockEntity(pos)); - } - } } diff --git a/src/main/java/com/jozufozu/flywheel/mixin/MultiBufferSourceMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/MultiBufferSourceMixin.java deleted file mode 100644 index ae7d1a77a..000000000 --- a/src/main/java/com/jozufozu/flywheel/mixin/MultiBufferSourceMixin.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.jozufozu.flywheel.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.GlStateTracker; -import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.core.RenderContext; - -import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; - -@Mixin(MultiBufferSource.BufferSource.class) -public class MultiBufferSourceMixin { - - @Inject(method = "endBatch(Lnet/minecraft/client/renderer/RenderType;)V", at = @At("TAIL")) - private void renderLayer(RenderType renderType, CallbackInfo ci) { - if (RenderContext.CURRENT != null && Backend.isGameActive() && Backend.isOn()) { - try (var restoreState = GlStateTracker.getRestoreState()) { - - InstancedRenderDispatcher.renderSpecificType(RenderContext.CURRENT, renderType); - - } - } - } - - @Inject(method = "endBatch()V", at = @At("TAIL")) - private void endBatch(CallbackInfo ci) { - if (RenderContext.CURRENT != null && Backend.isGameActive() && Backend.isOn()) { - try (var restoreState = GlStateTracker.getRestoreState()) { - InstancedRenderDispatcher.renderAllRemaining(RenderContext.CURRENT); - } - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java b/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java index beed0590a..be55e6518 100644 --- a/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java +++ b/src/main/java/com/jozufozu/flywheel/util/FlwUtil.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.stream.Stream; import com.jozufozu.flywheel.mixin.BlockEntityRenderDispatcherAccessor; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; @@ -29,6 +30,13 @@ public class FlwUtil { return new String(arr); } + public static PoseStack copyPoseStack(PoseStack stack) { + PoseStack copy = new PoseStack(); + copy.last().pose().load(stack.last().pose()); + copy.last().normal().load(stack.last().normal()); + return copy; + } + public static int numDigits(int number) { // cursed but allegedly the fastest algorithm, taken from https://www.baeldung.com/java-number-of-digits-in-int if (number < 100000) { diff --git a/src/main/resources/flywheel.mixins.json b/src/main/resources/flywheel.mixins.json index 0f9d0df2e..71f847654 100644 --- a/src/main/resources/flywheel.mixins.json +++ b/src/main/resources/flywheel.mixins.json @@ -19,8 +19,9 @@ "InstanceAddMixin", "InstanceRemoveMixin", "LevelRendererAccessor", + "LevelRendererDispatchMixin", + "LevelRendererInstanceUpdateMixin", "LevelRendererMixin", - "MultiBufferSourceMixin", "PausedPartialTickAccessor", "RenderTypeMixin", "light.LightUpdateMixin",