Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache multi draw batches on regions #2771

Merged
merged 1 commit into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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