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