| 77 | } |
| 78 | |
| 79 | void BitonicSort::Sort(GPUContext* context, GPUBuffer* indicesBuffer, GPUBuffer* keysBuffer, GPUBuffer* countBuffer, uint32 counterOffset, bool sortAscending, int32 maxElements) |
| 80 | { |
| 81 | ASSERT(context && indicesBuffer && keysBuffer && countBuffer); |
| 82 | if (checkIfSkipPass()) |
| 83 | return; |
| 84 | PROFILE_GPU_CPU("Bitonic Sort"); |
| 85 | int32 maxNumElements = (int32)indicesBuffer->GetElementsCount(); |
| 86 | if (maxElements > 0 && maxElements < maxNumElements) |
| 87 | maxNumElements = maxElements; |
| 88 | const uint32 alignedMaxNumElements = Math::RoundUpToPowerOf2(maxNumElements); |
| 89 | const uint32 maxIterations = (uint32)Math::Log2((float)Math::Max(2048u, alignedMaxNumElements)) - 10; |
| 90 | |
| 91 | // Setup constants buffer |
| 92 | Data data; |
| 93 | data.CounterOffset = counterOffset; |
| 94 | data.NullItemKey = sortAscending ? MAX_float : -MAX_float; |
| 95 | data.NullItemIndex = 0; |
| 96 | data.KeySign = sortAscending ? -1.0f : 1.0f; |
| 97 | data.MaxIterations = maxIterations; |
| 98 | data.LoopK = 0; |
| 99 | data.LoopJ = 0; |
| 100 | context->UpdateCB(_cb, &data); |
| 101 | context->BindCB(0, _cb); |
| 102 | context->BindSR(0, countBuffer->View()); |
| 103 | |
| 104 | // If item count is small we can do only presorting within a single dispatch thread group |
| 105 | if (maxNumElements <= 2048) |
| 106 | { |
| 107 | // Use pre-sort with smaller thread group size (eg. for small particle emitters sorting) |
| 108 | const int32 permutation = maxNumElements < 128 ? 1 : 0; |
| 109 | context->BindUA(0, indicesBuffer->View()); |
| 110 | context->BindUA(1, keysBuffer->View()); |
| 111 | context->Dispatch(_preSortCS.Get(permutation), 1, 1, 1); |
| 112 | } |
| 113 | else |
| 114 | { |
| 115 | // Generate execute indirect arguments |
| 116 | context->BindUA(0, _dispatchArgsBuffer->View()); |
| 117 | context->Dispatch(_indirectArgsCS, 1, 1, 1); |
| 118 | |
| 119 | // Pre-Sort the buffer up to k = 2048 (this also pads the list with invalid indices that will drift to the end of the sorted list) |
| 120 | context->BindUA(0, indicesBuffer->View()); |
| 121 | context->BindUA(1, keysBuffer->View()); |
| 122 | context->DispatchIndirect(_preSortCS.Get(0), _dispatchArgsBuffer, 0); |
| 123 | |
| 124 | // We have already pre-sorted up through k = 2048 when first writing our list, so we continue sorting with k = 4096 |
| 125 | // For really large values of k, these indirect dispatches will be skipped over with thread counts of 0 |
| 126 | uint32 indirectArgsOffset = sizeof(GPUDispatchIndirectArgs); |
| 127 | for (uint32 k = 4096; k <= alignedMaxNumElements; k *= 2) |
| 128 | { |
| 129 | for (uint32 j = k / 2; j >= 2048; j /= 2) |
| 130 | { |
| 131 | data.LoopK = k; |
| 132 | data.LoopJ = j; |
| 133 | context->UpdateCB(_cb, &data); |
| 134 | |
| 135 | context->DispatchIndirect(_outerSortCS, _dispatchArgsBuffer, indirectArgsOffset); |
| 136 | indirectArgsOffset += sizeof(GPUDispatchIndirectArgs); |
nothing calls this directly
no test coverage detected