Skip to content

Commit fa97f06

Browse files
authored
Fix translucency sorting for water-logged glass panes, and related follow-up work (#2352)
* rename some things for clarity * fix waterlogged glass panes (once again, but more this time) by avoiding distance sorting through the detection of primary intersectors when geometry is intersecting and then sorting them in a fixed order * use Mth.clamp for clarity * refactor buffer and sort result handling, buffers are now freed immediately instead of keeping them to avoid memory usage buffer caching would be a better solution but that's complicated and doesn't currently work correctly * reduce number of unique triggers by around 5 percent without impacting sorting or building performance * importantly sort a little farther away, sort tasks are fast * use defer zero frames for important sort tasks by default * fix build * clarify authorship of BitArray * fix bug with radix sort for SNR heuristic in BSP partition generating wrong indexes * skip heuristic if there's no quads * refactor primary intersector detection to handle large cases better, also removed the warning message about unpartitionable geometry as it seems to not be a relevant problem * fix topo sorting in some situations where the dot product was wrongly not recalculated when the normal is quantized. also fixed aligned quads not receiving the more accurate center based on the average of the unique vertexes. * tune primary intersector detection to handle situations where only a small amount of geometry is intersecting
1 parent 06bc27e commit fa97f06

41 files changed

Lines changed: 883 additions & 629 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/main/java/net/caffeinemc/mods/sodium/client/gui/SodiumGameOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static class PerformanceSettings {
4949
public boolean sortingEnabled = true;
5050

5151
public SortBehavior getSortBehavior() {
52-
return this.sortingEnabled ? SortBehavior.DYNAMIC_DEFER_NEARBY_ONE_FRAME : SortBehavior.OFF;
52+
return this.sortingEnabled ? SortBehavior.DYNAMIC_DEFER_NEARBY_ZERO_FRAMES : SortBehavior.OFF;
5353
}
5454
}
5555

src/main/java/net/caffeinemc/mods/sodium/client/model/quad/properties/ModelQuadFacing.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public enum ModelQuadFacing {
4343
public static final int OPPOSING_X = 1 << ModelQuadFacing.POS_X.ordinal() | 1 << ModelQuadFacing.NEG_X.ordinal();
4444
public static final int OPPOSING_Y = 1 << ModelQuadFacing.POS_Y.ordinal() | 1 << ModelQuadFacing.NEG_Y.ordinal();
4545
public static final int OPPOSING_Z = 1 << ModelQuadFacing.POS_Z.ordinal() | 1 << ModelQuadFacing.NEG_Z.ordinal();
46+
public static final int UNASSIGNED_MASK = 1 << ModelQuadFacing.UNASSIGNED.ordinal();
4647

4748
public static ModelQuadFacing fromDirection(Direction dir) {
4849
return switch (dir) {
@@ -120,4 +121,12 @@ public static ModelQuadFacing fromNormal(float x, float y, float z) {
120121
public static ModelQuadFacing fromPackedNormal(int normal) {
121122
return fromNormal(NormI8.unpackX(normal), NormI8.unpackY(normal), NormI8.unpackZ(normal));
122123
}
124+
125+
public static boolean bitmapIsOpposingAligned(int bitmap) {
126+
return bitmap == OPPOSING_X || bitmap == OPPOSING_Y || bitmap == OPPOSING_Z;
127+
}
128+
129+
public static boolean bitmapHasUnassigned(int bitmap) {
130+
return (bitmap & UNASSIGNED_MASK) != 0;
131+
}
123132
}

src/main/java/net/caffeinemc/mods/sodium/client/render/SodiumWorldRenderer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@
3939
import net.minecraft.world.level.block.entity.BlockEntity;
4040
import net.minecraft.world.phys.AABB;
4141
import net.minecraft.world.phys.Vec3;
42+
import org.joml.Vector3d;
43+
4244
import java.util.Collection;
4345
import java.util.Iterator;
4446
import java.util.SortedSet;
4547

46-
import org.joml.Vector3d;
47-
4848
/**
4949
* Provides an extension to vanilla's {@link LevelRenderer}.
5050
*/

src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSection.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,7 @@ public void setTranslucentData(TranslucentData translucentData) {
120120
if (translucentData == null) {
121121
throw new IllegalArgumentException("new translucentData cannot be null");
122122
}
123-
if (this.translucentData != null && this.translucentData != translucentData) {
124-
this.translucentData.delete();
125-
}
123+
126124
this.translucentData = translucentData;
127125
}
128126

@@ -136,9 +134,6 @@ public void delete() {
136134
this.taskCancellationToken.setCancelled();
137135
this.taskCancellationToken = null;
138136
}
139-
if (this.translucentData != null) {
140-
this.translucentData.delete();
141-
}
142137

143138
this.clearRenderState();
144139
this.disposed = true;

src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
3232
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior.DeferMode;
3333
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior.PriorityMode;
34+
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.DynamicTopoData;
3435
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.NoData;
35-
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.TopoSortDynamicData;
3636
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.TranslucentData;
3737
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.trigger.CameraMovement;
3838
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.trigger.SortTriggering;
@@ -303,7 +303,7 @@ public void uploadChunks() {
303303
this.needsGraphUpdate = this.needsGraphUpdate || this.processChunkBuildResults(results);
304304

305305
for (var result : results) {
306-
result.deleteAfterUploadSafe();
306+
result.destroy();
307307
}
308308
}
309309

@@ -325,9 +325,10 @@ private boolean processChunkBuildResults(ArrayList<BuilderTaskOutput> results) {
325325
// a rebuild always generates new translucent data which means applyTriggerChanges isn't necessary
326326
result.render.setTranslucentData(chunkBuildOutput.translucentData);
327327
}
328-
} else if (result instanceof ChunkSortOutput chunkSortOutput
329-
&& chunkSortOutput.dynamicData instanceof TopoSortDynamicData data) {
330-
this.sortTriggering.applyTriggerChanges(data, result.render.getPosition(), this.cameraPosition);
328+
} else if (result instanceof ChunkSortOutput sortOutput
329+
&& sortOutput.getTopoSorter() != null
330+
&& result.render.getTranslucentData() instanceof DynamicTopoData data) {
331+
this.sortTriggering.applyTriggerChanges(data, sortOutput.getTopoSorter(), result.render.getPosition(), this.cameraPosition);
331332
}
332333

333334
var job = result.render.getTaskCancellationToken();
@@ -358,10 +359,8 @@ private static List<BuilderTaskOutput> filterChunkBuildResults(ArrayList<Builder
358359
var map = new Reference2ReferenceLinkedOpenHashMap<RenderSection, BuilderTaskOutput>();
359360

360361
for (var output : outputs) {
361-
// when outdated or duplicate outputs are thrown out, make sure to delete their
362-
// buffers to avoid memory leaks
362+
// throw out outdated or duplicate outputs
363363
if (output.render.isDisposed() || output.render.getLastUploadFrame() > output.submitTime) {
364-
output.deleteFully();
365364
continue;
366365
}
367366

@@ -370,9 +369,6 @@ private static List<BuilderTaskOutput> filterChunkBuildResults(ArrayList<Builder
370369

371370
if (previous == null || previous.submitTime < output.submitTime) {
372371
map.put(render, output);
373-
if (previous != null) {
374-
previous.deleteFully();
375-
}
376372
}
377373
}
378374

@@ -542,7 +538,7 @@ public void destroy() {
542538
this.builder.shutdown(); // stop all the workers, and cancel any tasks
543539

544540
for (var result : this.collectChunkBuildResults()) {
545-
result.deleteFully(); // delete resources for any pending tasks (including those that were cancelled)
541+
result.destroy(); // delete resources for any pending tasks (including those that were cancelled)
546542
}
547543

548544
for (var section : this.sectionByPosition.values()) {
@@ -581,7 +577,7 @@ public void scheduleSort(long sectionPos, boolean isDirectTrigger) {
581577
var pendingUpdate = ChunkUpdateType.SORT;
582578
var priorityMode = SodiumClientMod.options().performance.getSortBehavior().getPriorityMode();
583579
if (priorityMode == PriorityMode.ALL
584-
|| priorityMode == PriorityMode.NEARBY && this.shouldPrioritizeTask(section)) {
580+
|| priorityMode == PriorityMode.NEARBY && this.shouldPrioritizeTask(section, NEARBY_SORT_DISTANCE)) {
585581
pendingUpdate = ChunkUpdateType.IMPORTANT_SORT;
586582
}
587583
pendingUpdate = ChunkUpdateType.getPromotionUpdateType(section.getPendingUpdate(), pendingUpdate);
@@ -602,7 +598,7 @@ public void scheduleRebuild(int x, int y, int z, boolean important) {
602598
if (section != null && section.isBuilt()) {
603599
ChunkUpdateType pendingUpdate;
604600

605-
if (allowImportantRebuilds() && (important || this.shouldPrioritizeTask(section))) {
601+
if (allowImportantRebuilds() && (important || this.shouldPrioritizeTask(section, NEARBY_REBUILD_DISTANCE))) {
606602
pendingUpdate = ChunkUpdateType.IMPORTANT_REBUILD;
607603
} else {
608604
pendingUpdate = ChunkUpdateType.REBUILD;
@@ -618,9 +614,10 @@ public void scheduleRebuild(int x, int y, int z, boolean important) {
618614
}
619615

620616
private static final float NEARBY_REBUILD_DISTANCE = Mth.square(16.0f);
617+
private static final float NEARBY_SORT_DISTANCE = Mth.square(25.0f);
621618

622-
private boolean shouldPrioritizeTask(RenderSection section) {
623-
return this.cameraPosition != null && section.getSquaredDistance(this.cameraBlockPos) < NEARBY_REBUILD_DISTANCE;
619+
private boolean shouldPrioritizeTask(RenderSection section, float distance) {
620+
return this.cameraBlockPos != null && section.getSquaredDistance(this.cameraBlockPos) < distance;
624621
}
625622

626623
private static boolean allowImportantRebuilds() {

src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/BuilderTaskOutput.java

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,12 @@
55
public abstract class BuilderTaskOutput {
66
public final RenderSection render;
77
public final int submitTime;
8-
private boolean fullyDeleted;
98

109
public BuilderTaskOutput(RenderSection render, int buildTime) {
1110
this.render = render;
1211
this.submitTime = buildTime;
1312
}
1413

15-
public void deleteFully() {
16-
this.fullyDeleted = true;
17-
this.deleteAfterUpload();
18-
}
19-
20-
public void deleteAfterUploadSafe() {
21-
if (!this.fullyDeleted) {
22-
this.deleteAfterUpload();
23-
}
24-
}
25-
26-
protected void deleteAfterUpload() {
14+
public void destroy() {
2715
}
2816
}

src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/ChunkBuildBuffers.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,23 +80,21 @@ public BuiltSectionMeshParts createMesh(TerrainRenderPass pass, boolean forceUna
8080
vertexCount += buffer.count();
8181
}
8282

83-
if (forceUnassigned) {
84-
vertexRanges[ModelQuadFacing.UNASSIGNED.ordinal()] = new VertexRange(0, vertexCount);
85-
}
86-
8783
if (vertexCount == 0) {
8884
return null;
8985
}
9086

87+
if (forceUnassigned) {
88+
vertexRanges[ModelQuadFacing.UNASSIGNED.ordinal()] = new VertexRange(0, vertexCount);
89+
}
90+
9191
var mergedBuffer = new NativeBuffer(vertexCount * this.vertexType.getVertexFormat().getStride());
9292
var mergedBufferBuilder = mergedBuffer.getDirectBuffer();
9393

9494
for (var buffer : vertexBuffers) {
9595
mergedBufferBuilder.put(buffer);
9696
}
9797

98-
mergedBufferBuilder.flip(); // TODO: necessary?
99-
10098
return new BuiltSectionMeshParts(mergedBuffer, vertexRanges);
10199
}
102100

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package net.caffeinemc.mods.sodium.client.render.chunk.compile;
22

33
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
4+
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
5+
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts;
46
import net.caffeinemc.mods.sodium.client.render.chunk.terrain.TerrainRenderPass;
5-
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.PresentTranslucentData;
67
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.TranslucentData;
7-
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionMeshParts;
8-
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
98

109
import java.util.Map;
1110

@@ -15,7 +14,7 @@
1514
* finishing its work and not before the result is processed, the result will
1615
* instead be discarded.
1716
*/
18-
public class ChunkBuildOutput extends BuilderTaskOutput implements OutputWithIndexData {
17+
public class ChunkBuildOutput extends ChunkSortOutput {
1918
public final BuiltSectionInfo info;
2019
public final TranslucentData translucentData;
2120
public final Map<TerrainRenderPass, BuiltSectionMeshParts> meshes;
@@ -34,32 +33,11 @@ public BuiltSectionMeshParts getMesh(TerrainRenderPass pass) {
3433
}
3534

3635
@Override
37-
public PresentTranslucentData getTranslucentData() {
38-
if (this.translucentData instanceof PresentTranslucentData present) {
39-
return present;
40-
}
41-
return null;
42-
}
43-
44-
@Override
45-
public void deleteAfterUpload() {
46-
super.deleteAfterUpload();
47-
48-
// delete translucent data if it's not persisted for dynamic sorting
49-
if (this.translucentData != null && !this.translucentData.retainAfterUpload()) {
50-
this.translucentData.delete();
51-
}
36+
public void destroy() {
37+
super.destroy();
5238

5339
for (BuiltSectionMeshParts data : this.meshes.values()) {
5440
data.getVertexData().free();
5541
}
5642
}
57-
58-
@Override
59-
public void deleteFully() {
60-
super.deleteFully();
61-
if (this.translucentData != null) {
62-
this.translucentData.delete();
63-
}
64-
}
6543
}
Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,57 @@
11
package net.caffeinemc.mods.sodium.client.render.chunk.compile;
22

33
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
4-
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.DynamicData;
5-
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.PresentTranslucentData;
4+
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.DynamicTopoData;
5+
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.SortData;
6+
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.data.Sorter;
7+
import net.caffeinemc.mods.sodium.client.util.NativeBuffer;
68

7-
public class ChunkSortOutput extends BuilderTaskOutput implements OutputWithIndexData {
8-
public final DynamicData dynamicData;
9+
public class ChunkSortOutput extends BuilderTaskOutput implements SortData {
10+
private NativeBuffer indexBuffer;
11+
private boolean reuseUploadedIndexData;
12+
private DynamicTopoData.DynamicTopoSorter topoSorter;
913

10-
public ChunkSortOutput(RenderSection render, int buildTime, DynamicData dynamicData) {
14+
public ChunkSortOutput(RenderSection render, int buildTime) {
1115
super(render, buildTime);
16+
}
17+
18+
public ChunkSortOutput(RenderSection render, int buildTime, Sorter data) {
19+
this(render, buildTime);
20+
this.copyResultFrom(data);
21+
}
1222

13-
this.dynamicData = dynamicData;
23+
public void copyResultFrom(Sorter sorter) {
24+
this.indexBuffer = sorter.getIndexBuffer();
25+
this.reuseUploadedIndexData = false;
26+
if (sorter instanceof DynamicTopoData.DynamicTopoSorter topoSorterInstance) {
27+
this.topoSorter = topoSorterInstance;
28+
}
29+
}
30+
31+
public void markAsReusingUploadedData() {
32+
this.reuseUploadedIndexData = true;
1433
}
1534

1635
@Override
17-
public PresentTranslucentData getTranslucentData() {
18-
return this.dynamicData;
36+
public NativeBuffer getIndexBuffer() {
37+
return this.indexBuffer;
1938
}
2039

21-
// doesn't implement deletion because the task doesn't allocate any new buffers.
22-
// the buffers used belong to the section and are deleted when it is deleted.
23-
// buffers created during section building are deleted at section deletion or
24-
// when the rebuild is cancelled.
40+
@Override
41+
public boolean isReusingUploadedIndexData() {
42+
return this.reuseUploadedIndexData;
43+
}
44+
45+
public DynamicTopoData.DynamicTopoSorter getTopoSorter() {
46+
return this.topoSorter;
47+
}
48+
49+
@Override
50+
public void destroy() {
51+
super.destroy();
52+
53+
if (this.indexBuffer != null) {
54+
this.indexBuffer.free();
55+
}
56+
}
2557
}

src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/compile/OutputWithIndexData.java

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)