Skip to content

Commit

Permalink
Allow configuring the Radix sort to be stable
Browse files Browse the repository at this point in the history
The radix sort is inherently stable, but the fallback sort
it uses on small arrays (or when memory cannot be allocated)
was not.

This fixes issues with Banner rendering in Minecraft, since
the model uses many identical overlapping cuboids, which must
retain their draw order.
  • Loading branch information
jellysquid3 committed Jan 24, 2025
1 parent 2fe7c8b commit 4222e5d
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ static private BSPNode buildSNRLeafNodeFromQuads(BSPWorkspace workspace, IntArra
perm[i] = i;
}

RadixSort.sortIndirect(perm, keys);
RadixSort.sortIndirect(perm, keys, false);

for (int i = 0; i < indexCount; i++) {
perm[i] = indexBuffer[perm[i]];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ static void distanceSortDirect(IntBuffer indexBuffer, TQuad[] quads, Vector3fc c
perm[idx] = idx;
}

RadixSort.sortIndirect(perm, keys);
RadixSort.sortIndirect(perm, keys, false);

for (int idx = 0; idx < quads.length; idx++) {
TranslucentData.writeQuadVertexIndexes(indexBuffer, perm[idx]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ private static StaticNormalRelativeData fromDoubleUnaligned(int[] vertexCounts,
perm[q] = q;
}

RadixSort.sortIndirect(perm, keys);
RadixSort.sortIndirect(perm, keys, false);

for (int i = 0; i < quads.length; i++) {
TranslucentData.writeQuadVertexIndexes(indexBuffer, perm[i]);
Expand Down Expand Up @@ -102,7 +102,7 @@ private static StaticNormalRelativeData fromMixed(int[] vertexCounts,
perm[idx] = idx;
}

RadixSort.sortIndirect(perm, keys);
RadixSort.sortIndirect(perm, keys, false);

for (int idx = 0; idx < count; idx++) {
TranslucentData.writeQuadVertexIndexes(indexBuffer, perm[idx]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,29 @@ private static void prefixSums(int[][] offsets) {
}

/**
* <p>Sorts the specified array according to the natural ascending order using an unstable, out-of-place, indirect,
* 256-way LSD radix sort.</p>
*
* <p>This algorithm is well suited for large arrays of integers, especially when they are uniformly distributed
* over the entire range of the data type.</p>
* <p>Sorts the specified array according to the natural ascending order using an out-of-place, indirect,
* 256-way LSD radix sort. This algorithm is well suited for large arrays of integers, especially when they are
* uniformly distributed over the entire range of the data type.</p>
*
* <p>This method implements an <em>indirect</em> sort. The elements of {@param perm} (which must be
* exactly the numbers in the interval {@code [0..perm.length)}) will be permuted so that
* {@code x[perm[i]] < x[perm[i + 1]]}.</p>
*
* <p>While this radix sort is very fast on larger arrays, there is a certain amount of fixed cost involved in
* <p>While this implementation is very fast on larger arrays, there is a certain amount of fixed cost involved in
* computing the histogram and prefix sums. Because of this, a fallback algorithm (currently quick sort) is used
* for very small arrays to ensure this method performs well for all inputs of all sizes.</p>
*
* <p>If {@param stable} is true, the sorting algorithm is defined to be <i>stable</i>, and duplicate keys will be
* returned in the ordering given by {@param keys}. Using a stable sort is slower than an unstable sort, but the
* overall time complexity and memory requirements are the same.</p>
*
* @param perm a permutation array indexing {@param keys}.
* @param keys the array of elements to be sorted.
* @param stable whether the sort is allowed to re-order duplicate keys
*/
public static void sortIndirect(final int[] perm, final int[] keys) {
public static void sortIndirect(final int[] perm, final int[] keys, boolean stable) {
if (perm.length <= RADIX_SORT_THRESHOLD) {
smallSort(perm, keys);
smallSort(perm, keys, stable);
return;
}

Expand All @@ -64,7 +67,7 @@ public static void sortIndirect(final int[] perm, final int[] keys) {
next = new int[perm.length];
} catch (OutOfMemoryError oom) {
// Not enough memory to perform an out-of-place sort, so use an in-place alternative.
fallbackInPlaceSort(perm, keys);
fallbackSort(perm, keys, stable);
return;
}

Expand Down Expand Up @@ -102,17 +105,21 @@ private static void sortIndirect(final int[] perm,
}
}

private static void smallSort(int[] perm, int[] keys) {
private static void smallSort(int[] perm, int[] keys, boolean stable) {
if (perm.length <= 1) {
return;
}

fallbackInPlaceSort(perm, keys);
fallbackSort(perm, keys, stable);
}

// Fallback sorting method which is guaranteed to be in-place and not require additional memory.
private static void fallbackInPlaceSort(int[] perm, int[] keys) {
private static void fallbackSort(int[] perm, int[] keys, boolean stable) {
IntArrays.quickSortIndirect(perm, keys);

if (stable) {
IntArrays.stabilize(perm, keys);
}
}

private static int extractDigit(int key, int digit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ private abstract static class AbstractSorter implements VertexSortingExtended {
perm[index] = index;
}

RadixSort.sortIndirect(perm, keys);
RadixSort.sortIndirect(perm, keys, true);

return perm;
}
Expand Down Expand Up @@ -134,7 +134,7 @@ public static int[] sort(ByteBuffer buffer, int vertexCount, int vertexStride, V
pVertex2 += primitiveStride;
}

RadixSort.sortIndirect(perm, keys);
RadixSort.sortIndirect(perm, keys, true);

return perm;
}
Expand Down

0 comments on commit 4222e5d

Please sign in to comment.