Skip to content

Commit

Permalink
add quad splitting mode setting and integrate it with the partition t…
Browse files Browse the repository at this point in the history
…ree builder and the topo sorter
  • Loading branch information
douira committed Jan 26, 2025
1 parent 1557ae5 commit 0939e4f
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import net.caffeinemc.mods.sodium.client.gui.options.storage.MinecraftOptionsStorage;
import net.caffeinemc.mods.sodium.client.gui.options.storage.SodiumOptionsStorage;
import net.caffeinemc.mods.sodium.client.compatibility.workarounds.Workarounds;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.QuadSplittingMode;
import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation;
import net.minecraft.client.AttackIndicatorStatus;
import net.minecraft.client.InactivityFpsLimit;
Expand Down Expand Up @@ -343,19 +344,29 @@ public static OptionPage performance() {
.build())
.build());

var tsGroupBuilder = OptionGroup.createBuilder();
if (PlatformRuntimeInformation.getInstance().isDevelopmentEnvironment()) {
groups.add(OptionGroup.createBuilder()
.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setName(Component.translatable("sodium.options.sort_behavior.name"))
.setTooltip(Component.translatable("sodium.options.sort_behavior.tooltip"))
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> opts.performance.sortingEnabled = value, opts -> opts.performance.sortingEnabled)
.setImpact(OptionImpact.LOW)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build())
.build());
tsGroupBuilder.add(OptionImpl.createBuilder(boolean.class, sodiumOpts)
.setName(Component.translatable("sodium.options.sort_behavior.name"))
.setTooltip(Component.translatable("sodium.options.sort_behavior.tooltip"))
.setControl(TickBoxControl::new)
.setBinding((opts, value) -> opts.performance.sortingEnabled = value, opts -> opts.performance.sortingEnabled)
.setImpact(OptionImpact.LOW)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build());
}

tsGroupBuilder.add(OptionImpl.createBuilder(QuadSplittingMode.class, sodiumOpts)
.setName(Component.translatable("sodium.options.quad_splitting.name"))
.setTooltip(Component.translatable("sodium.options.quad_splitting.tooltip"))
.setControl(option -> new CyclingControl<>(option, QuadSplittingMode.class))
.setBinding((opts, value) -> opts.performance.quadSplittingMode = value, opts -> opts.performance.quadSplittingMode)
.setImpact(OptionImpact.MEDIUM)
.setFlags(OptionFlag.REQUIRES_RENDERER_RELOAD)
.build());

groups.add(tsGroupBuilder.build());

return new OptionPage(Component.translatable("sodium.options.pages.performance"), ImmutableList.copyOf(groups));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.SerializedName;
import net.caffeinemc.mods.sodium.client.gui.options.TextProvider;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.QuadSplittingMode;
import net.caffeinemc.mods.sodium.client.services.PlatformRuntimeInformation;
import net.caffeinemc.mods.sodium.client.util.FileUtil;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior;
import net.minecraft.client.GraphicsStatus;
import net.minecraft.network.chat.Component;

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Modifier;
Expand Down Expand Up @@ -48,6 +50,7 @@ public static class PerformanceSettings {

@SerializedName("sorting_enabled_v2") // reset the older option in configs before we started hiding it
public boolean sortingEnabled = true;
public QuadSplittingMode quadSplittingMode = QuadSplittingMode.SAFE;

public SortBehavior getSortBehavior() {
return this.sortingEnabled ? SortBehavior.DYNAMIC_DEFER_NEARBY_ZERO_FRAMES : SortBehavior.OFF;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public ChunkBuildOutput execute(ChunkBuildContext buildContext, CancellationToke
var oldData = this.render.getTranslucentData();
var translucentMesh = meshes.get(DefaultTerrainRenderPasses.TRANSLUCENT);
ChunkMeshBufferBuilder translucentVertexBuffer = null;
if (translucentMesh != null) {
if (translucentMesh != null && collector.isSplittingQuads()) {
translucentVertexBuffer = buffers.get(DefaultTerrainRenderPasses.TRANSLUCENT).getVertexBuffer(ModelQuadFacing.UNASSIGNED);
translucentVertexBuffer.restart();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting;

import net.caffeinemc.mods.sodium.client.gui.options.TextProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.util.Mth;

public enum QuadSplittingMode implements TextProvider {
OFF(0.0f, "options.off"),
SAFE(1.0f, "sodium.options.quad_splitting.safe"),
UNLIMITED(Float.POSITIVE_INFINITY, "sodium.options.quad_splitting.unlimited");

// how much bigger the final geometry is allowed to be compared to the input geometry when performing quad splitting.
// 0.5f means that the final geometry can be 50% bigger than the input geometry.
private final float quadSplittingFactor;
private final Component name;

QuadSplittingMode(float quadSplittingFactor, String name) {
this.quadSplittingFactor = quadSplittingFactor;
this.name = Component.translatable(name);
}

@Override
public Component getLocalizedName() {
return this.name;
}

public boolean allowsSplitting() {
return this != OFF;
}

public int getMaxExtraQuads(int baseQuadCount) {
return Mth.ceil(baseQuadCount * this.quadSplittingFactor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import net.caffeinemc.mods.sodium.client.SodiumClientMod;
import net.caffeinemc.mods.sodium.client.model.quad.properties.ModelQuadFacing;
import net.caffeinemc.mods.sodium.client.render.chunk.compile.ChunkBuildOutput;
import net.caffeinemc.mods.sodium.client.render.chunk.compile.pipeline.DefaultFluidRenderer;
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.bsp_tree.BSPBuildFailureException;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.*;
Expand All @@ -18,8 +17,6 @@
import net.caffeinemc.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
import net.minecraft.core.SectionPos;
import net.minecraft.util.Mth;
import org.joml.Vector3f;
import org.joml.Vector3fc;

/**
* The translucent geometry collector collects the data from the renderers and
Expand Down Expand Up @@ -49,9 +46,8 @@
* task result.
*/
public class TranslucentGeometryCollector {
public static final boolean SPLIT_QUADS = true; // TODO: Move into setting, make it default on, add limit to geometry amplification

private final SectionPos sectionPos;
private final QuadSplittingMode quadSplittingMode = SodiumClientMod.options().performance.quadSplittingMode;

// true if there are any unaligned quads
private boolean hasUnaligned = false;
Expand Down Expand Up @@ -106,7 +102,7 @@ public void appendQuad(ChunkVertexEncoder.Vertex[] vertices, ModelQuadFacing fac
}

TQuad quad;
if (SPLIT_QUADS) {
if (this.isSplittingQuads()) {
quad = FullTQuad.fromVertices(vertices, facing, packedNormal);
} else {
quad = RegularTQuad.fromVertices(vertices, facing, packedNormal);
Expand Down Expand Up @@ -171,6 +167,10 @@ public void appendQuad(ChunkVertexEncoder.Vertex[] vertices, ModelQuadFacing fac
}
}

public boolean isSplittingQuads() {
return this.quadSplittingMode.allowsSplitting();
}

/**
* Filters the given sort type to fit within the selected sorting mode. If it
* doesn't match, then it's set to the NONE sort type.
Expand Down Expand Up @@ -321,12 +321,10 @@ private SortType sortTypeHeuristic() {

// use the given set of quad count limits to determine if a static topo sort
// should be attempted

// TODO: make topo sort fail on intersecting geometry when SPLIT_QUADS is enabled
// var attemptLimitIndex = Mth.clamp(normalCount, 2, STATIC_TOPO_SORT_ATTEMPT_LIMITS.length - 1);
// if (this.quads.length <= STATIC_TOPO_SORT_ATTEMPT_LIMITS[attemptLimitIndex]) {
// return SortType.STATIC_TOPO;
// }
var attemptLimitIndex = Mth.clamp(normalCount, 2, STATIC_TOPO_SORT_ATTEMPT_LIMITS.length - 1);
if (this.quads.length <= STATIC_TOPO_SORT_ATTEMPT_LIMITS[attemptLimitIndex]) {
return SortType.STATIC_TOPO;
}

return SortType.DYNAMIC;
}
Expand Down Expand Up @@ -404,7 +402,7 @@ private TranslucentData makeNewTranslucentData(int[] vertexCounts, CombinedCamer
// (no backface culling) and all vertices are in the UNASSIGNED direction.
if (this.sortType == SortType.STATIC_TOPO) {
ensureUnassignedVertexCount(vertexCounts);
var result = StaticTopoData.fromMesh(this.quads, this.sectionPos);
var result = StaticTopoData.fromMesh(this.quads, this.sectionPos, this.isSplittingQuads());
if (result != null) {
return result;
}
Expand All @@ -421,7 +419,7 @@ private TranslucentData makeNewTranslucentData(int[] vertexCounts, CombinedCamer
if (this.sortType == SortType.DYNAMIC) {
ensureUnassignedVertexCount(vertexCounts);
try {
return DynamicBSPData.fromMesh(cameraPos, this.quads, this.sectionPos, oldData, translucentVertexBuffer);
return DynamicBSPData.fromMesh(cameraPos, this.quads, this.sectionPos, oldData, this.quadSplittingMode, translucentVertexBuffer);
} catch (BSPBuildFailureException e) {
var geometryPlanes = GeometryPlanes.fromQuadLists(this.sectionPos, this.quads);
return DynamicTopoData.fromMesh(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.bsp_tree;

import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.QuadSplittingMode;
import net.caffeinemc.mods.sodium.client.render.chunk.vertex.builder.ChunkMeshBufferBuilder;
import org.joml.Vector3fc;

Expand Down Expand Up @@ -33,13 +34,13 @@ public void collectSortedQuads(NativeBuffer nativeBuffer, Vector3fc cameraPos) {
}

public static BSPResult buildBSP(TQuad[] quads, SectionPos sectionPos, BSPNode oldRoot,
boolean prepareNodeReuse, ChunkMeshBufferBuilder translucentVertexBuffer) {
boolean prepareNodeReuse, QuadSplittingMode quadSplittingMode, ChunkMeshBufferBuilder translucentVertexBuffer) {
// throw if there's too many quads
InnerPartitionBSPNode.validateQuadCount(quads.length);

// create a workspace and then the nodes figure out the recursive building.
// throws if the BSP can't be built, null if none is necessary
var workspace = new BSPWorkspace(quads, sectionPos, prepareNodeReuse, translucentVertexBuffer);
var workspace = new BSPWorkspace(quads, sectionPos, prepareNodeReuse, quadSplittingMode, translucentVertexBuffer);

// initialize the indexes to all quads
int[] initialIndexes = new int[quads.length];
Expand All @@ -54,7 +55,7 @@ public static BSPResult buildBSP(TQuad[] quads, SectionPos sectionPos, BSPNode o
return result;
}

private static boolean doubleLeafPossible(TQuad quadA, TQuad quadB) {
private static boolean doubleLeafPossible(TQuad quadA, TQuad quadB, boolean failOnIntersection) {
// check for coplanar or mutually invisible quads
var facingA = quadA.getFacing();
var facingB = quadB.getFacing();
Expand Down Expand Up @@ -83,8 +84,8 @@ else if (facingA == facingB.getOpposite()) {

// aligned otherwise mutually invisible
else {
return !TopoGraphSorting.orthogonalQuadVisibleThrough(quadA, quadB)
&& !TopoGraphSorting.orthogonalQuadVisibleThrough(quadB, quadA);
return !TopoGraphSorting.orthogonalQuadVisibleThrough(quadA, quadB, failOnIntersection)
&& !TopoGraphSorting.orthogonalQuadVisibleThrough(quadB, quadA, failOnIntersection);
}

return false;
Expand All @@ -104,7 +105,7 @@ static BSPNode build(BSPWorkspace workspace, IntArrayList indexes, int depth, BS
var quadA = workspace.get(quadIndexA);
var quadB = workspace.get(quadIndexB);

if (doubleLeafPossible(quadA, quadB)) {
if (doubleLeafPossible(quadA, quadB, workspace.canSplitQuads())) {
return new LeafDoubleBSPNode(quadIndexA, quadIndexB);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.QuadSplittingMode;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.quad.FullTQuad;
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.quad.TQuad;
import net.caffeinemc.mods.sodium.client.render.chunk.vertex.builder.ChunkMeshBufferBuilder;
Expand All @@ -22,19 +23,30 @@ class BSPWorkspace extends ObjectArrayList<TQuad> {

final SectionPos sectionPos;
final boolean prepareNodeReuse;
final QuadSplittingMode quadSplittingMode;
private int remainingNewQuads = 0;

private final ChunkMeshBufferBuilder translucentVertexBuffer;

BSPWorkspace(TQuad[] quads, SectionPos sectionPos, boolean prepareNodeReuse, ChunkMeshBufferBuilder translucentVertexBuffer) {
BSPWorkspace(TQuad[] quads, SectionPos sectionPos, boolean prepareNodeReuse, QuadSplittingMode quadSplittingMode, ChunkMeshBufferBuilder translucentVertexBuffer) {
super(quads);
this.sectionPos = sectionPos;
this.prepareNodeReuse = prepareNodeReuse;
this.quadSplittingMode = quadSplittingMode;
this.translucentVertexBuffer = translucentVertexBuffer;

if (quadSplittingMode.allowsSplitting()) {
this.remainingNewQuads = quadSplittingMode.getMaxExtraQuads(quads.length);
}
}

boolean canSplitQuads() {
return this.remainingNewQuads > 0;
}

// TODO: better bidirectional triggering: integrate bidirectionality in GFNI if
// top-level topo sorting isn't used anymore (and only use half as much memory
// by not storing it double)
// by not storing trigger planes twice)
void addAlignedPartitionPlane(int axis, float distance) {
this.result.addDoubleSidedAlignedPlane(this.sectionPos, axis, distance);
}
Expand All @@ -48,6 +60,8 @@ int pushQuad(FullTQuad quad) {
return -1;
}

this.remainingNewQuads--;

this.translucentVertexBuffer.push(quad.getVertices(), DefaultMaterials.TRANSLUCENT);

var index = this.size();
Expand Down
Loading

0 comments on commit 0939e4f

Please sign in to comment.