heuristic_spatial.h 15 KB

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