Blender V5.0
sort.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
2 * SPDX-FileCopyrightText: 2011-2022 Blender Foundation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Adapted code from NVIDIA Corporation. */
7
8#include "bvh/sort.h"
9
10#include "bvh/params.h"
11#include "bvh/unaligned.h"
12
13#include "util/algorithm.h"
14#include "util/task.h"
15
17
18static const int BVH_SORT_THRESHOLD = 4096;
19
21 public:
22 int dim;
25
32
34 {
35 return (aligned_space != nullptr) ?
36 unaligned_heuristic->compute_aligned_prim_boundbox(prim, *aligned_space) :
37 prim.bounds();
38 }
39
40 /* Compare two references.
41 *
42 * Returns value is similar to return value of `strcmp()`.
43 */
44 __forceinline int compare(const BVHReference &ra, const BVHReference &rb) const
45 {
46 BoundBox ra_bounds = get_prim_bounds(ra);
47 BoundBox rb_bounds = get_prim_bounds(rb);
48 const float ca = ra_bounds.min[dim] + ra_bounds.max[dim];
49 const float cb = rb_bounds.min[dim] + rb_bounds.max[dim];
50
51 if (ca < cb) {
52 return -1;
53 }
54 if (ca > cb) {
55 return 1;
56 }
57 if (ra.prim_object() < rb.prim_object()) {
58 return -1;
59 }
60 if (ra.prim_object() > rb.prim_object()) {
61 return 1;
62 }
63 if (ra.prim_index() < rb.prim_index()) {
64 return -1;
65 }
66 if (ra.prim_index() > rb.prim_index()) {
67 return 1;
68 }
69 if (ra.prim_type() < rb.prim_type()) {
70 return -1;
71 }
72 if (ra.prim_type() > rb.prim_type()) {
73 return 1;
74 }
75
76 return 0;
77 }
78
79 bool operator()(const BVHReference &ra, const BVHReference &rb)
80 {
81 return (compare(ra, rb) < 0);
82 }
83};
84
87 const int job_start,
88 const int job_end,
89 const BVHReferenceCompare &compare);
90
91/* Multi-threaded reference sort. */
94 const int job_start,
95 const int job_end,
96 const BVHReferenceCompare &compare)
97{
98 int start = job_start;
99 int end = job_end;
100 bool have_work = (start < end);
101 while (have_work) {
102 const int count = job_end - job_start;
104 /* Number of reference low enough, faster to finish the job
105 * in one thread rather than to spawn more threads.
106 */
107 sort(data + job_start, data + job_end + 1, compare);
108 break;
109 }
110 /* Single QSort step.
111 * Use median-of-three method for the pivot point.
112 */
113 int left = start;
114 int right = end;
115 const int center = (left + right) >> 1;
116 if (compare.compare(data[left], data[center]) > 0) {
117 swap(data[left], data[center]);
118 }
119 if (compare.compare(data[left], data[right]) > 0) {
120 swap(data[left], data[right]);
121 }
122 if (compare.compare(data[center], data[right]) > 0) {
123 swap(data[center], data[right]);
124 }
125 swap(data[center], data[right - 1]);
126 const BVHReference median = data[right - 1];
127 do {
128 while (compare.compare(data[left], median) < 0) {
129 ++left;
130 }
131 while (compare.compare(data[right], median) > 0) {
132 --right;
133 }
134 if (left <= right) {
135 swap(data[left], data[right]);
136 ++left;
137 --right;
138 }
139 } while (left <= right);
140 /* We only create one new task here to reduce downside effects of
141 * latency in TaskScheduler.
142 * So generally current thread keeps working on the left part of the
143 * array, and we create new task for the right side.
144 * However, if there's nothing to be done in the left side of the array
145 * we don't create any tasks and make it so current thread works on the
146 * right side.
147 */
148 have_work = false;
149 if (left < end) {
150 if (start < right) {
151 task_pool->push([task_pool, data, left, end, compare] {
153 });
154 }
155 else {
156 start = left;
157 have_work = true;
158 }
159 }
160 if (start < right) {
161 end = right;
162 have_work = true;
163 }
164 }
165}
166
167void bvh_reference_sort(const int start,
168 const int end,
170 const int dim,
171 const BVHUnaligned *unaligned_heuristic,
172 const Transform *aligned_space)
173{
174 const int count = end - start;
175 const BVHReferenceCompare compare(dim, unaligned_heuristic, aligned_space);
177 /* It is important to not use any mutex if array is small enough,
178 * otherwise we end up in situation when we're going to sleep far
179 * too often.
180 */
181 sort(data + start, data + end, compare);
182 }
183 else {
185 bvh_reference_sort_threaded(&task_pool, data, start, end - 1, compare);
186 task_pool.wait_work();
187 }
188}
189
BMesh const char void * data
static DBVT_INLINE btDbvtNode * sort(btDbvtNode *n, btDbvtNode *&r)
Definition btDbvt.cpp:418
__forceinline int prim_type() const
Definition params.h:216
__forceinline int prim_object() const
Definition params.h:212
__forceinline const BoundBox & bounds() const
Definition params.h:204
__forceinline int prim_index() const
Definition params.h:208
#define __forceinline
#define CCL_NAMESPACE_END
TaskPool * task_pool
int count
static int left
#define swap(a, b)
Definition sort.cc:59
void bvh_reference_sort(const int start, const int end, BVHReference *data, const int dim, const BVHUnaligned *unaligned_heuristic, const Transform *aligned_space)
Definition sort.cpp:167
static CCL_NAMESPACE_BEGIN const int BVH_SORT_THRESHOLD
Definition sort.cpp:18
static void bvh_reference_sort_threaded(TaskPool *task_pool, BVHReference *data, const int job_start, const int job_end, const BVHReferenceCompare &compare)
Definition sort.cpp:92
const Transform * aligned_space
Definition sort.cpp:24
const BVHUnaligned * unaligned_heuristic
Definition sort.cpp:23
BVHReferenceCompare(const int dim, const BVHUnaligned *unaligned_heuristic, const Transform *aligned_space)
Definition sort.cpp:26
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
Definition sort.cpp:33
__forceinline int compare(const BVHReference &ra, const BVHReference &rb) const
Definition sort.cpp:44
bool operator()(const BVHReference &ra, const BVHReference &rb)
Definition sort.cpp:79
float3 max
Definition boundbox.h:20
float3 min
Definition boundbox.h:20