Path: blob/master/thirdparty/embree/kernels/builders/heuristic_spatial.h
9913 views
// Copyright 2009-2021 Intel Corporation1// SPDX-License-Identifier: Apache-2.023#pragma once45#include "priminfo.h"67namespace embree8{9static const unsigned int RESERVED_NUM_SPATIAL_SPLITS_GEOMID_BITS = 5;1011namespace isa12{1314/*! mapping into bins */15template<size_t BINS>16struct SpatialBinMapping17{18public:19__forceinline SpatialBinMapping() {}2021/*! calculates the mapping */22__forceinline SpatialBinMapping(const CentGeomBBox3fa& pinfo)23{24const vfloat4 lower = (vfloat4) pinfo.geomBounds.lower;25const vfloat4 upper = (vfloat4) pinfo.geomBounds.upper;26const vfloat4 eps = 128.0f*vfloat4(ulp)*max(abs(lower),abs(upper));27const vfloat4 diag = max(eps,(vfloat4) pinfo.geomBounds.size());28scale = select(upper-lower <= eps,vfloat4(0.0f),vfloat4(BINS)/diag);29ofs = (vfloat4) pinfo.geomBounds.lower;30inv_scale = 1.0f / scale;31}3233/*! slower but safe binning */34__forceinline vint4 bin(const Vec3fa& p) const35{36const vint4 i = floori((vfloat4(p)-ofs)*scale);37return clamp(i,vint4(0),vint4(BINS-1));38}3940__forceinline std::pair<vint4,vint4> bin(const BBox3fa& b) const41{42#if defined(__AVX__)43const vfloat8 ofs8(ofs);44const vfloat8 scale8(scale);45const vint8 lu = floori((vfloat8::loadu(&b)-ofs8)*scale8);46const vint8 c_lu = clamp(lu,vint8(zero),vint8(BINS-1));47return std::pair<vint4,vint4>(extract4<0>(c_lu),extract4<1>(c_lu));48#else49const vint4 lower = floori((vfloat4(b.lower)-ofs)*scale);50const vint4 upper = floori((vfloat4(b.upper)-ofs)*scale);51const vint4 c_lower = clamp(lower,vint4(0),vint4(BINS-1));52const vint4 c_upper = clamp(upper,vint4(0),vint4(BINS-1));53return std::pair<vint4,vint4>(c_lower,c_upper);54#endif55}565758/*! calculates left spatial position of bin */59__forceinline float pos(const size_t bin, const size_t dim) const {60return madd(float(bin),inv_scale[dim],ofs[dim]);61}6263/*! calculates left spatial position of bin */64template<size_t N>65__forceinline vfloat<N> posN(const vfloat<N> bin, const size_t dim) const {66return madd(bin,vfloat<N>(inv_scale[dim]),vfloat<N>(ofs[dim]));67}6869/*! returns true if the mapping is invalid in some dimension */70__forceinline bool invalid(const size_t dim) const {71return scale[dim] == 0.0f;72}7374public:75vfloat4 ofs,scale,inv_scale; //!< linear function that maps to bin ID76};7778/*! stores all information required to perform some split */79template<size_t BINS>80struct SpatialBinSplit81{82/*! construct an invalid split by default */83__forceinline SpatialBinSplit()84: sah(inf), dim(-1), pos(0), left(-1), right(-1), factor(1.0f) {}8586/*! constructs specified split */87__forceinline SpatialBinSplit(float sah, int dim, int pos, const SpatialBinMapping<BINS>& mapping)88: sah(sah), dim(dim), pos(pos), left(-1), right(-1), factor(1.0f), mapping(mapping) {}8990/*! constructs specified split */91__forceinline SpatialBinSplit(float sah, int dim, int pos, int left, int right, float factor, const SpatialBinMapping<BINS>& mapping)92: sah(sah), dim(dim), pos(pos), left(left), right(right), factor(factor), mapping(mapping) {}9394/*! tests if this split is valid */95__forceinline bool valid() const { return dim != -1; }9697/*! calculates surface area heuristic for performing the split */98__forceinline float splitSAH() const { return sah; }99100/*! stream output */101friend embree_ostream operator<<(embree_ostream cout, const SpatialBinSplit& split) {102return cout << "SpatialBinSplit { sah = " << split.sah << ", dim = " << split.dim << ", pos = " << split.pos << ", left = " << split.left << ", right = " << split.right << ", factor = " << split.factor << "}";103}104105public:106float sah; //!< SAH cost of the split107int dim; //!< split dimension108int pos; //!< split position109int left; //!< number of elements on the left side110int right; //!< number of elements on the right side111float factor; //!< factor splitting the extended range112SpatialBinMapping<BINS> mapping; //!< mapping into bins113};114115/*! stores all binning information */116template<size_t BINS, typename PrimRef>117struct __aligned(64) SpatialBinInfo118{119SpatialBinInfo() {120}121122__forceinline SpatialBinInfo(EmptyTy) {123clear();124}125126/*! clears the bin info */127__forceinline void clear()128{129for (size_t i=0; i<BINS; i++) {130bounds[i][0] = bounds[i][1] = bounds[i][2] = empty;131numBegin[i] = numEnd[i] = 0;132}133}134135/*! adds binning data */136__forceinline void add(const size_t dim,137const size_t beginID,138const size_t endID,139const size_t binID,140const BBox3fa &b,141const size_t n = 1)142{143assert(beginID < BINS);144assert(endID < BINS);145assert(binID < BINS);146147numBegin[beginID][dim]+=(unsigned int)n;148numEnd [endID][dim]+=(unsigned int)n;149bounds [binID][dim].extend(b);150}151152/*! extends binning bounds */153__forceinline void extend(const size_t dim,154const size_t binID,155const BBox3fa &b)156{157assert(binID < BINS);158bounds [binID][dim].extend(b);159}160161/*! bins an array of primitives */162template<typename PrimitiveSplitterFactory>163__forceinline void bin2(const PrimitiveSplitterFactory& splitterFactory, const PrimRef* source, size_t begin, size_t end, const SpatialBinMapping<BINS>& mapping)164{165for (size_t i=begin; i<end; i++)166{167const PrimRef& prim = source[i];168unsigned splits = prim.geomID() >> (32-RESERVED_NUM_SPATIAL_SPLITS_GEOMID_BITS);169170if (unlikely(splits <= 1))171{172const vint4 bin = mapping.bin(center(prim.bounds()));173for (size_t dim=0; dim<3; dim++)174{175assert(bin[dim] >= (int)0 && bin[dim] < (int)BINS);176add(dim,bin[dim],bin[dim],bin[dim],prim.bounds());177}178}179else180{181const vint4 bin0 = mapping.bin(prim.bounds().lower);182const vint4 bin1 = mapping.bin(prim.bounds().upper);183184for (size_t dim=0; dim<3; dim++)185{186if (unlikely(mapping.invalid(dim)))187continue;188189size_t bin;190size_t l = bin0[dim];191size_t r = bin1[dim];192193// same bin optimization194if (likely(l == r))195{196add(dim,l,l,l,prim.bounds());197continue;198}199size_t bin_start = bin0[dim];200size_t bin_end = bin1[dim];201BBox3fa rest = prim.bounds();202203/* assure that split position always overlaps the primitive bounds */204while (bin_start < bin_end && mapping.pos(bin_start+1,dim) <= rest.lower[dim]) bin_start++;205while (bin_start < bin_end && mapping.pos(bin_end ,dim) >= rest.upper[dim]) bin_end--;206207const auto splitter = splitterFactory(prim);208for (bin=bin_start; bin<bin_end; bin++)209{210const float pos = mapping.pos(bin+1,dim);211BBox3fa left,right;212splitter(rest,dim,pos,left,right);213214if (unlikely(left.empty())) l++;215extend(dim,bin,left);216rest = right;217}218if (unlikely(rest.empty())) r--;219add(dim,l,r,bin,rest);220}221}222}223}224225226/*! bins an array of primitives */227__forceinline void binSubTreeRefs(const PrimRef* source, size_t begin, size_t end, const SpatialBinMapping<BINS>& mapping)228{229for (size_t i=begin; i<end; i++)230{231const PrimRef &prim = source[i];232const vint4 bin0 = mapping.bin(prim.bounds().lower);233const vint4 bin1 = mapping.bin(prim.bounds().upper);234235for (size_t dim=0; dim<3; dim++)236{237if (unlikely(mapping.invalid(dim)))238continue;239240const size_t l = bin0[dim];241const size_t r = bin1[dim];242243const unsigned int n = prim.primID();244245// same bin optimization246if (likely(l == r))247{248add(dim,l,l,l,prim.bounds(),n);249continue;250}251const size_t bin_start = bin0[dim];252const size_t bin_end = bin1[dim];253for (size_t bin=bin_start; bin<bin_end; bin++)254add(dim,l,r,bin,prim.bounds(),n);255}256}257}258259/*! merges in other binning information */260void merge (const SpatialBinInfo& other)261{262for (size_t i=0; i<BINS; i++)263{264numBegin[i] += other.numBegin[i];265numEnd [i] += other.numEnd [i];266bounds[i][0].extend(other.bounds[i][0]);267bounds[i][1].extend(other.bounds[i][1]);268bounds[i][2].extend(other.bounds[i][2]);269}270}271272/*! merges in other binning information */273static __forceinline const SpatialBinInfo reduce (const SpatialBinInfo& a, const SpatialBinInfo& b)274{275SpatialBinInfo c(empty);276for (size_t i=0; i<BINS; i++)277{278c.numBegin[i] += a.numBegin[i]+b.numBegin[i];279c.numEnd [i] += a.numEnd [i]+b.numEnd [i];280c.bounds[i][0] = embree::merge(a.bounds[i][0],b.bounds[i][0]);281c.bounds[i][1] = embree::merge(a.bounds[i][1],b.bounds[i][1]);282c.bounds[i][2] = embree::merge(a.bounds[i][2],b.bounds[i][2]);283}284return c;285}286287/*! finds the best split by scanning binning information */288SpatialBinSplit<BINS> best(const SpatialBinMapping<BINS>& mapping, const size_t blocks_shift) const289{290/* sweep from right to left and compute parallel prefix of merged bounds */291vfloat4 rAreas[BINS];292vuint4 rCounts[BINS];293vuint4 count = 0; BBox3fa bx = empty; BBox3fa by = empty; BBox3fa bz = empty;294for (size_t i=BINS-1; i>0; i--)295{296count += numEnd[i];297rCounts[i] = count;298bx.extend(bounds[i][0]); rAreas[i][0] = halfArea(bx);299by.extend(bounds[i][1]); rAreas[i][1] = halfArea(by);300bz.extend(bounds[i][2]); rAreas[i][2] = halfArea(bz);301rAreas[i][3] = 0.0f;302}303304/* sweep from left to right and compute SAH */305vuint4 blocks_add = (1 << blocks_shift)-1;306vuint4 ii = 1; vfloat4 vbestSAH = pos_inf; vuint4 vbestPos = 0; vuint4 vbestlCount = 0; vuint4 vbestrCount = 0;307count = 0; bx = empty; by = empty; bz = empty;308for (size_t i=1; i<BINS; i++, ii+=1)309{310count += numBegin[i-1];311bx.extend(bounds[i-1][0]); float Ax = halfArea(bx);312by.extend(bounds[i-1][1]); float Ay = halfArea(by);313bz.extend(bounds[i-1][2]); float Az = halfArea(bz);314const vfloat4 lArea = vfloat4(Ax,Ay,Az,Az);315const vfloat4 rArea = rAreas[i];316const vuint4 lCount = (count +blocks_add) >> (unsigned int)(blocks_shift);317const vuint4 rCount = (rCounts[i]+blocks_add) >> (unsigned int)(blocks_shift);318const vfloat4 sah = madd(lArea,vfloat4(lCount),rArea*vfloat4(rCount));319// const vfloat4 sah = madd(lArea,vfloat4(vint4(lCount)),rArea*vfloat4(vint4(rCount)));320const vbool4 mask = sah < vbestSAH;321vbestPos = select(mask,ii ,vbestPos);322vbestSAH = select(mask,sah,vbestSAH);323vbestlCount = select(mask,count,vbestlCount);324vbestrCount = select(mask,rCounts[i],vbestrCount);325}326327/* find best dimension */328float bestSAH = inf;329int bestDim = -1;330int bestPos = 0;331unsigned int bestlCount = 0;332unsigned int bestrCount = 0;333for (int dim=0; dim<3; dim++)334{335/* ignore zero sized dimensions */336if (unlikely(mapping.invalid(dim)))337continue;338339/* test if this is a better dimension */340if (vbestSAH[dim] < bestSAH && vbestPos[dim] != 0) {341bestDim = dim;342bestPos = vbestPos[dim];343bestSAH = vbestSAH[dim];344bestlCount = vbestlCount[dim];345bestrCount = vbestrCount[dim];346}347}348assert(bestSAH >= 0.0f);349350/* return invalid split if no split found */351if (bestDim == -1)352return SpatialBinSplit<BINS>(inf,-1,0,mapping);353354/* return best found split */355return SpatialBinSplit<BINS>(bestSAH,bestDim,bestPos,bestlCount,bestrCount,1.0f,mapping);356}357358private:359BBox3fa bounds[BINS][3]; //!< geometry bounds for each bin in each dimension360vuint4 numBegin[BINS]; //!< number of primitives starting in bin361vuint4 numEnd[BINS]; //!< number of primitives ending in bin362};363}364}365366367368