From d6785606247417dead7757ae0e9b315efbc9f5e8 Mon Sep 17 00:00:00 2001 From: douira Date: Mon, 23 Sep 2024 02:49:09 +0200 Subject: [PATCH] cache multi draw batches on regions --- .../client/gl/device/GLRenderDevice.java | 2 +- .../client/gl/device/MultiDrawBatch.java | 14 ++------ .../render/chunk/DefaultChunkRenderer.java | 17 +++++---- .../render/chunk/lists/ChunkRenderList.java | 18 ++++++++-- .../render/chunk/region/RenderRegion.java | 36 +++++++++++++++++++ .../chunk/region/RenderRegionManager.java | 4 +++ 6 files changed, 67 insertions(+), 24 deletions(-) diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/GLRenderDevice.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/GLRenderDevice.java index 98af1f3ba3..237d49cc6e 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/GLRenderDevice.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/GLRenderDevice.java @@ -274,7 +274,7 @@ public void multiDrawElementsBaseVertex(MultiDrawBatch batch, GlIndexType indexT batch.pElementCount, indexType.getFormatId(), batch.pElementPointer, - batch.size(), + batch.size, batch.pBaseVertex); } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java index ceda21721a..7169f4bf26 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java @@ -14,9 +14,8 @@ public final class MultiDrawBatch { public final long pElementCount; public final long pBaseVertex; - private final int capacity; - public int size; + public boolean isFilled; public MultiDrawBatch(int capacity) { this.pElementPointer = MemoryUtil.nmemAlignedAlloc(32, (long) capacity * Pointer.POINTER_SIZE); @@ -24,20 +23,11 @@ public MultiDrawBatch(int capacity) { this.pElementCount = MemoryUtil.nmemAlignedAlloc(32, (long) capacity * Integer.BYTES); this.pBaseVertex = MemoryUtil.nmemAlignedAlloc(32, (long) capacity * Integer.BYTES); - - this.capacity = capacity; - } - - public int size() { - return this.size; - } - - public int capacity() { - return this.capacity; } public void clear() { this.size = 0; + this.isFilled = false; } public void delete() { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java index e273e61744..910500c9f7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java @@ -29,14 +29,11 @@ import java.util.Iterator; public class DefaultChunkRenderer extends ShaderChunkRenderer { - private final MultiDrawBatch batch; - private final SharedQuadIndexBuffer sharedIndexBuffer; public DefaultChunkRenderer(RenderDevice device, ChunkVertexType vertexType) { super(device, vertexType); - this.batch = new MultiDrawBatch((ModelQuadFacing.COUNT * RenderRegion.REGION_SIZE) + 1); this.sharedIndexBuffer = new SharedQuadIndexBuffer(device.createCommandList(), SharedQuadIndexBuffer.IndexType.INTEGER); } @@ -72,16 +69,19 @@ public void render(ChunkRenderMatrices matrices, continue; } - fillCommandBuffer(this.batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling); + var batch = region.getCachedBatch(renderPass); + if (!batch.isFilled) { + fillCommandBuffer(batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling); + } - if (this.batch.isEmpty()) { + if (batch.isEmpty()) { continue; } // When the shared index buffer is being used, we must ensure the storage has been allocated *before* // the tessellation is prepared. if (!useIndexedTessellation) { - this.sharedIndexBuffer.ensureCapacity(commandList, this.batch.getIndexBufferSize()); + this.sharedIndexBuffer.ensureCapacity(commandList, batch.getIndexBufferSize()); } GlTessellation tessellation; @@ -93,7 +93,7 @@ public void render(ChunkRenderMatrices matrices, } setModelMatrixUniforms(shader, region, camera); - executeDrawBatch(commandList, tessellation, this.batch); + executeDrawBatch(commandList, tessellation, batch); } super.end(renderPass); @@ -110,7 +110,7 @@ private static void fillCommandBuffer(MultiDrawBatch batch, CameraTransform camera, TerrainRenderPass pass, boolean useBlockFaceCulling) { - batch.clear(); + batch.isFilled = true; var iterator = renderList.sectionsWithGeometryIterator(pass.isTranslucent()); @@ -321,6 +321,5 @@ public void delete(CommandList commandList) { super.delete(commandList); this.sharedIndexBuffer.delete(commandList); - this.batch.delete(); } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java index 6212573773..5ee59f7e8d 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java @@ -16,6 +16,7 @@ public class ChunkRenderList { private final byte[] sectionsWithGeometry = new byte[RenderRegion.REGION_SIZE]; private int sectionsWithGeometryCount = 0; + private int prevSectionsWithGeometryCount = 0; private final byte[] sectionsWithSprites = new byte[RenderRegion.REGION_SIZE]; private int sectionsWithSpritesCount = 0; @@ -32,6 +33,9 @@ public ChunkRenderList(RenderRegion region) { } public void reset(int frame) { + // TODO: benchmark overall improvement and test if partial batch writing is worth it + this.prevSectionsWithGeometryCount = this.sectionsWithGeometryCount; + this.sectionsWithGeometryCount = 0; this.sectionsWithSpritesCount = 0; this.sectionsWithEntitiesCount = 0; @@ -83,8 +87,14 @@ public void add(RenderSection render) { int index = render.getSectionIndex(); int flags = render.getFlags(); - this.sectionsWithGeometry[this.sectionsWithGeometryCount] = (byte) index; - this.sectionsWithGeometryCount += (flags >>> RenderSectionFlags.HAS_BLOCK_GEOMETRY) & 1; + if (((flags >>> RenderSectionFlags.HAS_BLOCK_GEOMETRY) & 1) != 0) { + var byteIndex = (byte) index; + if (this.sectionsWithGeometry[this.sectionsWithGeometryCount] != byteIndex) { + this.sectionsWithGeometry[this.sectionsWithGeometryCount] = byteIndex; + this.prevSectionsWithGeometryCount = -1; + } + this.sectionsWithGeometryCount++; + } this.sectionsWithSprites[this.sectionsWithSpritesCount] = (byte) index; this.sectionsWithSpritesCount += (flags >>> RenderSectionFlags.HAS_ANIMATED_SPRITES) & 1; @@ -93,6 +103,10 @@ public void add(RenderSection render) { this.sectionsWithEntitiesCount += (flags >>> RenderSectionFlags.HAS_BLOCK_ENTITIES) & 1; } + public boolean isCacheInvalidated() { + return this.prevSectionsWithGeometryCount != this.sectionsWithGeometryCount; + } + public @Nullable ByteIterator sectionsWithGeometryIterator(boolean reverse) { if (this.sectionsWithGeometryCount == 0) { return null; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java index 29ca64b226..e9f24c702a 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java @@ -5,7 +5,9 @@ import net.caffeinemc.mods.sodium.client.gl.arena.staging.StagingBuffer; import net.caffeinemc.mods.sodium.client.gl.buffer.GlBuffer; import net.caffeinemc.mods.sodium.client.gl.device.CommandList; +import net.caffeinemc.mods.sodium.client.gl.device.MultiDrawBatch; import net.caffeinemc.mods.sodium.client.gl.tessellation.GlTessellation; +import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.data.SectionRenderDataStorage; import net.caffeinemc.mods.sodium.client.render.chunk.lists.ChunkRenderList; @@ -51,6 +53,8 @@ public class RenderRegion { private final Map sectionRenderData = new Reference2ReferenceOpenHashMap<>(); private DeviceResources resources; + private final Map cachedBatches = new Reference2ReferenceOpenHashMap<>(); + public RenderRegion(int x, int y, int z, StagingBuffer stagingBuffer) { this.x = x; this.y = y; @@ -113,6 +117,38 @@ public void delete(CommandList commandList) { } Arrays.fill(this.sections, null); + + for (var batch : this.cachedBatches.values()) { + batch.delete(); + } + this.cachedBatches.clear(); + } + + public void clearAllCachedBatches() { + for (var batch : this.cachedBatches.values()) { + batch.clear(); + } + } + + public void clearCachedBatchFor(TerrainRenderPass pass) { + var batch = this.cachedBatches.remove(pass); + if (batch != null) { + batch.delete(); + } + } + + public MultiDrawBatch getCachedBatch(TerrainRenderPass pass) { + MultiDrawBatch batch = this.cachedBatches.get(pass); + if (batch != null) { + if (this.renderList.isCacheInvalidated()) { + batch.clear(); + } + return batch; + } + + batch = new MultiDrawBatch((ModelQuadFacing.COUNT * RenderRegion.REGION_SIZE) + 1); + this.cachedBatches.put(pass, batch); + return batch; } public boolean isEmpty() { diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java index 2b4c704256..fe45924c78 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java @@ -76,6 +76,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect if (storage != null) { storage.removeVertexData(renderSectionIndex); + region.clearCachedBatchFor(pass); } BuiltSectionMeshParts mesh = chunkBuildOutput.getMesh(pass); @@ -100,6 +101,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect var storage = region.getStorage(DefaultTerrainRenderPasses.TRANSLUCENT); if (storage != null) { storage.removeIndexData(renderSectionIndex); + region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT); } } } @@ -124,6 +126,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect // Once invalidated the tessellation will be re-created on the next attempted use if (bufferChanged) { region.refreshTesselation(commandList); + region.clearAllCachedBatches(); } // Collect the upload results @@ -143,6 +146,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect if (bufferChanged) { region.refreshIndexedTesselation(commandList); + region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT); } for (PendingSectionIndexBufferUpload upload : indexUploads) {