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

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
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 @@ -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);
}

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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());

Expand Down Expand Up @@ -321,6 +321,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 @@ -13,6 +13,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 @@ -29,6 +30,8 @@ public ChunkRenderList(RenderRegion region) {
}

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

this.sectionsWithGeometryCount = 0;
this.sectionsWithSpritesCount = 0;
this.sectionsWithEntitiesCount = 0;
Expand All @@ -47,8 +50,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 @@ -57,6 +66,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,44 +27,4 @@ public ReversibleObjectArrayIterator<ChunkRenderList> iterator(boolean reverse)
public static SortedRenderLists empty() {
return EMPTY;
}

public static class Builder {
private final ObjectArrayList<ChunkRenderList> lists = new ObjectArrayList<>();
private final int frame;

public Builder(int frame) {
this.frame = frame;
}

public void add(RenderSection section) {
RenderRegion region = section.getRegion();
ChunkRenderList list = region.getRenderList();

// Even if a section does not have render objects, we must ensure the render list is initialized and put
// into the sorted queue of lists, so that we maintain the correct order of draw calls.
if (list.getLastVisibleFrame() != this.frame) {
list.reset(this.frame);

this.lists.add(list);
}

// Only add the section to the render list if it actually contains render objects
if (section.getFlags() != 0) {
list.add(section);
}
}

public SortedRenderLists build() {
var filtered = new ObjectArrayList<ChunkRenderList>(this.lists.size());

// Filter any empty render lists
for (var list : this.lists) {
if (list.size() > 0) {
filtered.add(list);
}
}

return new SortedRenderLists(filtered);
}
}
}
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 @@ -101,6 +105,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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,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 @@ -98,6 +99,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);
}
}
}
Expand All @@ -118,6 +120,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 All @@ -135,6 +138,7 @@ private void uploadResults(CommandList commandList, RenderRegion region, Collect

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

for (PendingSectionIndexBufferUpload upload : indexUploads) {
Expand Down