diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java index 75e6757df8..6212573773 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/ChunkRenderList.java @@ -1,11 +1,14 @@ package net.caffeinemc.mods.sodium.client.render.chunk.lists; +import net.caffeinemc.mods.sodium.client.render.chunk.LocalSectionIndex; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionFlags; +import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; +import net.caffeinemc.mods.sodium.client.render.viewport.CameraTransform; +import net.caffeinemc.mods.sodium.client.util.iterator.ByteArrayIterator; import net.caffeinemc.mods.sodium.client.util.iterator.ByteIterator; import net.caffeinemc.mods.sodium.client.util.iterator.ReversibleByteArrayIterator; -import net.caffeinemc.mods.sodium.client.util.iterator.ByteArrayIterator; -import net.caffeinemc.mods.sodium.client.render.chunk.region.RenderRegion; +import net.minecraft.util.Mth; import org.jetbrains.annotations.Nullable; public class ChunkRenderList { @@ -37,6 +40,39 @@ public void reset(int frame) { this.lastVisibleFrame = frame; } + // clamping the relative camera position to the region bounds means there can only be very few different distances + private static final int SORTING_HISTOGRAM_SIZE = RenderRegion.REGION_WIDTH + RenderRegion.REGION_HEIGHT + RenderRegion.REGION_LENGTH - 2; + + public void sortSections(CameraTransform transform, int[] sortItems) { + var cameraX = Mth.clamp((transform.intX >> 4) - this.region.getChunkX(), 0, RenderRegion.REGION_WIDTH - 1); + var cameraY = Mth.clamp((transform.intY >> 4) - this.region.getChunkY(), 0, RenderRegion.REGION_HEIGHT - 1); + var cameraZ = Mth.clamp((transform.intZ >> 4) - this.region.getChunkZ(), 0, RenderRegion.REGION_LENGTH - 1); + + int[] histogram = new int[SORTING_HISTOGRAM_SIZE]; + + for (int i = 0; i < this.sectionsWithGeometryCount; i++) { + var index = this.sectionsWithGeometry[i] & 0xFF; // makes sure the byte -> int conversion is unsigned + var x = Math.abs(LocalSectionIndex.unpackX(index) - cameraX); + var y = Math.abs(LocalSectionIndex.unpackY(index) - cameraY); + var z = Math.abs(LocalSectionIndex.unpackZ(index) - cameraZ); + + var distance = x + y + z; + histogram[distance]++; + sortItems[i] = distance << 8 | index; + } + + // prefix sum to calculate indexes + for (int i = 1; i < SORTING_HISTOGRAM_SIZE; i++) { + histogram[i] += histogram[i - 1]; + } + + for (int i = 0; i < this.sectionsWithGeometryCount; i++) { + var item = sortItems[i]; + var distance = item >>> 8; + this.sectionsWithGeometry[--histogram[distance]] = (byte) item; + } + } + public void add(RenderSection render) { if (this.size >= RenderRegion.REGION_SIZE) { throw new ArrayIndexOutOfBoundsException("Render list is full"); diff --git a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java index 035e1b69c4..6d378f57e0 100644 --- a/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java +++ b/common/src/main/java/net/caffeinemc/mods/sodium/client/render/chunk/lists/VisibleChunkCollector.java @@ -1,5 +1,6 @@ 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.ChunkUpdateType; import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection; @@ -62,6 +63,8 @@ private void addToRebuildLists(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 transform = viewport.getTransform(); @@ -70,20 +73,29 @@ public SortedRenderLists createRenderLists(Viewport viewport) { var cameraZ = transform.intZ >> (4 + RenderRegion.REGION_LENGTH_SH); var size = this.sortedRenderLists.size(); - var items = new int[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); - items[i] = (x + y + z) << 16 | i; + sortItems[i] = (x + y + z) << 16 | i; } - Arrays.sort(items); + IntArrays.unstableSort(sortItems, 0, size); var sorted = new ObjectArrayList(size); - for (var key : items) { - sorted.add(this.sortedRenderLists.get(key & 0xFFFF)); + for (var i = 0; i < size; i++) { + var key = sortItems[i]; + var renderList = this.sortedRenderLists.get(key & 0xFFFF); + sorted.add(renderList); + } + + for (var list : sorted) { + list.sortSections(transform, sortItems); } return new SortedRenderLists(sorted);