heuristic_binning.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. // Copyright 2009-2021 Intel Corporation
  2. // SPDX-License-Identifier: Apache-2.0
  3. #pragma once
  4. #include "priminfo.h"
  5. #include "../../common/algorithms/parallel_reduce.h"
  6. #include "../../common/algorithms/parallel_partition.h"
  7. namespace embree
  8. {
  9. namespace isa
  10. {
  11. /*! mapping into bins */
  12. template<size_t BINS>
  13. struct BinMapping
  14. {
  15. public:
  16. __forceinline BinMapping() {}
  17. /*! calculates the mapping */
  18. __forceinline BinMapping(size_t N, const BBox3fa& centBounds)
  19. {
  20. num = min(BINS,size_t(4.0f + 0.05f*N));
  21. assert(num >= 1);
  22. const vfloat4 eps = 1E-34f;
  23. const vfloat4 diag = max(eps, (vfloat4) centBounds.size());
  24. scale = select(diag > eps,vfloat4(0.99f*num)/diag,vfloat4(0.0f));
  25. ofs = (vfloat4) centBounds.lower;
  26. }
  27. /*! calculates the mapping */
  28. __forceinline BinMapping(const BBox3fa& centBounds)
  29. {
  30. num = BINS;
  31. const vfloat4 eps = 1E-34f;
  32. const vfloat4 diag = max(eps, (vfloat4) centBounds.size());
  33. scale = select(diag > eps,vfloat4(0.99f*num)/diag,vfloat4(0.0f));
  34. ofs = (vfloat4) centBounds.lower;
  35. }
  36. /*! calculates the mapping */
  37. template<typename PrimInfo>
  38. __forceinline BinMapping(const PrimInfo& pinfo)
  39. {
  40. const vfloat4 eps = 1E-34f;
  41. num = min(BINS,size_t(4.0f + 0.05f*pinfo.size()));
  42. const vfloat4 diag = max(eps,(vfloat4) pinfo.centBounds.size());
  43. scale = select(diag > eps,vfloat4(0.99f*num)/diag,vfloat4(0.0f));
  44. ofs = (vfloat4) pinfo.centBounds.lower;
  45. }
  46. /*! returns number of bins */
  47. __forceinline size_t size() const { return num; }
  48. /*! slower but safe binning */
  49. __forceinline Vec3ia bin(const Vec3fa& p) const
  50. {
  51. const vint4 i = floori((vfloat4(p)-ofs)*scale);
  52. #if 1
  53. assert(i[0] >= 0 && (size_t)i[0] < num);
  54. assert(i[1] >= 0 && (size_t)i[1] < num);
  55. assert(i[2] >= 0 && (size_t)i[2] < num);
  56. return Vec3ia(i);
  57. #else
  58. return Vec3ia(clamp(i,vint4(0),vint4(num-1)));
  59. #endif
  60. }
  61. /*! faster but unsafe binning */
  62. __forceinline Vec3ia bin_unsafe(const Vec3fa& p) const {
  63. return Vec3ia(floori((vfloat4(p)-ofs)*scale));
  64. }
  65. /*! faster but unsafe binning */
  66. template<typename PrimRef>
  67. __forceinline Vec3ia bin_unsafe(const PrimRef& p) const {
  68. return bin_unsafe(p.binCenter());
  69. }
  70. /*! faster but unsafe binning */
  71. template<typename PrimRef, typename BinBoundsAndCenter>
  72. __forceinline Vec3ia bin_unsafe(const PrimRef& p, const BinBoundsAndCenter& binBoundsAndCenter) const {
  73. return bin_unsafe(binBoundsAndCenter.binCenter(p));
  74. }
  75. template<typename PrimRef>
  76. __forceinline bool bin_unsafe(const PrimRef& ref,
  77. const vint4& vSplitPos,
  78. const vbool4& splitDimMask) const // FIXME: rename to isLeft
  79. {
  80. return any(((vint4)bin_unsafe(center2(ref.bounds())) < vSplitPos) & splitDimMask);
  81. }
  82. /*! calculates left spatial position of bin */
  83. __forceinline float pos(const size_t bin, const size_t dim) const {
  84. return madd(float(bin),1.0f / scale[dim],ofs[dim]);
  85. }
  86. /*! returns true if the mapping is invalid in some dimension */
  87. __forceinline bool invalid(const size_t dim) const {
  88. return scale[dim] == 0.0f;
  89. }
  90. /*! stream output */
  91. friend embree_ostream operator<<(embree_ostream cout, const BinMapping& mapping) {
  92. return cout << "BinMapping { num = " << mapping.num << ", ofs = " << mapping.ofs << ", scale = " << mapping.scale << "}";
  93. }
  94. public:
  95. size_t num;
  96. vfloat4 ofs,scale; //!< linear function that maps to bin ID
  97. };
  98. /*! stores all information to perform some split */
  99. template<size_t BINS>
  100. struct BinSplit
  101. {
  102. enum
  103. {
  104. SPLIT_OBJECT = 0,
  105. SPLIT_FALLBACK = 1,
  106. SPLIT_ENFORCE = 2, // splits with larger ID are enforced in createLargeLeaf even if we could create a leaf already
  107. SPLIT_TEMPORAL = 2,
  108. SPLIT_GEOMID = 3,
  109. };
  110. /*! construct an invalid split by default */
  111. __forceinline BinSplit()
  112. : sah(inf), dim(-1), pos(0), data(0) {}
  113. __forceinline BinSplit(float sah, unsigned data, int dim = 0, float fpos = 0)
  114. : sah(sah), dim(dim), fpos(fpos), data(data) {}
  115. /*! constructs specified split */
  116. __forceinline BinSplit(float sah, int dim, int pos, const BinMapping<BINS>& mapping)
  117. : sah(sah), dim(dim), pos(pos), data(0), mapping(mapping) {}
  118. /*! tests if this split is valid */
  119. __forceinline bool valid() const { return dim != -1; }
  120. /*! calculates surface area heuristic for performing the split */
  121. __forceinline float splitSAH() const { return sah; }
  122. /*! stream output */
  123. friend embree_ostream operator<<(embree_ostream cout, const BinSplit& split) {
  124. return cout << "BinSplit { sah = " << split.sah << ", dim = " << split.dim << ", pos = " << split.pos << "}";
  125. }
  126. public:
  127. float sah; //!< SAH cost of the split
  128. int dim; //!< split dimension
  129. union { int pos; float fpos; }; //!< bin index for splitting
  130. unsigned int data; //!< extra optional split data
  131. BinMapping<BINS> mapping; //!< mapping into bins
  132. };
  133. /*! stores extended information about the split */
  134. template<typename BBox>
  135. struct SplitInfoT
  136. {
  137. __forceinline SplitInfoT () {}
  138. __forceinline SplitInfoT (size_t leftCount, const BBox& leftBounds, size_t rightCount, const BBox& rightBounds)
  139. : leftCount(leftCount), rightCount(rightCount), leftBounds(leftBounds), rightBounds(rightBounds) {}
  140. public:
  141. size_t leftCount,rightCount;
  142. BBox leftBounds,rightBounds;
  143. };
  144. typedef SplitInfoT<BBox3fa> SplitInfo;
  145. typedef SplitInfoT<LBBox3fa> SplitInfo2;
  146. /*! stores all binning information */
  147. template<size_t BINS, typename PrimRef, typename BBox>
  148. struct __aligned(64) BinInfoT
  149. {
  150. typedef BinSplit<BINS> Split;
  151. typedef vbool4 vbool;
  152. typedef vint4 vint;
  153. typedef vfloat4 vfloat;
  154. __forceinline BinInfoT() {
  155. }
  156. __forceinline BinInfoT(EmptyTy) {
  157. clear();
  158. }
  159. /*! bin access function */
  160. __forceinline BBox &bounds(const size_t binID, const size_t dimID) { return _bounds[binID][dimID]; }
  161. __forceinline const BBox &bounds(const size_t binID, const size_t dimID) const { return _bounds[binID][dimID]; }
  162. __forceinline unsigned int &counts(const size_t binID, const size_t dimID) { return _counts[binID][dimID]; }
  163. __forceinline const unsigned int &counts(const size_t binID, const size_t dimID) const { return _counts[binID][dimID]; }
  164. __forceinline vuint4 &counts(const size_t binID) { return _counts[binID]; }
  165. __forceinline const vuint4 &counts(const size_t binID) const { return _counts[binID]; }
  166. /*! clears the bin info */
  167. __forceinline void clear()
  168. {
  169. for (size_t i=0; i<BINS; i++) {
  170. bounds(i,0) = bounds(i,1) = bounds(i,2) = empty;
  171. counts(i) = vuint4(zero);
  172. }
  173. }
  174. /*! bins an array of primitives */
  175. __forceinline void bin (const PrimRef* prims, size_t N, const BinMapping<BINS>& mapping)
  176. {
  177. if (unlikely(N == 0)) return;
  178. size_t i;
  179. for (i=0; i<N-1; i+=2)
  180. {
  181. /*! map even and odd primitive to bin */
  182. BBox prim0; Vec3fa center0;
  183. prims[i+0].binBoundsAndCenter(prim0,center0);
  184. const vint4 bin0 = (vint4)mapping.bin(center0);
  185. BBox prim1; Vec3fa center1;
  186. prims[i+1].binBoundsAndCenter(prim1,center1);
  187. const vint4 bin1 = (vint4)mapping.bin(center1);
  188. /*! increase bounds for bins for even primitive */
  189. const unsigned int b00 = extract<0>(bin0); bounds(b00,0).extend(prim0);
  190. const unsigned int b01 = extract<1>(bin0); bounds(b01,1).extend(prim0);
  191. const unsigned int b02 = extract<2>(bin0); bounds(b02,2).extend(prim0);
  192. const unsigned int s0 = (unsigned int)prims[i+0].size();
  193. counts(b00,0)+=s0;
  194. counts(b01,1)+=s0;
  195. counts(b02,2)+=s0;
  196. /*! increase bounds of bins for odd primitive */
  197. const unsigned int b10 = extract<0>(bin1); bounds(b10,0).extend(prim1);
  198. const unsigned int b11 = extract<1>(bin1); bounds(b11,1).extend(prim1);
  199. const unsigned int b12 = extract<2>(bin1); bounds(b12,2).extend(prim1);
  200. const unsigned int s1 = (unsigned int)prims[i+1].size();
  201. counts(b10,0)+=s1;
  202. counts(b11,1)+=s1;
  203. counts(b12,2)+=s1;
  204. }
  205. /*! for uneven number of primitives */
  206. if (i < N)
  207. {
  208. /*! map primitive to bin */
  209. BBox prim0; Vec3fa center0;
  210. prims[i].binBoundsAndCenter(prim0,center0);
  211. const vint4 bin0 = (vint4)mapping.bin(center0);
  212. /*! increase bounds of bins */
  213. const unsigned int s0 = (unsigned int)prims[i].size();
  214. const int b00 = extract<0>(bin0); counts(b00,0)+=s0; bounds(b00,0).extend(prim0);
  215. const int b01 = extract<1>(bin0); counts(b01,1)+=s0; bounds(b01,1).extend(prim0);
  216. const int b02 = extract<2>(bin0); counts(b02,2)+=s0; bounds(b02,2).extend(prim0);
  217. }
  218. }
  219. /*! bins an array of primitives */
  220. template<typename BinBoundsAndCenter>
  221. __forceinline void bin (const PrimRef* prims, size_t N, const BinMapping<BINS>& mapping, const BinBoundsAndCenter& binBoundsAndCenter)
  222. {
  223. if (N == 0) return;
  224. size_t i;
  225. for (i=0; i<N-1; i+=2)
  226. {
  227. /*! map even and odd primitive to bin */
  228. BBox prim0; Vec3fa center0; binBoundsAndCenter.binBoundsAndCenter(prims[i+0],prim0,center0);
  229. const vint4 bin0 = (vint4)mapping.bin(center0);
  230. BBox prim1; Vec3fa center1; binBoundsAndCenter.binBoundsAndCenter(prims[i+1],prim1,center1);
  231. const vint4 bin1 = (vint4)mapping.bin(center1);
  232. /*! increase bounds for bins for even primitive */
  233. const unsigned int s0 = prims[i+0].size();
  234. const int b00 = extract<0>(bin0); counts(b00,0)+=s0; bounds(b00,0).extend(prim0);
  235. const int b01 = extract<1>(bin0); counts(b01,1)+=s0; bounds(b01,1).extend(prim0);
  236. const int b02 = extract<2>(bin0); counts(b02,2)+=s0; bounds(b02,2).extend(prim0);
  237. /*! increase bounds of bins for odd primitive */
  238. const unsigned int s1 = prims[i+1].size();
  239. const int b10 = extract<0>(bin1); counts(b10,0)+=s1; bounds(b10,0).extend(prim1);
  240. const int b11 = extract<1>(bin1); counts(b11,1)+=s1; bounds(b11,1).extend(prim1);
  241. const int b12 = extract<2>(bin1); counts(b12,2)+=s1; bounds(b12,2).extend(prim1);
  242. }
  243. /*! for uneven number of primitives */
  244. if (i < N)
  245. {
  246. /*! map primitive to bin */
  247. BBox prim0; Vec3fa center0; binBoundsAndCenter.binBoundsAndCenter(prims[i+0],prim0,center0);
  248. const vint4 bin0 = (vint4)mapping.bin(center0);
  249. /*! increase bounds of bins */
  250. const unsigned int s0 = prims[i+0].size();
  251. const int b00 = extract<0>(bin0); counts(b00,0)+=s0; bounds(b00,0).extend(prim0);
  252. const int b01 = extract<1>(bin0); counts(b01,1)+=s0; bounds(b01,1).extend(prim0);
  253. const int b02 = extract<2>(bin0); counts(b02,2)+=s0; bounds(b02,2).extend(prim0);
  254. }
  255. }
  256. __forceinline void bin(const PrimRef* prims, size_t begin, size_t end, const BinMapping<BINS>& mapping) {
  257. bin(prims+begin,end-begin,mapping);
  258. }
  259. template<typename BinBoundsAndCenter>
  260. __forceinline void bin(const PrimRef* prims, size_t begin, size_t end, const BinMapping<BINS>& mapping, const BinBoundsAndCenter& binBoundsAndCenter) {
  261. bin<BinBoundsAndCenter>(prims+begin,end-begin,mapping,binBoundsAndCenter);
  262. }
  263. /*! merges in other binning information */
  264. __forceinline void merge (const BinInfoT& other, size_t numBins)
  265. {
  266. for (size_t i=0; i<numBins; i++)
  267. {
  268. counts(i) += other.counts(i);
  269. bounds(i,0).extend(other.bounds(i,0));
  270. bounds(i,1).extend(other.bounds(i,1));
  271. bounds(i,2).extend(other.bounds(i,2));
  272. }
  273. }
  274. /*! reduces binning information */
  275. static __forceinline const BinInfoT reduce (const BinInfoT& a, const BinInfoT& b, const size_t numBins = BINS)
  276. {
  277. BinInfoT c;
  278. for (size_t i=0; i<numBins; i++)
  279. {
  280. c.counts(i) = a.counts(i)+b.counts(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. /*! finds the best split by scanning binning information */
  288. __forceinline Split best(const BinMapping<BINS>& mapping, const size_t blocks_shift) const
  289. {
  290. /* sweep from right to left and compute parallel prefix of merged bounds */
  291. vfloat4 rAreas[BINS];
  292. vuint4 rCounts[BINS];
  293. vuint4 count = 0; BBox bx = empty; BBox by = empty; BBox bz = empty;
  294. for (size_t i=mapping.size()-1; i>0; i--)
  295. {
  296. count += counts(i);
  297. rCounts[i] = count;
  298. bx.extend(bounds(i,0)); rAreas[i][0] = expectedApproxHalfArea(bx);
  299. by.extend(bounds(i,1)); rAreas[i][1] = expectedApproxHalfArea(by);
  300. bz.extend(bounds(i,2)); rAreas[i][2] = expectedApproxHalfArea(bz);
  301. rAreas[i][3] = 0.0f;
  302. }
  303. /* sweep from left to right and compute SAH */
  304. vuint4 blocks_add = (1 << blocks_shift)-1;
  305. vuint4 ii = 1; vfloat4 vbestSAH = pos_inf; vuint4 vbestPos = 0;
  306. count = 0; bx = empty; by = empty; bz = empty;
  307. for (size_t i=1; i<mapping.size(); i++, ii+=1)
  308. {
  309. count += counts(i-1);
  310. bx.extend(bounds(i-1,0)); float Ax = expectedApproxHalfArea(bx);
  311. by.extend(bounds(i-1,1)); float Ay = expectedApproxHalfArea(by);
  312. bz.extend(bounds(i-1,2)); float Az = expectedApproxHalfArea(bz);
  313. const vfloat4 lArea = vfloat4(Ax,Ay,Az,Az);
  314. const vfloat4 rArea = rAreas[i];
  315. const vuint4 lCount = (count +blocks_add) >> (unsigned int)(blocks_shift); // if blocks_shift >=1 then lCount < 4B and could be represented with an vint4, which would allow for faster vfloat4 conversions.
  316. const vuint4 rCount = (rCounts[i]+blocks_add) >> (unsigned int)(blocks_shift);
  317. const vfloat4 sah = madd(lArea,vfloat4(lCount),rArea*vfloat4(rCount));
  318. //const vfloat4 sah = madd(lArea,vfloat4(vint4(lCount)),rArea*vfloat4(vint4(rCount)));
  319. vbestPos = select(sah < vbestSAH,ii ,vbestPos);
  320. vbestSAH = select(sah < vbestSAH,sah,vbestSAH);
  321. }
  322. /* find best dimension */
  323. float bestSAH = inf;
  324. int bestDim = -1;
  325. int bestPos = 0;
  326. for (int dim=0; dim<3; dim++)
  327. {
  328. /* ignore zero sized dimensions */
  329. if (unlikely(mapping.invalid(dim)))
  330. continue;
  331. /* test if this is a better dimension */
  332. if (vbestSAH[dim] < bestSAH && vbestPos[dim] != 0) {
  333. bestDim = dim;
  334. bestPos = vbestPos[dim];
  335. bestSAH = vbestSAH[dim];
  336. }
  337. }
  338. return Split(bestSAH,bestDim,bestPos,mapping);
  339. }
  340. /*! calculates extended split information */
  341. __forceinline void getSplitInfo(const BinMapping<BINS>& mapping, const Split& split, SplitInfoT<BBox>& info) const
  342. {
  343. if (split.dim == -1) {
  344. new (&info) SplitInfoT<BBox>(0,empty,0,empty);
  345. return;
  346. }
  347. size_t leftCount = 0;
  348. BBox leftBounds = empty;
  349. for (size_t i=0; i<(size_t)split.pos; i++) {
  350. leftCount += counts(i,split.dim);
  351. leftBounds.extend(bounds(i,split.dim));
  352. }
  353. size_t rightCount = 0;
  354. BBox rightBounds = empty;
  355. for (size_t i=split.pos; i<mapping.size(); i++) {
  356. rightCount += counts(i,split.dim);
  357. rightBounds.extend(bounds(i,split.dim));
  358. }
  359. new (&info) SplitInfoT<BBox>(leftCount,leftBounds,rightCount,rightBounds);
  360. }
  361. /*! gets the number of primitives left of the split */
  362. __forceinline size_t getLeftCount(const BinMapping<BINS>& mapping, const Split& split) const
  363. {
  364. if (unlikely(split.dim == -1)) return -1;
  365. size_t leftCount = 0;
  366. for (size_t i = 0; i < (size_t)split.pos; i++) {
  367. leftCount += counts(i, split.dim);
  368. }
  369. return leftCount;
  370. }
  371. /*! gets the number of primitives right of the split */
  372. __forceinline size_t getRightCount(const BinMapping<BINS>& mapping, const Split& split) const
  373. {
  374. if (unlikely(split.dim == -1)) return -1;
  375. size_t rightCount = 0;
  376. for (size_t i = (size_t)split.pos; i<mapping.size(); i++) {
  377. rightCount += counts(i, split.dim);
  378. }
  379. return rightCount;
  380. }
  381. private:
  382. BBox _bounds[BINS][3]; //!< geometry bounds for each bin in each dimension
  383. vuint4 _counts[BINS]; //!< counts number of primitives that map into the bins
  384. };
  385. }
  386. template<typename BinInfoT, typename BinMapping, typename PrimRef>
  387. __forceinline void bin_parallel(BinInfoT& binner, const PrimRef* prims, size_t begin, size_t end, size_t blockSize, size_t parallelThreshold, const BinMapping& mapping)
  388. {
  389. if (likely(end-begin < parallelThreshold)) {
  390. binner.bin(prims,begin,end,mapping);
  391. } else {
  392. binner = parallel_reduce(begin,end,blockSize,binner,
  393. [&](const range<size_t>& r) -> BinInfoT { BinInfoT binner(empty); binner.bin(prims + r.begin(), r.size(), mapping); return binner; },
  394. [&](const BinInfoT& b0, const BinInfoT& b1) -> BinInfoT { BinInfoT r = b0; r.merge(b1, mapping.size()); return r; });
  395. }
  396. }
  397. template<typename BinBoundsAndCenter, typename BinInfoT, typename BinMapping, typename PrimRef>
  398. __forceinline void bin_parallel(BinInfoT& binner, const PrimRef* prims, size_t begin, size_t end, size_t blockSize, size_t parallelThreshold, const BinMapping& mapping, const BinBoundsAndCenter& binBoundsAndCenter)
  399. {
  400. if (likely(end-begin < parallelThreshold)) {
  401. binner.bin(prims,begin,end,mapping,binBoundsAndCenter);
  402. } else {
  403. binner = parallel_reduce(begin,end,blockSize,binner,
  404. [&](const range<size_t>& r) -> BinInfoT { BinInfoT binner(empty); binner.bin(prims + r.begin(), r.size(), mapping, binBoundsAndCenter); return binner; },
  405. [&](const BinInfoT& b0, const BinInfoT& b1) -> BinInfoT { BinInfoT r = b0; r.merge(b1, mapping.size()); return r; });
  406. }
  407. }
  408. template<bool parallel, typename BinInfoT, typename BinMapping, typename PrimRef>
  409. __forceinline void bin_serial_or_parallel(BinInfoT& binner, const PrimRef* prims, size_t begin, size_t end, size_t blockSize, const BinMapping& mapping)
  410. {
  411. if (!parallel) {
  412. binner.bin(prims,begin,end,mapping);
  413. } else {
  414. binner = parallel_reduce(begin,end,blockSize,binner,
  415. [&](const range<size_t>& r) -> BinInfoT { BinInfoT binner(empty); binner.bin(prims + r.begin(), r.size(), mapping); return binner; },
  416. [&](const BinInfoT& b0, const BinInfoT& b1) -> BinInfoT { BinInfoT r = b0; r.merge(b1, mapping.size()); return r; });
  417. }
  418. }
  419. template<bool parallel, typename BinBoundsAndCenter, typename BinInfoT, typename BinMapping, typename PrimRef>
  420. __forceinline void bin_serial_or_parallel(BinInfoT& binner, const PrimRef* prims, size_t begin, size_t end, size_t blockSize, const BinMapping& mapping, const BinBoundsAndCenter& binBoundsAndCenter)
  421. {
  422. if (!parallel) {
  423. binner.bin(prims,begin,end,mapping,binBoundsAndCenter);
  424. } else {
  425. binner = parallel_reduce(begin,end,blockSize,binner,
  426. [&](const range<size_t>& r) -> BinInfoT { BinInfoT binner(empty); binner.bin(prims + r.begin(), r.size(), mapping, binBoundsAndCenter); return binner; },
  427. [&](const BinInfoT& b0, const BinInfoT& b1) -> BinInfoT { BinInfoT r = b0; r.merge(b1, mapping.size()); return r; });
  428. }
  429. }
  430. }