Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/embree/kernels/builders/heuristic_spatial.h
9913 views
1
// Copyright 2009-2021 Intel Corporation
2
// SPDX-License-Identifier: Apache-2.0
3
4
#pragma once
5
6
#include "priminfo.h"
7
8
namespace embree
9
{
10
static const unsigned int RESERVED_NUM_SPATIAL_SPLITS_GEOMID_BITS = 5;
11
12
namespace isa
13
{
14
15
/*! mapping into bins */
16
template<size_t BINS>
17
struct SpatialBinMapping
18
{
19
public:
20
__forceinline SpatialBinMapping() {}
21
22
/*! calculates the mapping */
23
__forceinline SpatialBinMapping(const CentGeomBBox3fa& pinfo)
24
{
25
const vfloat4 lower = (vfloat4) pinfo.geomBounds.lower;
26
const vfloat4 upper = (vfloat4) pinfo.geomBounds.upper;
27
const vfloat4 eps = 128.0f*vfloat4(ulp)*max(abs(lower),abs(upper));
28
const vfloat4 diag = max(eps,(vfloat4) pinfo.geomBounds.size());
29
scale = select(upper-lower <= eps,vfloat4(0.0f),vfloat4(BINS)/diag);
30
ofs = (vfloat4) pinfo.geomBounds.lower;
31
inv_scale = 1.0f / scale;
32
}
33
34
/*! slower but safe binning */
35
__forceinline vint4 bin(const Vec3fa& p) const
36
{
37
const vint4 i = floori((vfloat4(p)-ofs)*scale);
38
return clamp(i,vint4(0),vint4(BINS-1));
39
}
40
41
__forceinline std::pair<vint4,vint4> bin(const BBox3fa& b) const
42
{
43
#if defined(__AVX__)
44
const vfloat8 ofs8(ofs);
45
const vfloat8 scale8(scale);
46
const vint8 lu = floori((vfloat8::loadu(&b)-ofs8)*scale8);
47
const vint8 c_lu = clamp(lu,vint8(zero),vint8(BINS-1));
48
return std::pair<vint4,vint4>(extract4<0>(c_lu),extract4<1>(c_lu));
49
#else
50
const vint4 lower = floori((vfloat4(b.lower)-ofs)*scale);
51
const vint4 upper = floori((vfloat4(b.upper)-ofs)*scale);
52
const vint4 c_lower = clamp(lower,vint4(0),vint4(BINS-1));
53
const vint4 c_upper = clamp(upper,vint4(0),vint4(BINS-1));
54
return std::pair<vint4,vint4>(c_lower,c_upper);
55
#endif
56
}
57
58
59
/*! calculates left spatial position of bin */
60
__forceinline float pos(const size_t bin, const size_t dim) const {
61
return madd(float(bin),inv_scale[dim],ofs[dim]);
62
}
63
64
/*! calculates left spatial position of bin */
65
template<size_t N>
66
__forceinline vfloat<N> posN(const vfloat<N> bin, const size_t dim) const {
67
return madd(bin,vfloat<N>(inv_scale[dim]),vfloat<N>(ofs[dim]));
68
}
69
70
/*! returns true if the mapping is invalid in some dimension */
71
__forceinline bool invalid(const size_t dim) const {
72
return scale[dim] == 0.0f;
73
}
74
75
public:
76
vfloat4 ofs,scale,inv_scale; //!< linear function that maps to bin ID
77
};
78
79
/*! stores all information required to perform some split */
80
template<size_t BINS>
81
struct SpatialBinSplit
82
{
83
/*! construct an invalid split by default */
84
__forceinline SpatialBinSplit()
85
: sah(inf), dim(-1), pos(0), left(-1), right(-1), factor(1.0f) {}
86
87
/*! constructs specified split */
88
__forceinline SpatialBinSplit(float sah, int dim, int pos, const SpatialBinMapping<BINS>& mapping)
89
: sah(sah), dim(dim), pos(pos), left(-1), right(-1), factor(1.0f), mapping(mapping) {}
90
91
/*! constructs specified split */
92
__forceinline SpatialBinSplit(float sah, int dim, int pos, int left, int right, float factor, const SpatialBinMapping<BINS>& mapping)
93
: sah(sah), dim(dim), pos(pos), left(left), right(right), factor(factor), mapping(mapping) {}
94
95
/*! tests if this split is valid */
96
__forceinline bool valid() const { return dim != -1; }
97
98
/*! calculates surface area heuristic for performing the split */
99
__forceinline float splitSAH() const { return sah; }
100
101
/*! stream output */
102
friend embree_ostream operator<<(embree_ostream cout, const SpatialBinSplit& split) {
103
return cout << "SpatialBinSplit { sah = " << split.sah << ", dim = " << split.dim << ", pos = " << split.pos << ", left = " << split.left << ", right = " << split.right << ", factor = " << split.factor << "}";
104
}
105
106
public:
107
float sah; //!< SAH cost of the split
108
int dim; //!< split dimension
109
int pos; //!< split position
110
int left; //!< number of elements on the left side
111
int right; //!< number of elements on the right side
112
float factor; //!< factor splitting the extended range
113
SpatialBinMapping<BINS> mapping; //!< mapping into bins
114
};
115
116
/*! stores all binning information */
117
template<size_t BINS, typename PrimRef>
118
struct __aligned(64) SpatialBinInfo
119
{
120
SpatialBinInfo() {
121
}
122
123
__forceinline SpatialBinInfo(EmptyTy) {
124
clear();
125
}
126
127
/*! clears the bin info */
128
__forceinline void clear()
129
{
130
for (size_t i=0; i<BINS; i++) {
131
bounds[i][0] = bounds[i][1] = bounds[i][2] = empty;
132
numBegin[i] = numEnd[i] = 0;
133
}
134
}
135
136
/*! adds binning data */
137
__forceinline void add(const size_t dim,
138
const size_t beginID,
139
const size_t endID,
140
const size_t binID,
141
const BBox3fa &b,
142
const size_t n = 1)
143
{
144
assert(beginID < BINS);
145
assert(endID < BINS);
146
assert(binID < BINS);
147
148
numBegin[beginID][dim]+=(unsigned int)n;
149
numEnd [endID][dim]+=(unsigned int)n;
150
bounds [binID][dim].extend(b);
151
}
152
153
/*! extends binning bounds */
154
__forceinline void extend(const size_t dim,
155
const size_t binID,
156
const BBox3fa &b)
157
{
158
assert(binID < BINS);
159
bounds [binID][dim].extend(b);
160
}
161
162
/*! bins an array of primitives */
163
template<typename PrimitiveSplitterFactory>
164
__forceinline void bin2(const PrimitiveSplitterFactory& splitterFactory, const PrimRef* source, size_t begin, size_t end, const SpatialBinMapping<BINS>& mapping)
165
{
166
for (size_t i=begin; i<end; i++)
167
{
168
const PrimRef& prim = source[i];
169
unsigned splits = prim.geomID() >> (32-RESERVED_NUM_SPATIAL_SPLITS_GEOMID_BITS);
170
171
if (unlikely(splits <= 1))
172
{
173
const vint4 bin = mapping.bin(center(prim.bounds()));
174
for (size_t dim=0; dim<3; dim++)
175
{
176
assert(bin[dim] >= (int)0 && bin[dim] < (int)BINS);
177
add(dim,bin[dim],bin[dim],bin[dim],prim.bounds());
178
}
179
}
180
else
181
{
182
const vint4 bin0 = mapping.bin(prim.bounds().lower);
183
const vint4 bin1 = mapping.bin(prim.bounds().upper);
184
185
for (size_t dim=0; dim<3; dim++)
186
{
187
if (unlikely(mapping.invalid(dim)))
188
continue;
189
190
size_t bin;
191
size_t l = bin0[dim];
192
size_t r = bin1[dim];
193
194
// same bin optimization
195
if (likely(l == r))
196
{
197
add(dim,l,l,l,prim.bounds());
198
continue;
199
}
200
size_t bin_start = bin0[dim];
201
size_t bin_end = bin1[dim];
202
BBox3fa rest = prim.bounds();
203
204
/* assure that split position always overlaps the primitive bounds */
205
while (bin_start < bin_end && mapping.pos(bin_start+1,dim) <= rest.lower[dim]) bin_start++;
206
while (bin_start < bin_end && mapping.pos(bin_end ,dim) >= rest.upper[dim]) bin_end--;
207
208
const auto splitter = splitterFactory(prim);
209
for (bin=bin_start; bin<bin_end; bin++)
210
{
211
const float pos = mapping.pos(bin+1,dim);
212
BBox3fa left,right;
213
splitter(rest,dim,pos,left,right);
214
215
if (unlikely(left.empty())) l++;
216
extend(dim,bin,left);
217
rest = right;
218
}
219
if (unlikely(rest.empty())) r--;
220
add(dim,l,r,bin,rest);
221
}
222
}
223
}
224
}
225
226
227
/*! bins an array of primitives */
228
__forceinline void binSubTreeRefs(const PrimRef* source, size_t begin, size_t end, const SpatialBinMapping<BINS>& mapping)
229
{
230
for (size_t i=begin; i<end; i++)
231
{
232
const PrimRef &prim = source[i];
233
const vint4 bin0 = mapping.bin(prim.bounds().lower);
234
const vint4 bin1 = mapping.bin(prim.bounds().upper);
235
236
for (size_t dim=0; dim<3; dim++)
237
{
238
if (unlikely(mapping.invalid(dim)))
239
continue;
240
241
const size_t l = bin0[dim];
242
const size_t r = bin1[dim];
243
244
const unsigned int n = prim.primID();
245
246
// same bin optimization
247
if (likely(l == r))
248
{
249
add(dim,l,l,l,prim.bounds(),n);
250
continue;
251
}
252
const size_t bin_start = bin0[dim];
253
const size_t bin_end = bin1[dim];
254
for (size_t bin=bin_start; bin<bin_end; bin++)
255
add(dim,l,r,bin,prim.bounds(),n);
256
}
257
}
258
}
259
260
/*! merges in other binning information */
261
void merge (const SpatialBinInfo& other)
262
{
263
for (size_t i=0; i<BINS; i++)
264
{
265
numBegin[i] += other.numBegin[i];
266
numEnd [i] += other.numEnd [i];
267
bounds[i][0].extend(other.bounds[i][0]);
268
bounds[i][1].extend(other.bounds[i][1]);
269
bounds[i][2].extend(other.bounds[i][2]);
270
}
271
}
272
273
/*! merges in other binning information */
274
static __forceinline const SpatialBinInfo reduce (const SpatialBinInfo& a, const SpatialBinInfo& b)
275
{
276
SpatialBinInfo c(empty);
277
for (size_t i=0; i<BINS; i++)
278
{
279
c.numBegin[i] += a.numBegin[i]+b.numBegin[i];
280
c.numEnd [i] += a.numEnd [i]+b.numEnd [i];
281
c.bounds[i][0] = embree::merge(a.bounds[i][0],b.bounds[i][0]);
282
c.bounds[i][1] = embree::merge(a.bounds[i][1],b.bounds[i][1]);
283
c.bounds[i][2] = embree::merge(a.bounds[i][2],b.bounds[i][2]);
284
}
285
return c;
286
}
287
288
/*! finds the best split by scanning binning information */
289
SpatialBinSplit<BINS> best(const SpatialBinMapping<BINS>& mapping, const size_t blocks_shift) const
290
{
291
/* sweep from right to left and compute parallel prefix of merged bounds */
292
vfloat4 rAreas[BINS];
293
vuint4 rCounts[BINS];
294
vuint4 count = 0; BBox3fa bx = empty; BBox3fa by = empty; BBox3fa bz = empty;
295
for (size_t i=BINS-1; i>0; i--)
296
{
297
count += numEnd[i];
298
rCounts[i] = count;
299
bx.extend(bounds[i][0]); rAreas[i][0] = halfArea(bx);
300
by.extend(bounds[i][1]); rAreas[i][1] = halfArea(by);
301
bz.extend(bounds[i][2]); rAreas[i][2] = halfArea(bz);
302
rAreas[i][3] = 0.0f;
303
}
304
305
/* sweep from left to right and compute SAH */
306
vuint4 blocks_add = (1 << blocks_shift)-1;
307
vuint4 ii = 1; vfloat4 vbestSAH = pos_inf; vuint4 vbestPos = 0; vuint4 vbestlCount = 0; vuint4 vbestrCount = 0;
308
count = 0; bx = empty; by = empty; bz = empty;
309
for (size_t i=1; i<BINS; i++, ii+=1)
310
{
311
count += numBegin[i-1];
312
bx.extend(bounds[i-1][0]); float Ax = halfArea(bx);
313
by.extend(bounds[i-1][1]); float Ay = halfArea(by);
314
bz.extend(bounds[i-1][2]); float Az = halfArea(bz);
315
const vfloat4 lArea = vfloat4(Ax,Ay,Az,Az);
316
const vfloat4 rArea = rAreas[i];
317
const vuint4 lCount = (count +blocks_add) >> (unsigned int)(blocks_shift);
318
const vuint4 rCount = (rCounts[i]+blocks_add) >> (unsigned int)(blocks_shift);
319
const vfloat4 sah = madd(lArea,vfloat4(lCount),rArea*vfloat4(rCount));
320
// const vfloat4 sah = madd(lArea,vfloat4(vint4(lCount)),rArea*vfloat4(vint4(rCount)));
321
const vbool4 mask = sah < vbestSAH;
322
vbestPos = select(mask,ii ,vbestPos);
323
vbestSAH = select(mask,sah,vbestSAH);
324
vbestlCount = select(mask,count,vbestlCount);
325
vbestrCount = select(mask,rCounts[i],vbestrCount);
326
}
327
328
/* find best dimension */
329
float bestSAH = inf;
330
int bestDim = -1;
331
int bestPos = 0;
332
unsigned int bestlCount = 0;
333
unsigned int bestrCount = 0;
334
for (int dim=0; dim<3; dim++)
335
{
336
/* ignore zero sized dimensions */
337
if (unlikely(mapping.invalid(dim)))
338
continue;
339
340
/* test if this is a better dimension */
341
if (vbestSAH[dim] < bestSAH && vbestPos[dim] != 0) {
342
bestDim = dim;
343
bestPos = vbestPos[dim];
344
bestSAH = vbestSAH[dim];
345
bestlCount = vbestlCount[dim];
346
bestrCount = vbestrCount[dim];
347
}
348
}
349
assert(bestSAH >= 0.0f);
350
351
/* return invalid split if no split found */
352
if (bestDim == -1)
353
return SpatialBinSplit<BINS>(inf,-1,0,mapping);
354
355
/* return best found split */
356
return SpatialBinSplit<BINS>(bestSAH,bestDim,bestPos,bestlCount,bestrCount,1.0f,mapping);
357
}
358
359
private:
360
BBox3fa bounds[BINS][3]; //!< geometry bounds for each bin in each dimension
361
vuint4 numBegin[BINS]; //!< number of primitives starting in bin
362
vuint4 numEnd[BINS]; //!< number of primitives ending in bin
363
};
364
}
365
}
366
367
368