123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- using System;
-
- // Utilities that do not allocate.
-
- namespace UnityEngine.Rendering.Universal
- {
- // Non-allocating sorts.
- // NOTE: Do NOT make this public. It's likely that these will be merged/moved to core or replaced by something else.
- internal struct Sorting
- {
- // Add profling samplers to sorts as they often are a bottleneck when scaling things up.
- // By default avoid sampling recursion, but these can be used externally as well.
- static public ProfilingSampler s_QuickSortSampler = new ProfilingSampler("QuickSort");
- static public ProfilingSampler s_InsertionSortSampler = new ProfilingSampler("InsertionSort");
-
- public static void QuickSort<T>(T[] data, Func<T, T, int> compare)
- {
- using var scope = new ProfilingScope(s_QuickSortSampler);
- QuickSort<T>(data, 0, data.Length - 1, compare);
- }
-
- // <summary>
- // A non-allocating predicated sub-array quick sort for managed arrays.
- //
- // Example: Sorting.QuickSort(test, 0, test.Length - 1, (int a, int b) => a - b);
- // </summary>
- // <param name="data"></param>
- // <param name="start"></param>
- // <param name="end"> Then end param is inclusive! </param>
- // <param name="compare"> Should return int -1, 0 or 1 </param>
- //
- // NOTE: Similar to UnityEngine.Rendering.CoreUnsafeUtils.QuickSort in CoreUnsafeUtils.cs,
- // we should see if these could be merged in the future.
- //
- // TODO: parallel alternative
- public static void QuickSort<T>(T[] data, int start, int end, Func<T, T, int> compare)
- {
- int diff = end - start;
- if (diff < 1)
- return;
- if (diff < 8)
- {
- InsertionSort(data, start, end, compare);
- return;
- }
-
- Assertions.Assert.IsTrue((uint)start < data.Length);
- Assertions.Assert.IsTrue((uint)end < data.Length); // end == inclusive
-
- if (start < end)
- {
- int pivot = Partition<T>(data, start, end, compare);
-
- if (pivot >= 1)
- QuickSort<T>(data, start, pivot, compare);
-
- if (pivot + 1 < end)
- QuickSort<T>(data, pivot + 1, end, compare);
- }
- }
-
- static T Median3Pivot<T>(T[] data, int start, int pivot, int end, Func<T, T, int> compare)
- {
- void Swap(int a, int b)
- {
- var tmp = data[a];
- data[a] = data[b];
- data[b] = tmp;
- }
-
- if (compare(data[end], data[start]) < 0) Swap(start, end);
- if (compare(data[pivot], data[start]) < 0) Swap(start, pivot);
- if (compare(data[end], data[pivot]) < 0) Swap(pivot, end);
- return data[pivot];
- }
-
- static int Partition<T>(T[] data, int start, int end, Func<T, T, int> compare)
- {
- int diff = end - start;
- int pivot = start + diff / 2;
-
- var pivotValue = Median3Pivot(data, start, pivot, end, compare);
-
- while (true)
- {
- while (compare(data[start], pivotValue) < 0) ++start;
- while (compare(data[end], pivotValue) > 0) --end;
-
- if (start >= end)
- {
- return end;
- }
-
- var tmp = data[start];
- data[start++] = data[end];
- data[end--] = tmp;
- }
- }
-
- static public void InsertionSort<T>(T[] data, Func<T, T, int> compare)
- {
- using var scope = new ProfilingScope(s_InsertionSortSampler);
- InsertionSort<T>(data, 0, data.Length - 1, compare);
- }
-
- // A non-allocating predicated sub-array insertion sort for managed arrays.
- //
- // NOTE: called also from QuickSort for small ranges.
- static public void InsertionSort<T>(T[] data, int start, int end, Func<T, T, int> compare)
- {
- Assertions.Assert.IsTrue((uint)start < data.Length);
- Assertions.Assert.IsTrue((uint)end < data.Length);
-
- for (int i = start + 1; i < end + 1; i++)
- {
- var iData = data[i];
- int j = i - 1;
- while (j >= 0 && compare(iData, data[j]) < 0)
- {
- data[j + 1] = data[j];
- j--;
- }
- data[j + 1] = iData;
- }
- }
- }
- }
|