Skip to content

Commit

Permalink
Implement optional GPU-based translucency sorting
Browse files Browse the repository at this point in the history
Implementation directly derived from CaffeineMC/sodium-fabric#963

Some changes were made to improve frametimes on low-end systems

This is still very experimental, and is not enabled by default
  • Loading branch information
embeddedt committed Oct 9, 2023
1 parent 021a4c7 commit e685a7e
Show file tree
Hide file tree
Showing 15 changed files with 718 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import org.lwjgl.opengl.GL20C;
import org.lwjgl.opengl.GL31C;
import org.lwjgl.opengl.GL43C;

public enum GlBufferTarget {
ARRAY_BUFFER(GL20C.GL_ARRAY_BUFFER, GL20C.GL_ARRAY_BUFFER_BINDING),
ELEMENT_BUFFER(GL20C.GL_ELEMENT_ARRAY_BUFFER, GL20C.GL_ELEMENT_ARRAY_BUFFER_BINDING),
COPY_READ_BUFFER(GL31C.GL_COPY_READ_BUFFER, GL31C.GL_COPY_READ_BUFFER),
COPY_WRITE_BUFFER(GL31C.GL_COPY_WRITE_BUFFER, GL31C.GL_COPY_WRITE_BUFFER);
COPY_WRITE_BUFFER(GL31C.GL_COPY_WRITE_BUFFER, GL31C.GL_COPY_WRITE_BUFFER),
SHADER_STORAGE_BUFFER(GL43C.GL_SHADER_STORAGE_BUFFER, GL43C.GL_SHADER_STORAGE_BUFFER_BINDING);

public static final GlBufferTarget[] VALUES = GlBufferTarget.values();
public static final int COUNT = VALUES.length;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import me.jellysquid.mods.sodium.client.gl.util.EnumBitField;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;

public interface CommandList extends AutoCloseable {
GlMutableBuffer createMutableBuffer();
Expand All @@ -21,10 +22,16 @@ public interface CommandList extends AutoCloseable {

void uploadData(GlMutableBuffer glBuffer, ByteBuffer byteBuffer, GlBufferUsage usage);

void bufferData(GlBufferTarget target, GlMutableBuffer glBuffer, int[] intArray, GlBufferUsage usage);

void bufferData(GlBufferTarget target, GlMutableBuffer glBuffer, IntBuffer intBuffer, GlBufferUsage usage);

void copyBufferSubData(GlBuffer src, GlBuffer dst, long readOffset, long writeOffset, long bytes);

void bindBuffer(GlBufferTarget target, GlBuffer buffer);

void bindBufferBase(GlBufferTarget target, int index, GlBuffer buffer);

void unbindVertexArray();

void allocateStorage(GlMutableBuffer buffer, long bufferSize, GlBufferUsage usage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ public void uploadData(GlMutableBuffer glBuffer, ByteBuffer byteBuffer, GlBuffer
glBuffer.setSize(byteBuffer.remaining());
}

@Override
public void bufferData(GlBufferTarget target, GlMutableBuffer glBuffer, int[] intArray, GlBufferUsage usage) {
this.bindBuffer(target, glBuffer);

GL15C.glBufferData(target.getTargetParameter(), intArray, usage.getId());
}

@Override
public void bufferData(GlBufferTarget target, GlMutableBuffer glBuffer, IntBuffer intBuffer, GlBufferUsage usage) {
this.bindBuffer(target, glBuffer);

GL15C.glBufferData(target.getTargetParameter(), intBuffer, usage.getId());
}

@Override
public void copyBufferSubData(GlBuffer src, GlBuffer dst, long readOffset, long writeOffset, long bytes) {
this.bindBuffer(GlBufferTarget.COPY_READ_BUFFER, src);
Expand All @@ -105,6 +119,11 @@ public void bindBuffer(GlBufferTarget target, GlBuffer buffer) {
}
}

@Override
public void bindBufferBase(GlBufferTarget target, int index, GlBuffer buffer) {
GL30C.glBindBufferBase(target.getTargetParameter(), index, buffer.handle());
}

@Override
public void unbindVertexArray() {
if (this.stateTracker.makeVertexArrayActive(null)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package me.jellysquid.mods.sodium.client.gl.shader;

import org.lwjgl.opengl.GL20C;
import org.lwjgl.opengl.GL43C;

/**
* An enumeration over the supported OpenGL shader types.
*/
public enum ShaderType {
VERTEX(GL20C.GL_VERTEX_SHADER),
FRAGMENT(GL20C.GL_FRAGMENT_SHADER);
FRAGMENT(GL20C.GL_FRAGMENT_SHADER),
COMPUTE(GL43C.GL_COMPUTE_SHADER);

public final int id;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import org.lwjgl.opengl.GL32C;

public enum GlIndexType {
UNSIGNED_BYTE(GL32C.GL_UNSIGNED_BYTE, 1),
UNSIGNED_SHORT(GL32C.GL_UNSIGNED_SHORT, 2),
UNSIGNED_INT(GL32C.GL_UNSIGNED_INT, 4);

private final int id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import me.jellysquid.mods.sodium.client.gui.options.control.TickBoxControl;
import me.jellysquid.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage;
import me.jellysquid.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ComputeShaderInterface;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gl.Framebuffer;
import net.minecraft.client.option.AoMode;
Expand Down Expand Up @@ -289,6 +290,16 @@ public static OptionPage performance() {
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build()
)
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setName(new TranslatableText("sodium.options.translucent_face_sorting.name"))
.setTooltip(new TranslatableText("sodium.options.translucent_face_sorting.tooltip"))
.setControl(TickBoxControl::new)
.setImpact(OptionImpact.VARIES)
.setEnabled(ComputeShaderInterface.isSupported(RenderDevice.INSTANCE))
.setBinding((opts, value) -> opts.performance.useTranslucentFaceSorting = value, opts -> opts.performance.useTranslucentFaceSorting)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build()
)
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setName(new TranslatableText("sodium.options.use_fog_occlusion.name"))
.setTooltip(new TranslatableText("sodium.options.use_fog_occlusion.tooltip"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public static class PerformanceSettings {
public boolean useFogOcclusion = true;
public boolean useBlockFaceCulling = true;
public boolean useCompactVertexFormat = true;
public boolean useTranslucentFaceSorting = false;
}

public static class AdvancedSettings {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,7 @@ public Result pop() {
}

private static GlIndexType getOptimalIndexType(int count) {
if (count < 65536) {
return GlIndexType.UNSIGNED_SHORT;
} else {
return GlIndexType.UNSIGNED_INT;
}
return GlIndexType.UNSIGNED_INT;
}

public int getCount() {
Expand Down Expand Up @@ -65,8 +61,8 @@ private Result(IntArrayList indices) {
maxIndex = Math.max(maxIndex, i);
}

this.minIndex = minIndex;
this.maxIndex = maxIndex;
this.minIndex = 0;
this.maxIndex = this.indices.size();

this.format = getOptimalIndexType(this.maxIndex - this.minIndex);
}
Expand All @@ -78,13 +74,7 @@ public int writeTo(int offset, ByteBuffer buffer) {
int pointer = offset;

while (it.hasNext()) {
int value = it.nextInt() - this.minIndex;

switch (this.format) {
case UNSIGNED_BYTE -> buffer.put(pointer, (byte) value);
case UNSIGNED_SHORT -> buffer.putShort(pointer, (short) value);
case UNSIGNED_INT -> buffer.putInt(pointer, value);
}
buffer.putInt(pointer, it.nextInt());

pointer += stride;
}
Expand All @@ -101,7 +91,7 @@ public int getCount() {
}

public int getBaseVertex() {
return this.minIndex;
return 0;
}

public GlIndexType getFormat() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegion;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderInterface;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ComputeShaderInterface;
import org.lwjgl.system.MemoryUtil;
import repack.joml.Matrix4f;

import java.nio.ByteBuffer;
import java.util.List;
Expand All @@ -40,6 +42,10 @@ public class RegionChunkRenderer extends ShaderChunkRenderer {
private final GlMutableBuffer chunkInfoBuffer;
private final boolean isBlockFaceCullingEnabled = SodiumClientMod.options().performance.useBlockFaceCulling;

private double lastComputeUpdateX = 0;
private double lastComputeUpdateY = 0;
private double lastComputeUpdateZ = 0;

private GlVertexAttributeBinding[] getBindingsForType() {
if(this.vertexType != ChunkModelVertexFormats.VANILLA_LIKE) {
GlVertexFormat<ChunkMeshAttribute> compactFormat = (GlVertexFormat<ChunkMeshAttribute>)this.vertexFormat;
Expand Down Expand Up @@ -89,6 +95,9 @@ public RegionChunkRenderer(RenderDevice device, ChunkVertexType<?> vertexType) {
public void render(ChunkRenderMatrices matrices, CommandList commandList,
ChunkRenderList list, BlockRenderPass pass,
ChunkCameraContext camera) {
if(pass.isTranslucent() && SodiumClientMod.options().performance.useTranslucentFaceSorting) {
computeTranslucency(matrices, commandList, list, pass, camera);
}
super.begin(pass);

ChunkShaderInterface shader = this.activeProgram.getInterface();
Expand All @@ -112,6 +121,84 @@ public void render(ChunkRenderMatrices matrices, CommandList commandList,
super.end();
}

private final Matrix4f cachedModelViewMatrix = new Matrix4f();

private void computeTranslucency(ChunkRenderMatrices matrices, CommandList commandList,
ChunkRenderList list, BlockRenderPass pass,
ChunkCameraContext camera) {
if(this.batches.length > 1)
throw new UnsupportedOperationException("Multiple multidraw batches found");

super.beginCompute(pass);

boolean fullRebuild = false;
if (activeComputeProgram != null) {
ComputeShaderInterface compute = activeComputeProgram.getInterface();

double cameraX = camera.blockX + camera.deltaX;
double cameraY = camera.blockY + camera.deltaY;
double cameraZ = camera.blockZ + camera.deltaZ;

//If we have moved set all chunks as needing compute
double dx = cameraX - lastComputeUpdateX;
double dy = cameraY - lastComputeUpdateY;
double dz = cameraZ - lastComputeUpdateZ;
if(dx * dx + dy * dy + dz * dz > 1.0D) {
lastComputeUpdateX = cameraX;
lastComputeUpdateY = cameraY;
lastComputeUpdateZ = cameraZ;
fullRebuild = true;
}

compute.setDrawUniforms(this.chunkInfoBuffer);

boolean runCompute = true;
int regionsComputed = 0;
//We want compute to run beginning with the closest chunks
for (Map.Entry<RenderRegion, List<RenderSection>> entry : sortedRegions(list, false)) {
RenderRegion region = entry.getKey();
List<RenderSection> regionSections = entry.getValue();

if(fullRebuild) {
region.setNeedsTranslucencyCompute(true);
if(!runCompute) {
continue;
}
}

if (region.getNeedsTranslucencyCompute() && !regionSections.isEmpty()) {
if (!buildDrawBatches(regionSections, pass, camera)) {
continue;
}
float x = getCameraTranslation(region.getOriginX(), camera.blockX, camera.deltaX);
float y = getCameraTranslation(region.getOriginY(), camera.blockY, camera.deltaY);
float z = getCameraTranslation(region.getOriginZ(), camera.blockZ, camera.deltaZ);

Matrix4f matrix = this.cachedModelViewMatrix;
matrix.set(matrices.modelView());
matrix.translate(x, y, z);

compute.setModelViewMatrix(matrix);

RenderRegion.RenderRegionArenas arenas = region.getArenas();
runCompute = compute.execute(commandList, batches[0], arenas);
region.setNeedsTranslucencyCompute(false);
//noinspection ForLoopReplaceableByForEach
for(int i = 0; i < regionSections.size(); i++) {
if(regionSections.get(i).getGraphicsState(BlockRenderPass.TRANSLUCENT) != null)
regionsComputed++;
}
if(regionsComputed >= 15)
runCompute = false; // do not continue sorting for the rest of the frame
}
if(!runCompute && !fullRebuild) {
break;
}
}
}
super.endCompute();
}

private boolean buildDrawBatches(List<RenderSection> sections, BlockRenderPass pass, ChunkCameraContext camera) {
for (MultiDrawBatch batch : this.batches) {
batch.begin();
Expand All @@ -134,7 +221,7 @@ private boolean buildDrawBatches(List<RenderSection> sections, BlockRenderPass p

this.addDrawCall(state.getModelPart(ModelQuadFacing.UNASSIGNED), indexOffset, baseVertex);

if (this.isBlockFaceCullingEnabled) {
if (this.isBlockFaceCullingEnabled && !(pass.isTranslucent() && SodiumClientMod.options().performance.useTranslucentFaceSorting)) {
if (camera.posY > bounds.y1) {
this.addDrawCall(state.getModelPart(ModelQuadFacing.UP), indexOffset, baseVertex);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ public boolean canAcceptBuildResults(ChunkBuildResult result) {
public void onBuildFinished(ChunkBuildResult result) {
this.setData(result.data);
this.lastAcceptedBuildTime = result.buildTime;
region.setNeedsTranslucencyCompute(true);
}

public int getChunkId() {
Expand Down
Loading

0 comments on commit e685a7e

Please sign in to comment.