Skip to content

Commit 73caf78

Browse files
committed
cache multi draw batches on regions
1 parent 961747b commit 73caf78

File tree

7 files changed

+90
-30
lines changed

7 files changed

+90
-30
lines changed

common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/GLRenderDevice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ public void multiDrawElementsBaseVertex(MultiDrawBatch batch, GlIndexType indexT
274274
batch.pElementCount,
275275
indexType.getFormatId(),
276276
batch.pElementPointer,
277-
batch.size(),
277+
batch.size,
278278
batch.pBaseVertex);
279279
}
280280

common/src/main/java/net/caffeinemc/mods/sodium/client/gl/device/MultiDrawBatch.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,20 @@ public final class MultiDrawBatch {
1414
public final long pElementCount;
1515
public final long pBaseVertex;
1616

17-
private final int capacity;
18-
1917
public int size;
18+
public boolean isFilled;
2019

2120
public MultiDrawBatch(int capacity) {
2221
this.pElementPointer = MemoryUtil.nmemAlignedAlloc(32, (long) capacity * Pointer.POINTER_SIZE);
2322
MemoryUtil.memSet(this.pElementPointer, 0x0, (long) capacity * Pointer.POINTER_SIZE);
2423

2524
this.pElementCount = MemoryUtil.nmemAlignedAlloc(32, (long) capacity * Integer.BYTES);
2625
this.pBaseVertex = MemoryUtil.nmemAlignedAlloc(32, (long) capacity * Integer.BYTES);
27-
28-
this.capacity = capacity;
29-
}
30-
31-
public int size() {
32-
return this.size;
33-
}
34-
35-
public int capacity() {
36-
return this.capacity;
3726
}
3827

3928
public void clear() {
4029
this.size = 0;
30+
this.isFilled = false;
4131
}
4232

4333
public void delete() {

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/DefaultChunkRenderer.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,11 @@
2828
import java.util.Iterator;
2929

3030
public class DefaultChunkRenderer extends ShaderChunkRenderer {
31-
private final MultiDrawBatch batch;
32-
3331
private final SharedQuadIndexBuffer sharedIndexBuffer;
3432

3533
public DefaultChunkRenderer(RenderDevice device, ChunkVertexType vertexType) {
3634
super(device, vertexType);
3735

38-
this.batch = new MultiDrawBatch((ModelQuadFacing.COUNT * RenderRegion.REGION_SIZE) + 1);
3936
this.sharedIndexBuffer = new SharedQuadIndexBuffer(device.createCommandList(), SharedQuadIndexBuffer.IndexType.INTEGER);
4037
}
4138

@@ -71,16 +68,19 @@ public void render(ChunkRenderMatrices matrices,
7168
continue;
7269
}
7370

74-
fillCommandBuffer(this.batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling, useIndexedTessellation);
71+
var batch = region.getCachedBatch(renderPass);
72+
if (!batch.isFilled) {
73+
fillCommandBuffer(batch, region, storage, renderList, camera, renderPass, useBlockFaceCulling, useIndexedTessellation);
74+
}
7575

76-
if (this.batch.isEmpty()) {
76+
if (batch.isEmpty()) {
7777
continue;
7878
}
7979

8080
// When the shared index buffer is being used, we must ensure the storage has been allocated *before*
8181
// the tessellation is prepared.
8282
if (!useIndexedTessellation) {
83-
this.sharedIndexBuffer.ensureCapacity(commandList, this.batch.getIndexBufferSize());
83+
this.sharedIndexBuffer.ensureCapacity(commandList, batch.getIndexBufferSize());
8484
}
8585

8686
GlTessellation tessellation;
@@ -92,7 +92,7 @@ public void render(ChunkRenderMatrices matrices,
9292
}
9393

9494
setModelMatrixUniforms(shader, region, camera);
95-
executeDrawBatch(commandList, tessellation, this.batch);
95+
executeDrawBatch(commandList, tessellation, batch);
9696
}
9797

9898
super.end(renderPass);
@@ -110,7 +110,7 @@ private static void fillCommandBuffer(MultiDrawBatch batch,
110110
TerrainRenderPass pass,
111111
boolean useBlockFaceCulling,
112112
boolean useIndexedTessellation) {
113-
batch.clear();
113+
batch.isFilled = true;
114114

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

@@ -362,6 +362,5 @@ public void delete(CommandList commandList) {
362362
super.delete(commandList);
363363

364364
this.sharedIndexBuffer.delete(commandList);
365-
this.batch.delete();
366365
}
367366
}

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/data/SectionRenderDataStorage.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,15 @@ public void setIndexData(int localSectionIndex, GlBufferSegment allocation) {
106106
SectionRenderDataUnsafe.setLocalBaseElement(pMeshData, allocation.getOffset());
107107
}
108108

109-
public void setSharedIndexUsage(int localSectionIndex, int newUsage) {
109+
public boolean setSharedIndexUsage(int localSectionIndex, int newUsage) {
110110
var previousUsage = this.sharedIndexUsage[localSectionIndex];
111111
if (previousUsage == newUsage) {
112-
return;
112+
return false;
113113
}
114114

115115
// mark for update if usage is down from max (may need to shrink buffer)
116116
// or if usage increased beyond the max (need to grow buffer)
117+
boolean newlyUsingSharedIndexBuffer = false;
117118
if (newUsage < previousUsage && previousUsage == this.sharedIndexCapacity ||
118119
newUsage > this.sharedIndexCapacity ||
119120
newUsage > 0 && this.sharedIndexAllocation == null) {
@@ -123,9 +124,15 @@ public void setSharedIndexUsage(int localSectionIndex, int newUsage) {
123124
var sharedBaseElement = this.sharedIndexAllocation.getOffset();
124125
var pMeshData = this.getDataPointer(localSectionIndex);
125126
SectionRenderDataUnsafe.setSharedBaseElement(pMeshData, sharedBaseElement);
127+
128+
if (previousUsage == 0 && newUsage > 0) {
129+
newlyUsingSharedIndexBuffer = true;
130+
}
126131
}
127132

128133
this.sharedIndexUsage[localSectionIndex] = newUsage;
134+
135+
return newlyUsingSharedIndexBuffer;
129136
}
130137

131138
public boolean needsSharedIndexUpdate() {

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class ChunkRenderList {
1616

1717
private final byte[] sectionsWithGeometry = new byte[RenderRegion.REGION_SIZE];
1818
private int sectionsWithGeometryCount = 0;
19+
private int prevSectionsWithGeometryCount = 0;
1920

2021
private final byte[] sectionsWithSprites = new byte[RenderRegion.REGION_SIZE];
2122
private int sectionsWithSpritesCount = 0;
@@ -32,6 +33,8 @@ public ChunkRenderList(RenderRegion region) {
3233
}
3334

3435
public void reset(int frame) {
36+
this.prevSectionsWithGeometryCount = this.sectionsWithGeometryCount;
37+
3538
this.sectionsWithGeometryCount = 0;
3639
this.sectionsWithSpritesCount = 0;
3740
this.sectionsWithEntitiesCount = 0;
@@ -83,8 +86,14 @@ public void add(RenderSection render) {
8386
int index = render.getSectionIndex();
8487
int flags = render.getFlags();
8588

86-
this.sectionsWithGeometry[this.sectionsWithGeometryCount] = (byte) index;
87-
this.sectionsWithGeometryCount += (flags >>> RenderSectionFlags.HAS_BLOCK_GEOMETRY) & 1;
89+
if (((flags >>> RenderSectionFlags.HAS_BLOCK_GEOMETRY) & 1) != 0) {
90+
var byteIndex = (byte) index;
91+
if (this.sectionsWithGeometry[this.sectionsWithGeometryCount] != byteIndex) {
92+
this.sectionsWithGeometry[this.sectionsWithGeometryCount] = byteIndex;
93+
this.prevSectionsWithGeometryCount = -1;
94+
}
95+
this.sectionsWithGeometryCount++;
96+
}
8897

8998
this.sectionsWithSprites[this.sectionsWithSpritesCount] = (byte) index;
9099
this.sectionsWithSpritesCount += (flags >>> RenderSectionFlags.HAS_ANIMATED_SPRITES) & 1;
@@ -93,6 +102,12 @@ public void add(RenderSection render) {
93102
this.sectionsWithEntitiesCount += (flags >>> RenderSectionFlags.HAS_BLOCK_ENTITIES) & 1;
94103
}
95104

105+
public boolean getAndResetCacheInvalidation() {
106+
var cacheIsInvalidated = this.prevSectionsWithGeometryCount != this.sectionsWithGeometryCount;
107+
this.prevSectionsWithGeometryCount = this.sectionsWithGeometryCount;
108+
return cacheIsInvalidated;
109+
}
110+
96111
public @Nullable ByteIterator sectionsWithGeometryIterator(boolean reverse) {
97112
if (this.sectionsWithGeometryCount == 0) {
98113
return null;

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegion.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import net.caffeinemc.mods.sodium.client.gl.arena.staging.StagingBuffer;
66
import net.caffeinemc.mods.sodium.client.gl.buffer.GlBuffer;
77
import net.caffeinemc.mods.sodium.client.gl.device.CommandList;
8+
import net.caffeinemc.mods.sodium.client.gl.device.MultiDrawBatch;
89
import net.caffeinemc.mods.sodium.client.gl.tessellation.GlTessellation;
10+
import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing;
911
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
1012
import net.caffeinemc.mods.sodium.client.render.chunk.data.SectionRenderDataStorage;
1113
import net.caffeinemc.mods.sodium.client.render.chunk.lists.ChunkRenderList;
@@ -51,6 +53,8 @@ public class RenderRegion {
5153
private final Map<TerrainRenderPass, SectionRenderDataStorage> sectionRenderData = new Reference2ReferenceOpenHashMap<>();
5254
private DeviceResources resources;
5355

56+
private final Map<TerrainRenderPass, MultiDrawBatch> cachedBatches = new Reference2ReferenceOpenHashMap<>();
57+
5458
public RenderRegion(int x, int y, int z, StagingBuffer stagingBuffer) {
5559
this.x = x;
5660
this.y = y;
@@ -113,6 +117,38 @@ public void delete(CommandList commandList) {
113117
}
114118

115119
Arrays.fill(this.sections, null);
120+
121+
for (var batch : this.cachedBatches.values()) {
122+
batch.delete();
123+
}
124+
this.cachedBatches.clear();
125+
}
126+
127+
public void clearAllCachedBatches() {
128+
for (var batch : this.cachedBatches.values()) {
129+
batch.clear();
130+
}
131+
}
132+
133+
public void clearCachedBatchFor(TerrainRenderPass pass) {
134+
var batch = this.cachedBatches.remove(pass);
135+
if (batch != null) {
136+
batch.delete();
137+
}
138+
}
139+
140+
public MultiDrawBatch getCachedBatch(TerrainRenderPass pass) {
141+
MultiDrawBatch batch = this.cachedBatches.get(pass);
142+
if (batch != null) {
143+
if (this.renderList.getAndResetCacheInvalidation()) {
144+
batch.clear();
145+
}
146+
return batch;
147+
}
148+
149+
batch = new MultiDrawBatch((ModelQuadFacing.COUNT * RenderRegion.REGION_SIZE) + 1);
150+
this.cachedBatches.put(pass, batch);
151+
return batch;
116152
}
117153

118154
public boolean isEmpty() {

common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/region/RenderRegionManager.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts;
1818
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.DefaultTerrainRenderPasses;
1919
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
20-
20+
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.SharedIndexSorter;
2121
import net.minecraft.util.profiling.Profiler;
2222
import net.minecraft.util.profiling.ProfilerFiller;
23-
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.SharedIndexSorter;
2423
import org.jetbrains.annotations.NotNull;
2524

26-
import java.util.*;
25+
import java.util.ArrayList;
26+
import java.util.Collection;
27+
import java.util.Iterator;
28+
import java.util.List;
2729

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

7880
if (storage != null) {
7981
storage.removeVertexData(renderSectionIndex);
82+
region.clearCachedBatchFor(pass);
8083
}
8184

8285
BuiltSectionMeshParts mesh = chunkBuildOutput.getMesh(pass);
@@ -93,12 +96,20 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect
9396
if (sorter instanceof SharedIndexSorter sharedIndexSorter) {
9497
var storage = region.createStorage(DefaultTerrainRenderPasses.TRANSLUCENT);
9598
storage.removeIndexData(renderSectionIndex);
96-
storage.setSharedIndexUsage(renderSectionIndex, sharedIndexSorter.quadCount());
99+
100+
// clear batch cache if it's newly using the shared index buffer and was not previously.
101+
// updates to the shared index buffer which cause the batch cache to be invalidated are handled with needsSharedIndexUpdate
102+
if (storage.setSharedIndexUsage(renderSectionIndex, sharedIndexSorter.quadCount())) {
103+
region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT);
104+
}
97105
} else {
98106
var storage = region.getStorage(DefaultTerrainRenderPasses.TRANSLUCENT);
99107
if (storage != null) {
100108
storage.removeIndexData(renderSectionIndex);
101109
storage.setSharedIndexUsage(renderSectionIndex, 0);
110+
111+
// always clear batch cache on uploads of new index data
112+
region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT);
102113
}
103114

104115
if (sorter == null) {
@@ -137,6 +148,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect
137148
// Once invalidated the tessellation will be re-created on the next attempted use
138149
if (bufferChanged) {
139150
region.refreshTesselation(commandList);
151+
region.clearAllCachedBatches();
140152
}
141153

142154
// Collect the upload results
@@ -167,6 +179,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect
167179

168180
if (indexBufferChanged) {
169181
region.refreshIndexedTesselation(commandList);
182+
region.clearCachedBatchFor(DefaultTerrainRenderPasses.TRANSLUCENT);
170183
}
171184

172185
profiler.pop();

0 commit comments

Comments
 (0)