Skip to content

Commit

Permalink
cache multi draw batches on regions
Browse files Browse the repository at this point in the history
  • Loading branch information
douira committed Feb 11, 2025
1 parent 961747b commit 73caf78
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public void multiDrawElementsBaseVertex(MultiDrawBatch batch, GlIndexType indexT
batch.pElementCount,
indexType.getFormatId(),
batch.pElementPointer,
batch.size(),
batch.size,
batch.pBaseVertex);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,20 @@ 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);
MemoryUtil.memSet(this.pElementPointer, 0x0, (long) capacity * Pointer.POINTER_SIZE);

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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,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);
}

Expand Down Expand Up @@ -71,16 +68,19 @@ public void render(ChunkRenderMatrices matrices,
continue;
}

fillCommandBuffer(this.batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling, useIndexedTessellation);
var batch = region.getCachedBatch(renderPass);
if (!batch.isFilled) {
fillCommandBuffer(batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling, useIndexedTessellation);
}

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;
Expand All @@ -92,7 +92,7 @@ public void render(ChunkRenderMatrices matrices,
}

setModelMatrixUniforms(shader, region, camera);
executeDrawBatch(commandList, tessellation, this.batch);
executeDrawBatch(commandList, tessellation, batch);
}

super.end(renderPass);
Expand All @@ -110,7 +110,7 @@ private static void fillCommandBuffer(MultiDrawBatch batch,
TerrainRenderPass pass,
boolean useBlockFaceCulling,
boolean useIndexedTessellation) {
batch.clear();
batch.isFilled = true;

var iterator = renderList.sectionsWithGeometryIterator(pass.isTranslucent());

Expand Down Expand Up @@ -362,6 +362,5 @@ public void delete(CommandList commandList) {
super.delete(commandList);

this.sharedIndexBuffer.delete(commandList);
this.batch.delete();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -106,14 +106,15 @@ public void setIndexData(int localSectionIndex, GlBufferSegment allocation) {
SectionRenderDataUnsafe.setLocalBaseElement(pMeshData, allocation.getOffset());
}

public void setSharedIndexUsage(int localSectionIndex, int newUsage) {
public boolean setSharedIndexUsage(int localSectionIndex, int newUsage) {
var previousUsage = this.sharedIndexUsage[localSectionIndex];
if (previousUsage == newUsage) {
return;
return false;
}

// mark for update if usage is down from max (may need to shrink buffer)
// or if usage increased beyond the max (need to grow buffer)
boolean newlyUsingSharedIndexBuffer = false;
if (newUsage < previousUsage && previousUsage == this.sharedIndexCapacity ||
newUsage > this.sharedIndexCapacity ||
newUsage > 0 && this.sharedIndexAllocation == null) {
Expand All @@ -123,9 +124,15 @@ public void setSharedIndexUsage(int localSectionIndex, int newUsage) {
var sharedBaseElement = this.sharedIndexAllocation.getOffset();
var pMeshData = this.getDataPointer(localSectionIndex);
SectionRenderDataUnsafe.setSharedBaseElement(pMeshData, sharedBaseElement);

if (previousUsage == 0 && newUsage > 0) {
newlyUsingSharedIndexBuffer = true;
}
}

this.sharedIndexUsage[localSectionIndex] = newUsage;

return newlyUsingSharedIndexBuffer;
}

public boolean needsSharedIndexUpdate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,6 +33,8 @@ public ChunkRenderList(RenderRegion region) {
}

public void reset(int frame) {
this.prevSectionsWithGeometryCount = this.sectionsWithGeometryCount;

this.sectionsWithGeometryCount = 0;
this.sectionsWithSpritesCount = 0;
this.sectionsWithEntitiesCount = 0;
Expand Down Expand Up @@ -83,8 +86,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;
Expand All @@ -93,6 +102,12 @@ public void add(RenderSection render) {
this.sectionsWithEntitiesCount += (flags >>> RenderSectionFlags.HAS_BLOCK_ENTITIES) & 1;
}

public boolean getAndResetCacheInvalidation() {
var cacheIsInvalidated = this.prevSectionsWithGeometryCount != this.sectionsWithGeometryCount;
this.prevSectionsWithGeometryCount = this.sectionsWithGeometryCount;
return cacheIsInvalidated;
}

public @Nullable ByteIterator sectionsWithGeometryIterator(boolean reverse) {
if (this.sectionsWithGeometryCount == 0) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -51,6 +53,8 @@ public class RenderRegion {
private final Map<TerrainRenderPass, SectionRenderDataStorage> sectionRenderData = new Reference2ReferenceOpenHashMap<>();
private DeviceResources resources;

private final Map<TerrainRenderPass, MultiDrawBatch> cachedBatches = new Reference2ReferenceOpenHashMap<>();

public RenderRegion(int x, int y, int z, StagingBuffer stagingBuffer) {
this.x = x;
this.y = y;
Expand Down Expand Up @@ -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.getAndResetCacheInvalidation()) {
batch.clear();
}
return batch;
}

batch = new MultiDrawBatch((ModelQuadFacing.COUNT * RenderRegion.REGION_SIZE) + 1);
this.cachedBatches.put(pass, batch);
return batch;
}

public boolean isEmpty() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;

import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.SharedIndexSorter;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.SharedIndexSorter;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

public class RenderRegionManager {
private final Long2ReferenceOpenHashMap<RenderRegion> regions = new Long2ReferenceOpenHashMap<>();
Expand Down Expand Up @@ -77,6 +79,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect

if (storage != null) {
storage.removeVertexData(renderSectionIndex);
region.clearCachedBatchFor(pass);
}

BuiltSectionMeshParts mesh = chunkBuildOutput.getMesh(pass);
Expand All @@ -93,12 +96,20 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect
if (sorter instanceof SharedIndexSorter sharedIndexSorter) {
var storage = region.createStorage(DefaultTerrainRenderPasses.TRANSLUCENT);
storage.removeIndexData(renderSectionIndex);
storage.setSharedIndexUsage(renderSectionIndex, sharedIndexSorter.quadCount());

// clear batch cache if it's newly using the shared index buffer and was not previously.
// updates to the shared index buffer which cause the batch cache to be invalidated are handled with needsSharedIndexUpdate
if (storage.setSharedIndexUsage(renderSectionIndex, sharedIndexSorter.quadCount())) {
region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT);
}
} else {
var storage = region.getStorage(DefaultTerrainRenderPasses.TRANSLUCENT);
if (storage != null) {
storage.removeIndexData(renderSectionIndex);
storage.setSharedIndexUsage(renderSectionIndex, 0);

// always clear batch cache on uploads of new index data
region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT);
}

if (sorter == null) {
Expand Down Expand Up @@ -137,6 +148,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
Expand Down Expand Up @@ -167,6 +179,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect

if (indexBufferChanged) {
region.refreshIndexedTesselation(commandList);
region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT);
}

profiler.pop();
Expand Down

0 comments on commit 73caf78

Please sign in to comment.