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