From 29caa4b9097ad1066d53bad84b3c805a38e6db11 Mon Sep 17 00:00:00 2001 From: douira Date: Tue, 31 Dec 2024 05:01:31 +0100 Subject: [PATCH] add region sorting to fix translucency sorting issues, tree traversal only does correct in-region sorting, not between regions --- .../render/chunk/RenderSectionManager.java | 4 +- .../lists/FallbackVisibleChunkCollector.java | 4 +- .../chunk/lists/RenderListProvider.java | 54 +++++++++++++++++++ .../lists/VisibleChunkCollectorAsync.java | 26 +++++---- .../lists/VisibleChunkCollectorSync.java | 46 +++++----------- .../render/chunk/tree/TraversableTree.java | 3 ++ 6 files changed, 89 insertions(+), 48 deletions(-) create mode 100644 common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/RenderListProvider.java diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java index 29f6861eaa..110d890f29 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/RenderSectionManager.java @@ -423,7 +423,7 @@ private void processRenderListUpdate(Viewport viewport) { this.renderableSectionTree.prepareForTraversal(); this.renderableSectionTree.traverse(visitor, viewport, searchDistance); - this.renderLists = visitor.createRenderLists(); + this.renderLists = visitor.createRenderLists(viewport); this.frustumTaskLists = visitor.getPendingTaskLists(); this.globalTaskLists = null; this.renderTree = null; @@ -432,7 +432,7 @@ private void processRenderListUpdate(Viewport viewport) { var visibleCollector = new VisibleChunkCollectorAsync(this.regions, this.frame); bestTree.traverse(visibleCollector, viewport, this.getSearchDistance()); - this.renderLists = visibleCollector.createRenderLists(); + this.renderLists = visibleCollector.createRenderLists(viewport); var end = System.nanoTime(); var time = end - start; diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/FallbackVisibleChunkCollector.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/FallbackVisibleChunkCollector.java index 9eec7b4eaa..70e1d33689 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/FallbackVisibleChunkCollector.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/FallbackVisibleChunkCollector.java @@ -13,8 +13,8 @@ public FallbackVisibleChunkCollector(Viewport viewport, float buildDistance, Lon this.renderListCollector = new VisibleChunkCollectorAsync(regions, frame); } - public SortedRenderLists createRenderLists() { - return this.renderListCollector.createRenderLists(); + public SortedRenderLists createRenderLists(Viewport viewport) { + return this.renderListCollector.createRenderLists(viewport); } @Override diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/RenderListProvider.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/RenderListProvider.java new file mode 100644 index 0000000000..156e66edfa --- /dev/null +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/RenderListProvider.java @@ -0,0 +1,54 @@ +package net.caffeinemc.mods.sodium.client.render.chunk.lists; + +import it.unimi.dsi.fastutil.ints.IntArrays; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; +import net.caffeinemc.mods.sodium.client.render.viewport.Viewport; + +public interface RenderListProvider { + ObjectArrayList getUnsortedRenderLists(); + + int[] getCachedSortItems(); + + void setCachedSortItems(int[] sortItems); + + default SortedRenderLists createRenderLists(Viewport viewport) { + // sort the regions by distance to fix rare region ordering bugs + var sectionPos = viewport.getChunkCoord(); + var cameraX = sectionPos.getX() >> RenderRegion.REGION_WIDTH_SH; + var cameraY = sectionPos.getY() >> RenderRegion.REGION_HEIGHT_SH; + var cameraZ = sectionPos.getZ() >> RenderRegion.REGION_LENGTH_SH; + + var unsortedRenderLists = this.getUnsortedRenderLists(); + var size = unsortedRenderLists.size(); + + var sortItems = this.getCachedSortItems(); + if (sortItems.length < size) { + sortItems = new int[size]; + this.setCachedSortItems(sortItems); + } + + for (var i = 0; i < size; i++) { + var region = unsortedRenderLists.get(i).getRegion(); + var x = Math.abs(region.getX() - cameraX); + var y = Math.abs(region.getY() - cameraY); + var z = Math.abs(region.getZ() - cameraZ); + sortItems[i] = (x + y + z) << 16 | i; + } + + IntArrays.unstableSort(sortItems, 0, size); + + var sorted = new ObjectArrayList(size); + for (var i = 0; i < size; i++) { + var key = sortItems[i]; + var renderList = unsortedRenderLists.get(key & 0xFFFF); + sorted.add(renderList); + } + + for (var list : sorted) { + list.sortSections(sectionPos, sortItems); + } + + return new SortedRenderLists(sorted); + } +} diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorAsync.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorAsync.java index cc82e94d33..1dc15df9ab 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorAsync.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorAsync.java @@ -1,22 +1,15 @@ package net.caffeinemc.mods.sodium.client.render.chunk.lists; -import it.unimi.dsi.fastutil.ints.IntArrays; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.caffeinemc.mods.sodium.client.render.chunk.LocalSectionIndex; import net.caffeinemc.mods.sodium.client.render.chunk.occlusion.SectionTree; import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegionManager; -import net.caffeinemc.mods.sodium.client.render.viewport.Viewport; - -import java.util.ArrayDeque; -import java.util.EnumMap; -import java.util.Map; -import java.util.Queue; /** * The async visible chunk collector is passed into a section tree to collect visible chunks. */ -public class VisibleChunkCollectorAsync implements SectionTree.VisibleSectionVisitor { +public class VisibleChunkCollectorAsync implements SectionTree.VisibleSectionVisitor, RenderListProvider { private final RenderRegionManager regions; private final int frame; @@ -55,7 +48,20 @@ public void visit(int x, int y, int z) { renderList.add(sectionIndex); } - public SortedRenderLists createRenderLists() { - return new SortedRenderLists(this.sortedRenderLists); + private static int[] sortItems = new int[RenderRegion.REGION_SIZE]; + + @Override + public ObjectArrayList getUnsortedRenderLists() { + return this.sortedRenderLists; + } + + @Override + public int[] getCachedSortItems() { + return sortItems; + } + + @Override + public void setCachedSortItems(int[] sortItems) { + VisibleChunkCollectorAsync.sortItems = sortItems; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorSync.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorSync.java index 6c1edffcc1..e69a647cb7 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorSync.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollectorSync.java @@ -1,6 +1,5 @@ package net.caffeinemc.mods.sodium.client.render.chunk.lists; -import it.unimi.dsi.fastutil.ints.IntArrays; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionFlags; @@ -13,7 +12,7 @@ /** * The sync visible chunk collector is passed into the graph search occlusion culler to collect visible chunks. */ -public class VisibleChunkCollectorSync extends SectionTree { +public class VisibleChunkCollectorSync extends SectionTree implements RenderListProvider { private final ObjectArrayList sortedRenderLists; public VisibleChunkCollectorSync(Viewport viewport, float buildDistance, int frame, CullType cullType, Level level) { @@ -44,39 +43,18 @@ public void visit(RenderSection section) { private static int[] sortItems = new int[RenderRegion.REGION_SIZE]; - public SortedRenderLists createRenderLists(Viewport viewport) { - // sort the regions by distance to fix rare region ordering bugs - var sectionPos = viewport.getChunkCoord(); - var cameraX = sectionPos.getX() >> RenderRegion.REGION_WIDTH_SH; - var cameraY = sectionPos.getY() >> RenderRegion.REGION_HEIGHT_SH; - var cameraZ = sectionPos.getZ() >> RenderRegion.REGION_LENGTH_SH; - var size = this.sortedRenderLists.size(); - - if (sortItems.length < size) { - sortItems = new int[size]; - } - - for (var i = 0; i < size; i++) { - var region = this.sortedRenderLists.get(i).getRegion(); - var x = Math.abs(region.getX() - cameraX); - var y = Math.abs(region.getY() - cameraY); - var z = Math.abs(region.getZ() - cameraZ); - sortItems[i] = (x + y + z) << 16 | i; - } - - IntArrays.unstableSort(sortItems, 0, size); - - var sorted = new ObjectArrayList(size); - for (var i = 0; i < size; i++) { - var key = sortItems[i]; - var renderList = this.sortedRenderLists.get(key & 0xFFFF); - sorted.add(renderList); - } + @Override + public ObjectArrayList getUnsortedRenderLists() { + return this.sortedRenderLists; + } - for (var list : sorted) { - list.sortSections(sectionPos, sortItems); - } + @Override + public int[] getCachedSortItems() { + return sortItems; + } - return new SortedRenderLists(sorted); + @Override + public void setCachedSortItems(int[] sortItems) { + VisibleChunkCollectorSync.sortItems = sortItems; } } diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/tree/TraversableTree.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/tree/TraversableTree.java index f4f3c881b1..002059efd8 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/tree/TraversableTree.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/tree/TraversableTree.java @@ -5,6 +5,9 @@ import net.caffeinemc.mods.sodium.client.render.viewport.Viewport; import org.joml.FrustumIntersection; +/** + * A traversable tree is a tree of sections that can be traversed with a distance limit and a frustum. It traverses the sections in visual front-to-back order, so that they can be directly put into a render list. Note however that ordering regions by adding them to the list the first time one of their sections is visited does not yield the correct order. This is because the sections are traversed in visual order, not ordered by distance from the camera. + */ public class TraversableTree extends Tree { private static final int INSIDE_FRUSTUM = 0b01; private static final int INSIDE_DISTANCE = 0b10;