Skip to content

Commit

Permalink
Rewrite translucency sorting (#293)
Browse files Browse the repository at this point in the history
Thanks to MCRcortex for initial suggestions, which were applied in the rewrite
  • Loading branch information
embeddedt committed May 15, 2024
1 parent 7a47c5d commit cead28f
Show file tree
Hide file tree
Showing 19 changed files with 813 additions and 258 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,19 @@ public static OptionPage quality() {
.build())
.build());

groups.add(OptionGroup.createBuilder()
.setId(StandardOptions.Group.SORTING)
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setId(StandardOptions.Option.TRANSLUCENT_FACE_SORTING)
.setName(Component.translatable("sodium.options.translucent_face_sorting.name"))
.setTooltip(Component.translatable("sodium.options.translucent_face_sorting.tooltip"))
.setControl(TickBoxControl::new)
.setImpact(OptionImpact.VARIES)
.setBinding((opts, value) -> opts.performance.useTranslucentFaceSorting = value, opts -> opts.performance.useTranslucentFaceSorting)
.setEnabled(!ShaderModBridge.isNvidiumEnabled())
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build())
.build());

return new OptionPage(Component.translatable("sodium.options.pages.quality"), ImmutableList.copyOf(groups));
}
Expand Down Expand Up @@ -397,17 +410,6 @@ public static OptionPage advanced() {
.setBinding((opts, value) -> opts.advanced.cpuRenderAheadLimit = value, opts -> opts.advanced.cpuRenderAheadLimit)
.build()
)
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setId(StandardOptions.Option.TRANSLUCENT_FACE_SORTING)
.setName(Component.translatable("sodium.options.translucent_face_sorting.name"))
.setTooltip(Component.translatable("sodium.options.translucent_face_sorting.tooltip"))
.setControl(TickBoxControl::new)
.setImpact(OptionImpact.VARIES)
.setBinding((opts, value) -> opts.performance.useTranslucentFaceSorting = value, opts -> opts.performance.useTranslucentFaceSorting)
.setEnabled(!ShaderModBridge.isNvidiumEnabled())
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build()
)
.build());

return new OptionPage(Component.translatable("sodium.options.pages.advanced"), ImmutableList.copyOf(groups));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public class DefaultChunkRenderer extends ShaderChunkRenderer {

private final GlVertexAttributeBinding[] vertexAttributeBindings;

private boolean isIndexedPass;

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

Expand All @@ -60,6 +62,8 @@ public void render(ChunkRenderMatrices matrices,

Iterator<ChunkRenderList> iterator = renderLists.iterator(renderPass.isReverseOrder());

this.isIndexedPass = renderPass.isSorted();

while (iterator.hasNext()) {
ChunkRenderList renderList = iterator.next();

Expand All @@ -76,7 +80,11 @@ public void render(ChunkRenderMatrices matrices,
continue;
}

this.sharedIndexBuffer.ensureCapacity(commandList, this.batch.getIndexBufferSize());


if (!this.isIndexedPass) {
this.sharedIndexBuffer.ensureCapacity(commandList, this.batch.getIndexBufferSize());
}

var tessellation = this.prepareTessellation(commandList, region);

Expand Down Expand Up @@ -106,6 +114,8 @@ private static void fillCommandBuffer(MultiDrawBatch batch,
int originY = renderRegion.getChunkY();
int originZ = renderRegion.getChunkZ();

int indexPointerMask = pass.isSorted() ? 0xFFFFFFFF : 0;

while (iterator.hasNext()) {
int sectionIndex = iterator.nextByteAsInt();

Expand All @@ -117,7 +127,7 @@ private static void fillCommandBuffer(MultiDrawBatch batch,

int slices;

if (useBlockFaceCulling && (!pass.isReverseOrder() || !SodiumClientMod.canApplyTranslucencySorting())) {
if (useBlockFaceCulling && !pass.isSorted()) {
slices = getVisibleFaces(camera.intX, camera.intY, camera.intZ, chunkX, chunkY, chunkZ);
} else {
slices = ModelQuadFacing.ALL;
Expand All @@ -126,21 +136,23 @@ private static void fillCommandBuffer(MultiDrawBatch batch,
slices &= SectionRenderDataUnsafe.getSliceMask(pMeshData);

if (slices != 0) {
addDrawCommands(batch, pMeshData, slices);
addDrawCommands(batch, pMeshData, slices, indexPointerMask);
}
}
}

@SuppressWarnings("IntegerMultiplicationImplicitCastToLong")
private static void addDrawCommands(MultiDrawBatch batch, long pMeshData, int mask) {
private static void addDrawCommands(MultiDrawBatch batch, long pMeshData, int mask, int indexPointerMask) {
final var pBaseVertex = batch.pBaseVertex;
final var pElementCount = batch.pElementCount;
final var pElementPointer = batch.pElementPointer;

int size = batch.size;

for (int facing = 0; facing < ModelQuadFacing.COUNT; facing++) {
MemoryUtil.memPutInt(pBaseVertex + (size << 2), SectionRenderDataUnsafe.getVertexOffset(pMeshData, facing));
MemoryUtil.memPutInt(pElementCount + (size << 2), SectionRenderDataUnsafe.getElementCount(pMeshData, facing));
MemoryUtil.memPutAddress(pElementPointer + (size << 3), SectionRenderDataUnsafe.getIndexOffset(pMeshData, facing) & indexPointerMask);

size += (mask >> facing) & 1;
}
Expand Down Expand Up @@ -213,10 +225,15 @@ private static float getCameraTranslation(int chunkBlockPos, int cameraBlockPos,

private GlTessellation prepareTessellation(CommandList commandList, RenderRegion region) {
var resources = region.getResources();
var tessellation = resources.getTessellation();
var tessellation = this.isIndexedPass ? resources.getIndexedTessellation() : resources.getTessellation();

if (tessellation == null) {
resources.updateTessellation(commandList, tessellation = this.createRegionTessellation(commandList, resources));
tessellation = this.createRegionTessellation(commandList, resources);
if (this.isIndexedPass) {
resources.updateIndexedTessellation(commandList, tessellation);
} else {
resources.updateTessellation(commandList, tessellation);
}
}

return tessellation;
Expand Down Expand Up @@ -253,7 +270,7 @@ private GlVertexAttributeBinding[] getBindingsForType() {
private GlTessellation createRegionTessellation(CommandList commandList, RenderRegion.DeviceResources resources) {
return commandList.createTessellation(GlPrimitiveType.TRIANGLES, new TessellationBinding[] {
TessellationBinding.forVertexBuffer(resources.getVertexBuffer(), this.vertexAttributeBindings),
TessellationBinding.forElementBuffer(this.sharedIndexBuffer.getBufferObject())
TessellationBinding.forElementBuffer(this.isIndexedPass ? resources.getIndexBuffer() : this.sharedIndexBuffer.getBufferObject())
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package me.jellysquid.mods.sodium.client.render.chunk;

import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBufferSorter;
import me.jellysquid.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
import me.jellysquid.mods.sodium.client.render.chunk.occlusion.GraphDirection;
import me.jellysquid.mods.sodium.client.render.chunk.occlusion.GraphDirectionSet;
Expand All @@ -11,6 +10,7 @@
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.embeddedt.embeddium.render.chunk.sorting.TranslucentQuadAnalyzer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -49,7 +49,7 @@ public class RenderSection {
private BlockEntity @Nullable[] culledBlockEntities;
private TextureAtlasSprite @Nullable[] animatedSprites;

private ChunkBufferSorter.SortBuffer translucencyData;
private TranslucentQuadAnalyzer.SortState sortState;


// Pending Update State
Expand All @@ -65,6 +65,9 @@ public class RenderSection {
// Lifetime state
private boolean disposed;

// Used by the translucency sorter, to determine when a section needs sorting again
public double lastCameraX, lastCameraY, lastCameraZ;

public RenderSection(RenderRegion region, int chunkX, int chunkY, int chunkZ) {
this.chunkX = chunkX;
this.chunkY = chunkY;
Expand Down Expand Up @@ -285,6 +288,10 @@ public int getFlags() {
return this.flags;
}

public boolean isAlignedWithSectionOnGrid(int otherX, int otherY, int otherZ) {
return this.chunkX == otherX || this.chunkY == otherY || this.chunkZ == otherZ;
}

/**
* Returns the occlusion culling data which determines this chunk's connectedness on the visibility graph.
*/
Expand Down Expand Up @@ -314,12 +321,17 @@ public long getVisibilityData() {
return this.globalBlockEntities;
}

public ChunkBufferSorter.SortBuffer getTranslucencyData() {
return this.translucencyData;
@Nullable
public TranslucentQuadAnalyzer.SortState getSortState() {
return this.sortState;
}

public boolean containsTranslucentGeometry() {
return (this.getFlags() & (1 << RenderSectionFlags.HAS_TRANSLUCENT_DATA)) != 0;
}

public void setTranslucencyData(ChunkBufferSorter.SortBuffer data) {
this.translucencyData = data;
public void setSortState(@Nullable TranslucentQuadAnalyzer.SortState data) {
this.sortState = data != null ? data.compactForStorage() : null;
}

public @Nullable CancellationToken getBuildCancellationToken() {
Expand Down

0 comments on commit cead28f

Please sign in to comment.