Skip to content

Commit

Permalink
Implement Translucent Face Sorting using compute shader
Browse files Browse the repository at this point in the history
  • Loading branch information
MESLewis committed Nov 18, 2021
1 parent 1ca949a commit 33d8080
Show file tree
Hide file tree
Showing 6 changed files with 653 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
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 net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Matrix4f;
import org.lwjgl.system.MemoryStack;
Expand All @@ -38,6 +39,10 @@ public class RegionChunkRenderer extends ShaderChunkRenderer {
private final GlMutableBuffer chunkInfoBuffer;
private final boolean isBlockFaceCullingEnabled = SodiumClientMod.options().advanced.useBlockFaceCulling;

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

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

Expand Down Expand Up @@ -86,6 +91,70 @@ private static ByteBuffer createChunkInfoBuffer(MemoryStack stack) {
public void render(MatrixStack matrixStack, CommandList commandList,
ChunkRenderList list, BlockRenderPass pass,
ChunkCameraContext camera) {
if(pass.isTranslucent() && SodiumClientMod.options().advanced.useTranslucentFaceSorting) {
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;
//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 (!buildDrawBatch(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 = matrixStack.peek()
.getModel()
.copy();
matrix.multiplyByTranslation(x, y, z);

compute.setModelViewMatrix(matrix);

RenderRegion.RenderRegionArenas arenas = region.getArenas();
runCompute = compute.execute(commandList, batch, arenas);
region.setNeedsTranslucencyCompute(false);
}
if(!runCompute && !fullRebuild) {
break;
}
}
}
super.endCompute();
}

super.begin(pass);

ChunkShaderInterface shader = this.activeProgram.getInterface();
Expand All @@ -106,7 +175,7 @@ public void render(MatrixStack matrixStack, CommandList commandList,
GlTessellation tessellation = this.createTessellationForRegion(commandList, region.getArenas(), pass);
executeDrawBatches(commandList, tessellation);
}

super.end();
}

Expand All @@ -130,7 +199,7 @@ private boolean buildDrawBatch(List<RenderSection> sections, BlockRenderPass pas

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

if (this.isBlockFaceCullingEnabled) {
if (this.isBlockFaceCullingEnabled && !(pass.isTranslucent() && SodiumClientMod.options().advanced.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 @@ -2,9 +2,9 @@

import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildResult;
import me.jellysquid.mods.sodium.client.render.chunk.graph.ChunkGraphInfo;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderBounds;
import me.jellysquid.mods.sodium.client.render.chunk.data.ChunkRenderData;
import me.jellysquid.mods.sodium.client.render.chunk.graph.ChunkGraphInfo;
import me.jellysquid.mods.sodium.client.render.chunk.passes.BlockRenderPass;
import me.jellysquid.mods.sodium.client.render.chunk.region.RenderRegion;
import me.jellysquid.mods.sodium.client.render.texture.SpriteUtil;
Expand Down 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,27 @@

import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import me.jellysquid.mods.sodium.client.gl.attribute.GlVertexFormat;
import me.jellysquid.mods.sodium.client.gl.shader.*;
import me.jellysquid.mods.sodium.client.gl.device.RenderDevice;
import me.jellysquid.mods.sodium.client.gl.shader.*;
import me.jellysquid.mods.sodium.client.model.vertex.type.ChunkVertexType;
import me.jellysquid.mods.sodium.client.render.chunk.format.ChunkMeshAttribute;
import me.jellysquid.mods.sodium.client.render.chunk.passes.BlockRenderPass;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkFogMode;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderInterface;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderBindingPoints;
import me.jellysquid.mods.sodium.client.render.chunk.shader.ChunkShaderOptions;
import me.jellysquid.mods.sodium.client.render.chunk.shader.*;
import net.minecraft.util.Identifier;

import java.util.Map;

public abstract class ShaderChunkRenderer implements ChunkRenderer {
private final Map<ChunkShaderOptions, GlProgram<ChunkShaderInterface>> programs = new Object2ObjectOpenHashMap<>();
private final Map<ChunkShaderOptions, GlProgram<ComputeShaderInterface>> computes = new Object2ObjectOpenHashMap<>();

protected final ChunkVertexType vertexType;
protected final GlVertexFormat<ChunkMeshAttribute> vertexFormat;

protected final RenderDevice device;

protected GlProgram<ChunkShaderInterface> activeProgram;
protected GlProgram<ComputeShaderInterface> activeComputeProgram;

public ShaderChunkRenderer(RenderDevice device, ChunkVertexType vertexType) {
this.device = device;
Expand All @@ -41,12 +40,31 @@ protected GlProgram<ChunkShaderInterface> compileProgram(ChunkShaderOptions opti
return program;
}

protected GlProgram<ComputeShaderInterface> compileComputeProgram(ChunkShaderOptions options) {
GlProgram<ComputeShaderInterface> compute = this.computes.get(options);

if (compute == null) {
GlShader shader = ShaderLoader.loadShader(ShaderType.COMPUTE,
new Identifier("sodium", "blocks/block_layer_translucent_compute.glsl"), options.constants());

try {
this.computes.put(options,
compute = GlProgram.builder(new Identifier("sodium", "chunk_shader_compute"))
.attachShader(shader)
.link(ComputeShaderInterface::new));
} finally {
shader.delete();
}
}
return compute;
}

private GlProgram<ChunkShaderInterface> createShader(String path, ChunkShaderOptions options) {
ShaderConstants constants = options.constants();

GlShader vertShader = ShaderLoader.loadShader(ShaderType.VERTEX,
new Identifier("sodium", path + ".vsh"), constants);

GlShader fragShader = ShaderLoader.loadShader(ShaderType.FRAGMENT,
new Identifier("sodium", path + ".fsh"), constants);

Expand All @@ -69,6 +87,7 @@ private GlProgram<ChunkShaderInterface> createShader(String path, ChunkShaderOpt
protected void begin(BlockRenderPass pass) {
ChunkShaderOptions options = new ChunkShaderOptions(ChunkFogMode.SMOOTH, pass);

this.activeComputeProgram = null;
this.activeProgram = this.compileProgram(options);
this.activeProgram.bind();
this.activeProgram.getInterface()
Expand All @@ -80,11 +99,28 @@ protected void end() {
this.activeProgram = null;
}

protected void beginCompute(BlockRenderPass pass) {
ChunkShaderOptions options = new ChunkShaderOptions(ChunkFogMode.SMOOTH, pass);

this.activeProgram = null;
this.activeComputeProgram = this.compileComputeProgram(options);
this.activeComputeProgram.bind();
this.activeComputeProgram.getInterface()
.setup(this.vertexType);
}

protected void endCompute() {
this.activeComputeProgram.unbind();
this.activeComputeProgram = null;
}

@Override
public void delete() {
this.programs.values()
.forEach(GlProgram::delete);
this.programs.clear();
this.computes.values().forEach(GlProgram::delete);
this.computes.clear();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class RenderRegion {

private RenderRegionVisibility visibility;

private boolean needsTranslucencyCompute = false;

public RenderRegion(RenderRegionManager manager, int x, int y, int z) {
this.manager = manager;

Expand Down Expand Up @@ -137,6 +139,14 @@ public RenderRegionVisibility getVisibility() {
return this.visibility;
}

public void setNeedsTranslucencyCompute(boolean compute) {
this.needsTranslucencyCompute = compute;
}

public boolean getNeedsTranslucencyCompute() {
return this.needsTranslucencyCompute;
}

public static int getChunkIndex(int x, int y, int z) {
return (x * RenderRegion.REGION_LENGTH * RenderRegion.REGION_HEIGHT) + (y * RenderRegion.REGION_LENGTH) + z;
}
Expand Down
Loading

0 comments on commit 33d8080

Please sign in to comment.