diff --git a/src/main/java/com/jozufozu/flywheel/Flywheel.java b/src/main/java/com/jozufozu/flywheel/Flywheel.java index 7aaa6a7f6..df50bb4d2 100644 --- a/src/main/java/com/jozufozu/flywheel/Flywheel.java +++ b/src/main/java/com/jozufozu/flywheel/Flywheel.java @@ -7,7 +7,6 @@ import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.RenderWork; import com.jozufozu.flywheel.backend.ShadersModHandler; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; -import com.jozufozu.flywheel.backend.instancing.instancing.MeshPool; import com.jozufozu.flywheel.config.BackendTypeArgument; import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwConfig; @@ -16,7 +15,6 @@ import com.jozufozu.flywheel.core.PartialModel; import com.jozufozu.flywheel.core.QuadConverter; import com.jozufozu.flywheel.core.StitchedSprite; import com.jozufozu.flywheel.core.compile.ProgramCompiler; -import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.model.Models; import com.jozufozu.flywheel.event.EntityWorldHandler; import com.jozufozu.flywheel.event.ForgeEvents; @@ -80,10 +78,8 @@ public class Flywheel { forgeEventBus.addListener(FlwCommands::registerClientCommands); forgeEventBus.addListener(EventPriority.HIGHEST, QuadConverter::onRendererReload); - forgeEventBus.addListener(ProgramCompiler::invalidateAll); + forgeEventBus.addListener(ProgramCompiler::invalidateAll); forgeEventBus.addListener(Models::onReload); - forgeEventBus.addListener(MeshPool::reset); - forgeEventBus.addListener(CrumblingRenderer::onReloadRenderers); forgeEventBus.addListener(InstancedRenderDispatcher::onReloadRenderers); forgeEventBus.addListener(InstancedRenderDispatcher::onRenderStage); diff --git a/src/main/java/com/jozufozu/flywheel/backend/Loader.java b/src/main/java/com/jozufozu/flywheel/backend/Loader.java index 3519ace3e..d37b19a23 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/Loader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/Loader.java @@ -7,7 +7,6 @@ import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.core.ComponentRegistry; import com.jozufozu.flywheel.core.compile.ContextShader; import com.jozufozu.flywheel.core.compile.ProgramCompiler; -import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.core.source.FileResolution; import com.jozufozu.flywheel.core.source.ShaderLoadingException; import com.jozufozu.flywheel.core.source.ShaderSources; @@ -83,9 +82,7 @@ public class Loader implements ResourceManagerReloadListener { ClientLevel world = Minecraft.getInstance().level; if (Backend.canUseInstancing(world)) { - // TODO: looks like it might be good to have another event here InstancedRenderDispatcher.resetInstanceWorld(world); - CrumblingRenderer.reset(); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/SadCrumbling.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/SadCrumbling.java deleted file mode 100644 index 296544423..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/SadCrumbling.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.SortedSet; - -import javax.annotation.Nonnull; - -import org.jetbrains.annotations.NotNull; - -import com.jozufozu.flywheel.api.instancer.InstancedPart; -import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance; -import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager; -import com.jozufozu.flywheel.backend.instancing.instancing.GPUInstancer; -import com.jozufozu.flywheel.core.model.Model; -import com.jozufozu.flywheel.mixin.LevelRendererAccessor; - -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.server.level.BlockDestructionProgress; - -public class SadCrumbling { -// public void renderCrumbling(LevelRenderer levelRenderer, ClientLevel level, PoseStack stack, Camera camera, Matrix4f projectionMatrix) { -// var dataByStage = getDataByStage(levelRenderer, level); -// if (dataByStage.isEmpty()) { -// return; -// } -// -// var map = modelsToParts(dataByStage); -// var stateSnapshot = GameStateRegistry.takeSnapshot(); -// -// Vec3 cameraPosition = camera.getPosition(); -// var camX = cameraPosition.x - originCoordinate.getX(); -// var camY = cameraPosition.y - originCoordinate.getY(); -// var camZ = cameraPosition.z - originCoordinate.getZ(); -// -// // don't want to mutate viewProjection -// var vp = projectionMatrix.copy(); -// vp.multiplyWithTranslation((float) -camX, (float) -camY, (float) -camZ); -// -// GlBuffer instanceBuffer = GlBuffer.requestPersistent(GlBufferType.ARRAY_BUFFER); -// -// GlVertexArray crumblingVAO = new GlVertexArray(); -// -// crumblingVAO.bind(); -// -// // crumblingVAO.bindAttributes(); -// -// for (var entry : map.entrySet()) { -// var model = entry.getKey(); -// var parts = entry.getValue(); -// -// if (parts.isEmpty()) { -// continue; -// } -// -// StructType structType = parts.get(0).type; -// -// for (var meshEntry : model.get() -// .entrySet()) { -// Material material = meshEntry.getKey(); -// Mesh mesh = meshEntry.getValue(); -// -// MeshPool.BufferedMesh bufferedMesh = MeshPool.getInstance() -// .get(mesh); -// -// if (bufferedMesh == null || !bufferedMesh.isGpuResident()) { -// continue; -// } -// -// material.getRenderType().setupRenderState(); -// -// CoreShaderInfo coreShaderInfo = CoreShaderInfo.get(); -// -// -// CrumblingProgram program = Contexts.CRUMBLING.getProgram(new ProgramCompiler.Context(Formats.POS_TEX_NORMAL, -// structType.getInstanceShader(), material.getVertexShader(), material.getFragmentShader(), -// coreShaderInfo.getAdjustedAlphaDiscard(), coreShaderInfo.fogType(), -// GameStateRegistry.takeSnapshot())); -// -// program.bind(); -// program.uploadUniforms(camX, camY, camZ, vp, level); -// -// // bufferedMesh.drawInstances(); -// } -// } -// } - - @NotNull - private Map> modelsToParts(Int2ObjectMap>> dataByStage) { - var map = new HashMap>(); - - for (var entry : dataByStage.int2ObjectEntrySet()) { - RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(entry.getIntKey()); - - // something about when we call this means that the textures are not ready for use on the first frame they should appear - if (currentLayer == null) { - continue; - } - - for (var blockEntityInstance : entry.getValue()) { - - for (var part : blockEntityInstance.getCrumblingParts()) { - if (part.getOwner() instanceof GPUInstancer instancer) { - - // queue the instances for copying to the crumbling instance buffer - map.computeIfAbsent(instancer.parent.getModel(), k -> new ArrayList<>()).add(part); - } - } - } - } - return map; - } - - @Nonnull - private Int2ObjectMap>> getDataByStage(LevelRenderer levelRenderer, ClientLevel level) { - var destructionProgress = ((LevelRendererAccessor) levelRenderer).flywheel$getDestructionProgress(); - if (destructionProgress.isEmpty()) { - return Int2ObjectMaps.emptyMap(); - } - - if (!(InstancedRenderDispatcher.getInstanceWorld(level) - .getBlockEntities() instanceof BlockEntityInstanceManager beim)) { - return Int2ObjectMaps.emptyMap(); - } - - var dataByStage = new Int2ObjectArrayMap>>(); - - for (var entry : destructionProgress.long2ObjectEntrySet()) { - SortedSet progresses = entry.getValue(); - - if (progresses == null || progresses.isEmpty()) { - continue; - } - - int progress = progresses.last() - .getProgress(); - - var data = dataByStage.computeIfAbsent(progress, $ -> new ArrayList<>()); - - long pos = entry.getLongKey(); - - beim.getCrumblingInstances(pos, data); - } - - return dataByStage; - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/DrawCall.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/DrawCall.java index 334a42770..e8d856634 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/DrawCall.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/DrawCall.java @@ -4,23 +4,23 @@ import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; -import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.layout.BufferLayout; public class DrawCall { - private final GPUInstancer instancer; - private final Material material; + final GPUInstancer instancer; + final Material material; + private final int meshAttributes; MeshPool.BufferedMesh bufferedMesh; GlVertexArray vao; - DrawCall(GPUInstancer instancer, Material material, Mesh mesh) { + DrawCall(GPUInstancer instancer, Material material, MeshPool.BufferedMesh mesh) { this.instancer = instancer; this.material = material; this.vao = new GlVertexArray(); - this.bufferedMesh = MeshPool.getInstance() - .alloc(mesh); - this.instancer.attributeBaseIndex = this.bufferedMesh.getAttributeCount(); - this.vao.enableArrays(this.bufferedMesh.getAttributeCount() + instancer.instanceFormat.getAttributeCount()); + this.bufferedMesh = mesh; + this.meshAttributes = this.bufferedMesh.getAttributeCount(); + this.vao.enableArrays(this.meshAttributes + instancer.instanceFormat.getAttributeCount()); } public Material getMaterial() { @@ -32,11 +32,15 @@ public class DrawCall { } public void render() { - if (invalid()) return; + if (invalid()) { + return; + } try (var ignored = GlStateTracker.getRestoreState()) { - this.instancer.renderSetup(vao); + this.instancer.update(); + + bindInstancerToVAO(); if (this.instancer.glInstanceCount > 0) { bufferedMesh.drawInstances(vao, this.instancer.glInstanceCount); @@ -55,8 +59,24 @@ public class DrawCall { return this.instancer.vbo == null || bufferedMesh == null || vao == null; } + private void bindInstancerToVAO() { + if (!this.instancer.boundTo.add(vao)) { + return; + } + + var instanceFormat = this.instancer.instanceFormat; + + vao.bindAttributes(this.instancer.vbo, this.meshAttributes, instanceFormat, 0L); + + for (int i = 0; i < instanceFormat.getAttributeCount(); i++) { + vao.setAttributeDivisor(this.meshAttributes + i, 1); + } + } + public void delete() { - if (invalid()) return; + if (invalid()) { + return; + } vao.delete(); bufferedMesh.delete(); 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 a2fc2da05..d3e51e58e 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 @@ -17,19 +17,17 @@ import com.jozufozu.flywheel.core.layout.BufferLayout; public class GPUInstancer extends AbstractInstancer { - public final BufferLayout instanceFormat; - public final StructType structType; - public final InstancedModel parent; - + final BufferLayout instanceFormat; + final StructType structType; + final Set boundTo = new HashSet<>(); GlBuffer vbo; - int attributeBaseIndex; int glInstanceCount = 0; boolean anyToUpdate; - public GPUInstancer(InstancedModel parent, StructType type) { + + public GPUInstancer(StructType type) { super(type); - this.parent = parent; this.instanceFormat = type.getLayout(); this.structType = type; } @@ -40,7 +38,9 @@ public class GPUInstancer extends AbstractInstancer } public void init() { - if (vbo != null) return; + if (vbo != null) { + return; + } vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER, GlBufferUsage.DYNAMIC_DRAW); vbo.setGrowthMargin(instanceFormat.getStride() * 16); @@ -50,17 +50,7 @@ public class GPUInstancer extends AbstractInstancer return !anyToUpdate && !anyToRemove && glInstanceCount == 0; } - private final Set boundTo = new HashSet<>(); - - void renderSetup(GlVertexArray vao) { - update(); - - if (boundTo.add(vao)) { - bindInstanceAttributes(vao); - } - } - - private void update() { + void update() { if (anyToRemove) { removeDeletedInstances(); } @@ -115,14 +105,6 @@ public class GPUInstancer extends AbstractInstancer return vbo.ensureCapacity(requiredSize); } - private void bindInstanceAttributes(GlVertexArray vao) { - vao.bindAttributes(this.vbo, this.attributeBaseIndex, this.instanceFormat, 0L); - - for (int i = 0; i < this.instanceFormat.getAttributeCount(); i++) { - vao.setAttributeDivisor(this.attributeBaseIndex + i, 1); - } - } - @Override public void delete() { vbo.delete(); 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 bae60adbc..4842d2f7e 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 @@ -2,13 +2,13 @@ package com.jozufozu.flywheel.backend.instancing.instancing; import java.util.HashMap; import java.util.Map; +import java.util.function.BiConsumer; import java.util.function.Consumer; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.Instancer; import com.jozufozu.flywheel.api.instancer.InstancerFactory; import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.backend.instancing.AbstractInstancer; import com.jozufozu.flywheel.core.model.Model; /** @@ -17,53 +17,23 @@ import com.jozufozu.flywheel.core.model.Model; */ public class GPUInstancerFactory implements InstancerFactory { - protected final Map> models = new HashMap<>(); + protected final Map> models = new HashMap<>(); protected final StructType type; - private final Consumer> creationListener; + private final BiConsumer, Model> creationListener; - public GPUInstancerFactory(StructType type, Consumer> creationListener) { + public GPUInstancerFactory(StructType type, BiConsumer, Model> creationListener) { this.type = type; this.creationListener = creationListener; } @Override public Instancer model(Model modelKey) { - return models.computeIfAbsent(modelKey, this::createInstancer).getInstancer(); + return models.computeIfAbsent(modelKey, this::createInstancer); } - public int getInstanceCount() { - return models.values() - .stream() - .map(InstancedModel::getInstancer) - .mapToInt(AbstractInstancer::getInstanceCount) - .sum(); - } - - public int getVertexCount() { - return models.values() - .stream() - .mapToInt(InstancedModel::getVertexCount) - .sum(); - } - - public void delete() { - models.values().forEach(InstancedModel::delete); - models.clear(); - } - - /** - * Clear all instance data without freeing resources. - */ - public void clear() { - models.values() - .stream() - .map(InstancedModel::getInstancer) - .forEach(AbstractInstancer::clear); - } - - private InstancedModel createInstancer(Model model) { - var instancer = new InstancedModel<>(type, model); - this.creationListener.accept(instancer); + private GPUInstancer createInstancer(Model model) { + var instancer = new GPUInstancer<>(type); + this.creationListener.accept(instancer, model); return instancer; } } 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 deleted file mode 100644 index 0f67e625a..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancedModel.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.instancing; - -import java.util.List; - -import com.jozufozu.flywheel.api.instancer.InstancedPart; -import com.jozufozu.flywheel.api.struct.StructType; -import com.jozufozu.flywheel.core.model.Model; - -public class InstancedModel { - - private final StructType type; - private final Model model; - private final GPUInstancer instancer; - private List layers; - - public InstancedModel(StructType type, Model model) { - this.type = type; - this.model = model; - this.instancer = new GPUInstancer<>(this, type); - } - - public void init(RenderLists renderLists) { - instancer.init(); - - layers = model.getMeshes() - .entrySet() - .stream() - .map(entry -> new DrawCall(instancer, entry.getKey(), entry.getValue())) - .toList(); - - for (DrawCall layer : layers) { - renderLists.add(new ShaderState(layer.getMaterial(), layer.getVertexType(), type), layer); - } - } - - public Model getModel() { - return model; - } - - public GPUInstancer getInstancer() { - return instancer; - } - - public int getVertexCount() { - return model.getVertexCount() * instancer.glInstanceCount; - } - - public void delete() { - if (instancer.vbo == null) return; - - instancer.delete(); - - for (var layer : layers) { - layer.delete(); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingDrawManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingDrawManager.java new file mode 100644 index 000000000..37fa84cef --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/InstancingDrawManager.java @@ -0,0 +1,125 @@ +package com.jozufozu.flywheel.backend.instancing.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 org.jetbrains.annotations.NotNull; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableListMultimap; +import com.google.common.collect.ListMultimap; +import com.jozufozu.flywheel.api.RenderStage; +import com.jozufozu.flywheel.api.vertex.VertexType; +import com.jozufozu.flywheel.core.model.Mesh; +import com.jozufozu.flywheel.core.model.Model; + +public class InstancingDrawManager { + + private final List uninitializedModels = new ArrayList<>(); + private final List> allInstancers = new ArrayList<>(); + private final Map renderLists = new EnumMap<>(RenderStage.class); + private final Map meshPools = new HashMap<>(); + + public DrawSet get(RenderStage stage) { + return renderLists.getOrDefault(stage, DrawSet.EMPTY); + } + + public void create(GPUInstancer gpuInstancer, Model model) { + uninitializedModels.add(new UninitializedModel(gpuInstancer, model)); + } + + public void flush() { + for (var model : uninitializedModels) { + model.instancer() + .init(); + + add(model.instancer(), model.model()); + } + uninitializedModels.clear(); + + for (var pool : meshPools.values()) { + pool.flush(); + } + } + + public void delete() { + meshPools.values() + .forEach(MeshPool::delete); + meshPools.clear(); + + renderLists.values() + .forEach(DrawSet::delete); + renderLists.clear(); + + allInstancers.forEach(GPUInstancer::delete); + allInstancers.clear(); + } + + public void clearInstancers() { + allInstancers.forEach(GPUInstancer::clear); + } + + private void add(GPUInstancer instancer, Model model) { + var meshes = model.getMeshes(); + for (var entry : meshes.entrySet()) { + DrawCall layer = new DrawCall(instancer, entry.getKey(), alloc(entry.getValue())); + var material = layer.getMaterial(); + var shaderState = new ShaderState(material, layer.getVertexType(), layer.instancer.type); + + renderLists.computeIfAbsent(material.getRenderStage(), DrawSet::new) + .put(shaderState, layer); + } + allInstancers.add(instancer); + } + + private MeshPool.BufferedMesh alloc(Mesh mesh) { + return meshPools.computeIfAbsent(mesh.getVertexType(), MeshPool::new) + .alloc(mesh); + } + + public static class DrawSet implements Iterable>> { + + public static final DrawSet EMPTY = new DrawSet(ImmutableListMultimap.of()); + + 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 layer) { + drawCalls.put(shaderState, layer); + } + + public boolean isEmpty() { + return drawCalls.isEmpty(); + } + + @NotNull + @Override + public Iterator>> iterator() { + return drawCalls.asMap() + .entrySet() + .iterator(); + } + } + + private record UninitializedModel(GPUInstancer instancer, Model model) { + + } +} 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 43d1acd41..81b9382c7 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 @@ -1,6 +1,5 @@ package com.jozufozu.flywheel.backend.instancing.instancing; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -8,7 +7,6 @@ import java.util.Map; import org.jetbrains.annotations.NotNull; import org.lwjgl.opengl.GL32; -import com.google.common.collect.ListMultimap; import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.material.Material; @@ -43,8 +41,7 @@ public class InstancingEngine implements Engine { protected final Map, GPUInstancerFactory> factories = new HashMap<>(); - protected final List> uninitializedModels = new ArrayList<>(); - protected final RenderLists renderLists = new RenderLists(); + protected final InstancingDrawManager drawManager = new InstancingDrawManager(); /** * The set of instance managers that are attached to this engine. @@ -66,30 +63,20 @@ public class InstancingEngine implements Engine { @NotNull private GPUInstancerFactory createFactory(StructType type) { - return new GPUInstancerFactory<>(type, uninitializedModels::add); + return new GPUInstancerFactory<>(type, drawManager::create); } @Override public void renderStage(TaskEngine taskEngine, RenderContext context, RenderStage stage) { - var multimap = renderLists.get(stage); + var drawSet = drawManager.get(stage); - setup(); - - render(multimap); - } - - // TODO: Is this useful? Should it be added to the base interface? Currently it is only used for the old CrumblingRenderer. - @Deprecated - public void renderAll(TaskEngine taskEngine, RenderContext context) { - if (renderLists.isEmpty()) { + if (drawSet.isEmpty()) { return; } setup(); - for (var multimap : renderLists.getAll()) { - render(multimap); - } + render(drawSet); } private void setup() { @@ -103,12 +90,12 @@ public class InstancingEngine implements Engine { RenderSystem.enableCull(); } - protected void render(ListMultimap multimap) { - if (multimap.isEmpty()) { + protected void render(InstancingDrawManager.DrawSet drawSet) { + if (drawSet.isEmpty()) { return; } - for (var entry : multimap.asMap().entrySet()) { + for (var entry : drawSet) { var shader = entry.getKey(); var drawCalls = entry.getValue(); @@ -144,16 +131,10 @@ public class InstancingEngine implements Engine { UniformBuffer.getInstance().sync(); } - public void clearAll() { - factories.values().forEach(GPUInstancerFactory::clear); - } - @Override public void delete() { - factories.values() - .forEach(GPUInstancerFactory::delete); - factories.clear(); + drawManager.delete(); } @Override @@ -183,19 +164,13 @@ public class InstancingEngine implements Engine { @Override public void beginFrame(TaskEngine taskEngine, RenderContext context) { - for (var model : uninitializedModels) { - model.init(renderLists); - } - uninitializedModels.clear(); - - MeshPool.getInstance() - .flush(); + drawManager.flush(); } private void shiftListeners(int cX, int cY, int cZ) { originCoordinate = new BlockPos(cX, cY, cZ); - factories.values().forEach(GPUInstancerFactory::clear); + drawManager.clearInstancers(); instanceManagers.forEach(InstanceManager::onOriginShift); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java index f23b647d1..7e8d82640 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/MeshPool.java @@ -17,28 +17,12 @@ import com.jozufozu.flywheel.backend.gl.array.GlVertexArray; import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer; import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType; import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer; -import com.jozufozu.flywheel.core.layout.BufferLayout; import com.jozufozu.flywheel.core.model.Mesh; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; public class MeshPool { - private static MeshPool allocator; - - public static MeshPool getInstance() { - if (allocator == null) { - allocator = new MeshPool(); - } - return allocator; - } - - public static void reset(ReloadRenderersEvent ignored) { - if (allocator != null) { - allocator.delete(); - allocator = null; - } - } + private final VertexType vertexType; private final Map meshes = new HashMap<>(); private final List allBuffered = new ArrayList<>(); @@ -54,10 +38,12 @@ public class MeshPool { /** * Create a new mesh pool. */ - public MeshPool() { - vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); + public MeshPool(VertexType vertexType) { + this.vertexType = vertexType; + int stride = vertexType.getStride(); + this.vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER); - vbo.setGrowthMargin(2048); + this.vbo.setGrowthMargin(stride * 32); } /** @@ -68,6 +54,10 @@ public class MeshPool { */ public BufferedMesh alloc(Mesh mesh) { return meshes.computeIfAbsent(mesh, m -> { + if (m.getVertexType() != vertexType) { + throw new IllegalArgumentException("Mesh has wrong vertex type"); + } + BufferedMesh bufferedModel = new BufferedMesh(m, byteSize); byteSize += m.size(); allBuffered.add(bufferedModel); @@ -173,25 +163,24 @@ public class MeshPool { pendingUpload.clear(); } + @Override + public String toString() { + return "MeshPool{" + "vertexType=" + vertexType + ", byteSize=" + byteSize + ", meshCount=" + meshes.size() + '}'; + } + public class BufferedMesh { private final ElementBuffer ebo; private final Mesh mesh; - private final BufferLayout layout; private long byteIndex; - private boolean deleted; - private boolean gpuResident = false; - private final Set boundTo = new HashSet<>(); public BufferedMesh(Mesh mesh, long byteIndex) { this.mesh = mesh; this.byteIndex = byteIndex; this.ebo = mesh.createEBO(); - this.layout = mesh.getVertexType() - .getLayout(); } public void drawCall(GlVertexArray vao) { @@ -199,7 +188,9 @@ public class MeshPool { } public void drawInstances(GlVertexArray vao, int instanceCount) { - if (hasAnythingToRender()) return; + if (hasAnythingToRender()) { + return; + } setup(vao); @@ -221,7 +212,7 @@ public class MeshPool { private void setup(GlVertexArray vao) { if (this.boundTo.add(vao)) { vao.enableArrays(getAttributeCount()); - vao.bindAttributes(MeshPool.this.vbo, 0, this.layout, this.byteIndex); + vao.bindAttributes(MeshPool.this.vbo, 0, vertexType.getLayout(), this.byteIndex); } vao.bindElementArray(this.ebo.buffer); vao.bind(); @@ -241,19 +232,14 @@ public class MeshPool { this.mesh.write(ptr + byteIndex); this.boundTo.clear(); - this.gpuResident = true; } public int getAttributeCount() { - return this.layout.getAttributeCount(); - } - - public boolean isGpuResident() { - return gpuResident; + return vertexType.getLayout().getAttributeCount(); } public VertexType getVertexType() { - return this.mesh.getVertexType(); + return vertexType; } } 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 deleted file mode 100644 index eef738b3f..000000000 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/instancing/RenderLists.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.jozufozu.flywheel.backend.instancing.instancing; - -import java.util.Collection; -import java.util.EnumMap; -import java.util.Map; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ListMultimap; -import com.jozufozu.flywheel.api.RenderStage; -import com.jozufozu.flywheel.api.material.Material; - -public class RenderLists { - - public final Map> renderLists = new EnumMap<>(RenderStage.class); - - public ListMultimap get(RenderStage stage) { - var renderList = renderLists.get(stage); - if (renderList == null) { - return ImmutableListMultimap.of(); - } - return renderList; - } - - public void add(ShaderState shaderState, DrawCall layer) { - Material material = shaderState.material(); - - renderLists.computeIfAbsent(material.getRenderStage(), k -> ArrayListMultimap.create()) - .put(shaderState, layer); - } - - public boolean isEmpty() { - return renderLists.isEmpty(); - } - - public Collection> getAll() { - return renderLists.values(); - } -} diff --git a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java b/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java deleted file mode 100644 index 7459758f5..000000000 --- a/src/main/java/com/jozufozu/flywheel/core/crumbling/CrumblingRenderer.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.jozufozu.flywheel.core.crumbling; - -import java.util.ArrayList; -import java.util.List; -import java.util.SortedSet; - -import com.jozufozu.flywheel.backend.Backend; -import com.jozufozu.flywheel.backend.gl.GlStateTracker; -import com.jozufozu.flywheel.backend.instancing.InstanceManager; -import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine; -import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine; -import com.jozufozu.flywheel.core.Components; -import com.jozufozu.flywheel.core.RenderContext; -import com.jozufozu.flywheel.event.ReloadRenderersEvent; -import com.jozufozu.flywheel.mixin.LevelRendererAccessor; -import com.jozufozu.flywheel.util.Lazy; - -import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; -import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -import net.minecraft.client.multiplayer.ClientLevel; -import net.minecraft.client.renderer.LevelRenderer; -import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.resources.model.ModelBakery; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.BlockDestructionProgress; -import net.minecraft.world.level.block.entity.BlockEntity; - -// TODO: merge directly into InstancingEngine for efficiency -/** - * Responsible for rendering the crumbling overlay for instanced block entities. - */ -public class CrumblingRenderer { - - private static Lazy STATE; - - static { - _init(); - } - - public static void renderCrumbling(RenderContext context) { - // TODO: one pass base/crumbling - if (true) return; - - Int2ObjectMap> activeStages = getActiveStageBlockEntities(context.renderer(), context.level()); - if (activeStages.isEmpty()) return; - - try (var restoreState = GlStateTracker.getRestoreState()) { - State state = STATE.get(); - var instanceManager = state.instanceManager; - var engine = state.instancerManager; - - renderCrumblingInner(activeStages, instanceManager, engine, context); - } - } - - private static void renderCrumblingInner(Int2ObjectMap> activeStages, InstanceManager instanceManager, CrumblingEngine engine, RenderContext ctx) { - for (Int2ObjectMap.Entry> stage : activeStages.int2ObjectEntrySet()) { - RenderType currentLayer = ModelBakery.DESTROY_TYPES.get(stage.getIntKey()); - - // something about when we call this means that the textures are not ready for use on the first frame they should appear - if (currentLayer != null) { - stage.getValue().forEach(instanceManager::add); - - instanceManager.beginFrame(SerialTaskEngine.INSTANCE, ctx); - engine.beginFrame(SerialTaskEngine.INSTANCE, ctx); - - engine.renderAll(SerialTaskEngine.INSTANCE, ctx); - - instanceManager.invalidate(); - } - } - } - - /** - * Associate each breaking stage with a list of all block entities at that stage. - */ - private static Int2ObjectMap> getActiveStageBlockEntities(LevelRenderer levelRenderer, ClientLevel level) { - Long2ObjectMap> destructionProgress = ((LevelRendererAccessor) levelRenderer).flywheel$getDestructionProgress(); - if (destructionProgress.isEmpty()) { - return Int2ObjectMaps.emptyMap(); - } - - Int2ObjectMap> breakingEntities = new Int2ObjectArrayMap<>(); - BlockPos.MutableBlockPos breakingPos = new BlockPos.MutableBlockPos(); - - for (Long2ObjectMap.Entry> entry : destructionProgress.long2ObjectEntrySet()) { - breakingPos.set(entry.getLongKey()); - - SortedSet progresses = entry.getValue(); - if (progresses != null && !progresses.isEmpty()) { - int progress = progresses.last() - .getProgress(); - if (progress >= 0) { - BlockEntity blockEntity = level.getBlockEntity(breakingPos); - - if (blockEntity != null) { - List blockEntities = breakingEntities.computeIfAbsent(progress, $ -> new ArrayList<>()); - blockEntities.add(blockEntity); - } - } - } - } - - return breakingEntities; - } - - public static void onReloadRenderers(ReloadRenderersEvent event) { - ClientLevel world = event.getWorld(); - if (Backend.isOn() && world != null) { - reset(); - } - } - - public static void reset() { - STATE.ifPresent(State::kill); - _init(); - } - - private static void _init() { - STATE = Lazy.of(State::new); - } - - private static class State { - private final CrumblingEngine instancerManager; - private final InstanceManager instanceManager; - - private State() { - instancerManager = new CrumblingEngine(); - instanceManager = new CrumblingInstanceManager(instancerManager); - instancerManager.attachManagers(instanceManager); - } - - private void kill() { - instancerManager.delete(); - instanceManager.invalidate(); - } - } - - private static class CrumblingEngine extends InstancingEngine { - public CrumblingEngine() { - super(Components.CRUMBLING); - } - } -} diff --git a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java index 89952c908..43f362861 100644 --- a/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java +++ b/src/main/java/com/jozufozu/flywheel/mixin/LevelRendererMixin.java @@ -14,7 +14,6 @@ import com.jozufozu.flywheel.api.RenderStage; import com.jozufozu.flywheel.backend.Backend; import com.jozufozu.flywheel.backend.gl.GlStateTracker; import com.jozufozu.flywheel.core.RenderContext; -import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer; import com.jozufozu.flywheel.event.BeginFrameEvent; import com.jozufozu.flywheel.event.ReloadRenderersEvent; import com.jozufozu.flywheel.event.RenderStageEvent; @@ -66,7 +65,7 @@ public class LevelRendererMixin { ), method = "renderLevel") private void renderCrumbling(PoseStack poseStack, float partialTick, long finishNanoTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightTexture, Matrix4f projectionMatrix, CallbackInfo ci) { if (renderContext != null) { - CrumblingRenderer.renderCrumbling(renderContext); + // TODO: Crumbling } }