10#include <Common/assertion.h>
11#include <Common/logging.h>
12#include <Common/utility.h>
20template<std::
size_t N,
typename Item,
typename ItemToAABB>
21inline TBvhBuilder<N, Item, ItemToAABB>
23 ItemToAABB itemToAABB,
29 , m_itemToAABB(
std::move(itemToAABB))
31 PH_ASSERT_IN_RANGE_INCLUSIVE(params.
numSahBuckets, 2, MAX_SAH_BUCKETS);
34template<std::
size_t N,
typename Item,
typename ItemToAABB>
41 m_infoBuffer.resize(items.size());
42 for(std::size_t i = 0; i < items.size(); i++)
44 m_infoBuffer[i] =
ItemInfoType(m_itemToAABB(items[i]), items[i]);
50 const std::size_t maxLeaves = items.size();
51 const std::size_t maxNodes = 2 * maxLeaves - 1;
52 m_infoNodes.reserve(maxNodes);
55 switch(m_params.splitMethod)
58 rootNode = buildBvhInfoNodeRecursive<EBvhNodeSplitMethod::EqualItems>(
63 rootNode = buildBvhInfoNodeRecursive<EBvhNodeSplitMethod::SAH_Buckets_OneAxis>(
68 PH_DEFAULT_LOG(Warning,
69 "BVH{} builder: unsupported BVH split method", N);
73 PH_ASSERT_EQ(m_infoBuffer.size(), items.size());
74 PH_ASSERT_LE(m_infoNodes.size(), maxNodes);
77 PH_ASSERT_EQ(m_infoNodes.size(), calcTotalNodes(rootNode));
78 PH_ASSERT_EQ(m_infoBuffer.size(), calcTotalItems(rootNode));
82 "BVH{} builder: node buffer utilization = {}",
83 N,
static_cast<float>(m_infoNodes.size()) / maxNodes);
88template<std::
size_t N,
typename Item,
typename ItemToAABB>
96template<std::
size_t N,
typename Item,
typename ItemToAABB>
101 return m_infoNodes.size();
104template<std::
size_t N,
typename Item,
typename ItemToAABB>
109 return m_infoBuffer.size();
112template<std::
size_t N,
typename Item,
typename ItemToAABB>
113template<EBvhNodeSplitMethod SPLIT_METHOD>
117->
const InfoNodeType*
120 PH_ASSERT_LT(m_infoNodes.size(), m_infoNodes.capacity());
122 m_infoNodes.push_back(InfoNodeType{});
123 InfoNodeType*
const node = &m_infoNodes.back();
126 for(
const ItemInfoType& itemInfo : itemInfos)
132 if(itemInfos.size() <= 1)
134 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
137 if(itemInfos.empty())
139 PH_DEFAULT_LOG(Warning,
140 "BVH{} builder: leaf node without item detected", N);
147 AABB3D centroidsAABB(itemInfos.front().aabbCentroid);
148 for(
const ItemInfoType& itemInfo : itemInfos)
153 Vector3R extents = centroidsAABB.getExtents();
155 if(!extents.isNonNegative())
157 PH_DEFAULT_LOG(Warning,
158 "BVH{} builder: negative AABB extent detected", N);
163 const auto maxDimension = extents.maxDimension();
164 if(centroidsAABB.getMinVertex()[maxDimension] == centroidsAABB.getMaxVertex()[maxDimension])
166 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
169 else if constexpr(N == 2)
171 bool isSplitted =
false;
176 isSplitted = binarySplitWithEqualItems(
180 &positiveChildItems);
184 isSplitted = binarySplitWithSahBuckets(
190 &positiveChildItems);
193 if(!isSplitted && itemInfos.size() > m_params.maxNodeItems)
195 isSplitted = binarySplitWithEqualItems(
199 &positiveChildItems);
204 PH_DEFAULT_DEBUG_LOG(
205 "BVH{} builder: unsupported BVH split method detected", N);
210 if(isSplitted && (negativeChildItems.empty() || positiveChildItems.empty()))
212 PH_DEFAULT_DEBUG_LOG(
213 "BVH{} builder: bad binary split detected: #neg-child={}, #pos-child={}",
214 N, negativeChildItems.size(), positiveChildItems.size());
221 *node = InfoNodeType::makeInternal(
223 buildBvhInfoNodeRecursive<SPLIT_METHOD>(negativeChildItems),
224 buildBvhInfoNodeRecursive<SPLIT_METHOD>(positiveChildItems)
230 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
236 bool isSplitted =
false;
237 std::array<TSpan<ItemInfoType>, N> itemParts;
240 isSplitted = splitWithEqualItems(
247 isSplitted = splitWithSahBuckets(
255 if(!isSplitted && itemInfos.size() > m_params.maxNodeItems)
257 isSplitted = splitWithEqualItems(
265 PH_DEFAULT_DEBUG_LOG(
266 "BVH{} builder: unsupported BVH split method detected", N);
272 std::array<const InfoNodeType*, N> children{};
273 for(std::size_t ci = 0; ci < N; ++ci)
275 if(!itemParts[ci].empty())
277 children[ci] = buildBvhInfoNodeRecursive<SPLIT_METHOD>(itemParts[ci]);
281 *node = InfoNodeType::makeInternal(
287 *node = InfoNodeType::makeLeaf(itemInfos, nodeAABB);
295template<std::
size_t N,
typename Item,
typename ItemToAABB>
296inline std::size_t TBvhBuilder<N, Item, ItemToAABB>
304 std::size_t result = 1;
305 for(std::size_t ci = 0; ci < node->
numChildren(); ++ci)
312template<std::
size_t N,
typename Item,
typename ItemToAABB>
321 std::size_t result = node->
getItems().size();
322 for(std::size_t ci = 0; ci < node->
numChildren(); ++ci)
329template<std::
size_t N,
typename Item,
typename ItemToAABB>
338 std::size_t maxDepth = 0;
339 for(std::size_t ci = 0; ci < node->
numChildren(); ++ci)
344 maxDepth = std::max(calcMaxDepth(node->
getChild(ci)) + 1, maxDepth);
350template<std::
size_t N,
typename Item,
typename ItemToAABB>
354 const std::size_t splitDimension,
358 static_assert(N == 2,
"Requires a binary BVH builder.");
361 PH_ASSERT_GE(itemInfos.size(), 2);
363 const std::size_t midIndex = itemInfos.size() / 2 - 1;
365 auto sortedItemInfos = itemInfos;
367 sortedItemInfos.begin(),
368 sortedItemInfos.begin() + midIndex,
369 sortedItemInfos.end(),
370 [splitDimension](
const ItemInfoType& a,
const ItemInfoType& b)
372 return a.aabbCentroid[splitDimension] < b.aabbCentroid[splitDimension];
375 PH_ASSERT(out_negativePart);
376 PH_ASSERT(out_positivePart);
377 *out_negativePart = sortedItemInfos.subspan(0, midIndex + 1);
378 *out_positivePart = sortedItemInfos.subspan(midIndex + 1);
380 PH_ASSERT(!out_negativePart->empty() && !out_positivePart->empty());
384template<std::
size_t N,
typename Item,
typename ItemToAABB>
388 const std::size_t splitDimension,
390 const AABB3D& itemsCentroidAABB,
394 static_assert(N == 2,
"Requires a binary BVH builder.");
397 PH_ASSERT_GE(itemInfos.size(), 2);
399 const auto dim = splitDimension;
402 PH_ASSERT_GE(rcpSplitExtent, 0.0_r);
404 std::array<SahBucket, MAX_SAH_BUCKETS> buckets{};
405 for(
const ItemInfoType& itemInfo : itemInfos)
407 const real factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.
getMinVertex()[dim]) * rcpSplitExtent;
408 const auto bucketIndex =
clamp<std::size_t>(
static_cast<std::size_t
>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
410 buckets[bucketIndex].aabb = buckets[bucketIndex].isEmpty()
411 ? itemInfo.aabb: buckets[bucketIndex].aabb.unionWith(itemInfo.aabb);
412 buckets[bucketIndex].numItems++;
415 std::array<real, MAX_SAH_BUCKETS - 1> splitCosts{};
416 for(std::size_t i = 0; i < m_params.numSahBuckets - 1; ++i)
418 std::size_t numNegPartItems = 0;
420 for(std::size_t j = 0; j <= i; ++j)
422 numNegPartItems += buckets[j].numItems;
423 negPartAABB.unionWith(buckets[j].aabb);
426 std::size_t numPosPartItems = 0;
428 for(std::size_t j = i + 1; j < m_params.numSahBuckets; ++j)
430 numPosPartItems += buckets[j].numItems;
431 posPartAABB.unionWith(buckets[j].aabb);
437 negPartAABB.getSurfaceArea() / itemsAABB.
getSurfaceArea(), 0.0_r, 1.0_r);
439 posPartAABB.getSurfaceArea() / itemsAABB.
getSurfaceArea(), 0.0_r, 1.0_r);
442 m_params.traversalCost +
443 static_cast<real
>(numNegPartItems) * m_params.interactCost * probTestingNegPart +
444 static_cast<real
>(numPosPartItems) * m_params.interactCost * probTestingPosPart;
447 const auto minCostIndex =
static_cast<std::size_t
>(
448 std::min_element(splitCosts.begin(), splitCosts.begin() + m_params.numSahBuckets) - splitCosts.begin());
449 const real minSplitCost = splitCosts[minCostIndex];
450 const real noSplitCost = m_params.interactCost *
static_cast<real
>(itemInfos.size());
452 PH_ASSERT(out_negativePart);
453 PH_ASSERT(out_positivePart);
454 if(minSplitCost < noSplitCost || itemInfos.size() > m_params.maxNodeItems)
456 auto sortedItemInfos = itemInfos;
457 auto posPartBegin = std::partition(
458 sortedItemInfos.begin(),
459 sortedItemInfos.end(),
460 [
this, itemsCentroidAABB, dim, rcpSplitExtent, minCostIndex](
const ItemInfoType& itemInfo)
462 const real factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.getMinVertex()[dim]) * rcpSplitExtent;
463 const auto bucketIndex = clamp<std::size_t>(static_cast<std::size_t>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
464 return bucketIndex <= minCostIndex;
467 *out_negativePart = sortedItemInfos.subspan(0, posPartBegin - sortedItemInfos.begin());
468 *out_positivePart = sortedItemInfos.subspan(posPartBegin - sortedItemInfos.begin());
469 return !out_negativePart->empty() && !out_positivePart->empty();
477template<std::
size_t N,
typename Item,
typename ItemToAABB>
478inline bool TBvhBuilder<N, Item, ItemToAABB>
479::splitWithEqualItems(
481 const std::size_t splitDimension,
484 PH_ASSERT(out_parts);
489 auto sortedItemInfos = itemInfos;
490 bool hasAnySplit =
true;
491 for(std::size_t i = 0; i < N; ++i)
496 if(beginIdx < endIdx && i < N - 1)
499 sortedItemInfos.begin() + beginIdx,
500 sortedItemInfos.begin() + endIdx - 1,
501 sortedItemInfos.end(),
502 [splitDimension](
const ItemInfoType& a,
const ItemInfoType& b)
504 return a.aabbCentroid[splitDimension] < b.aabbCentroid[splitDimension];
508 (*out_parts)[i] = sortedItemInfos.subspan(beginIdx, endIdx - beginIdx);
509 hasAnySplit = hasAnySplit && (*out_parts)[i].size() < itemInfos.size();
514template<std::
size_t N,
typename Item,
typename ItemToAABB>
515inline bool TBvhBuilder<N, Item, ItemToAABB>
516::splitWithSahBuckets(
518 const std::size_t splitDimension,
520 const AABB3D& itemsCentroidAABB,
523 PH_ASSERT(out_parts);
525 const auto dim = splitDimension;
526 const auto rcpSplitExtent =
safe_rcp(itemsCentroidAABB.getExtents()[dim]);
528 PH_ASSERT_GE(rcpSplitExtent, 0.0_r);
530 std::array<SahBucket, MAX_SAH_BUCKETS> buckets{};
531 for(
const ItemInfoType& itemInfo : itemInfos)
533 const real factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.getMinVertex()[dim]) * rcpSplitExtent;
534 const auto bucketIndex =
clamp<std::size_t>(
static_cast<std::size_t
>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
536 buckets[bucketIndex].aabb = buckets[bucketIndex].isEmpty()
537 ? itemInfo.aabb: buckets[bucketIndex].aabb.unionWith(itemInfo.aabb);
538 buckets[bucketIndex].numItems++;
541 const auto noSplitCost =
static_cast<real
>(m_params.interactCost * itemInfos.size());
542 const auto emptySplitEnds = make_array<std::size_t, N>(m_params.numSahBuckets);
544 auto bestSplitCost = noSplitCost;
545 auto bestSplitEnds = emptySplitEnds;
546 splitWithSahBucketsBacktracking(
549 {buckets.begin(), m_params.numSahBuckets},
553 m_params.traversalCost,
557 if(bestSplitCost < noSplitCost || itemInfos.size() > m_params.maxNodeItems)
561 auto sortedItemInfos = itemInfos;
562 bool hasAnySplit =
true;
563 for(std::size_t i = 0; i < N; ++i)
565 const auto splitBegin = i > 0 ? bestSplitEnds[i - 1] : 0;
566 const auto splitEnd = bestSplitEnds[i];
569 auto nextPartBegin = sortedItemInfos.end();
570 if(splitBegin < splitEnd && i < N - 1)
572 nextPartBegin = std::partition(
573 sortedItemInfos.begin(),
574 sortedItemInfos.end(),
575 [
this, itemsCentroidAABB, dim, rcpSplitExtent, splitEnd](
const ItemInfoType& itemInfo)
577 const auto factor = (itemInfo.aabbCentroid[dim] - itemsCentroidAABB.getMinVertex()[dim]) * rcpSplitExtent;
578 const auto bucketIndex = clamp<std::size_t>(static_cast<std::size_t>(factor * m_params.numSahBuckets), 0, m_params.numSahBuckets - 1);
579 return bucketIndex < splitEnd;
583 const auto numPartItems = nextPartBegin - sortedItemInfos.begin();
584 (*out_parts)[i] = sortedItemInfos.subspan(0, numPartItems);
585 sortedItemInfos = sortedItemInfos.subspan(numPartItems);
586 hasAnySplit = hasAnySplit && (*out_parts)[i].size() < itemInfos.size();
596template<std::
size_t N,
typename Item,
typename ItemToAABB>
597inline void TBvhBuilder<N, Item, ItemToAABB>
598::splitWithSahBucketsBacktracking(
599 const std::size_t splitDimension,
602 const std::size_t numSplits,
603 const std::size_t splitBegin,
604 const std::array<std::size_t, N>& splitEnds,
606 real*
const out_bestCost,
607 std::array<std::size_t, N>*
const out_bestSplitEnds)
const
609 PH_ASSERT(out_bestCost);
610 PH_ASSERT(out_bestSplitEnds);
613 if(cost >= *out_bestCost)
619 if(splitBegin == buckets.size() || numSplits == N)
621 PH_ASSERT_LT(cost, *out_bestCost);
622 *out_bestCost = cost;
623 *out_bestSplitEnds = splitEnds;
628 PH_ASSERT_LT(splitBegin, buckets.size());
629 PH_ASSERT_LT(numSplits, N);
632 std::size_t splitEnd = numSplits == N - 1 ? buckets.size() : splitBegin + 1;
635 while(splitEnd <= buckets.size())
637 auto newSplitEnds = splitEnds;
638 newSplitEnds[numSplits] = splitEnd;
640 std::size_t numItems = 0;
642 for(std::size_t bi = splitBegin; bi < splitEnd; ++bi)
644 numItems += buckets[bi].numItems;
645 aabb.unionWith(buckets[bi].aabb);
651 aabb.getSurfaceArea() / itemsAABB.getSurfaceArea(), 0.0_r, 1.0_r);
653 const auto newCost =
static_cast<real
>(
654 cost + numItems * m_params.interactCost * testProb);
656 splitWithSahBucketsBacktracking(
668 if(newCost >= *out_bestCost)
Definition BvhParams.h:20
uint32 numSahBuckets
Definition BvhParams.h:26
T getSurfaceArea() const
Get the surface area of the bound.
Definition TAABB3D.ipp:177
static TAABB3D makeUnioned(const TAABB3D &a, const TAABB3D &b)
Definition TAABB3D.ipp:26
static TAABB3D makeEmpty()
Definition TAABB3D.ipp:15
TVector3< T > getExtents() const
Get the side lengths of the bound.
Definition TAABB3D.ipp:171
const TVector3< T > & getMinVertex() const
Get the corner vertex of the minimum (—) octant.
Definition TAABB3D.ipp:146
Definition TBvhBuilder.h:25
General BVH node packed with additional information. This node type is typically used for building ot...
Definition TLinearDepthFirstWideBvh.h:21
auto getItems() const -> TSpanView< ItemInfoType >
Definition TBvhInfoNode.ipp:137
auto getChild(std::size_t childIdx) const -> const TBvhInfoNode *
Definition TBvhInfoNode.ipp:127
static constexpr std::size_t numChildren()
Definition TBvhInfoNode.ipp:65
Definition TBvhItemInfo.h:11
Miscellaneous math utilities.
Math functions and utilities.
Definition TransformInfo.h:10
T safe_clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound], fallback to lowerBound. If a floating-point value is non-...
Definition math.h:98
T safe_rcp(const T value)
Calculates the reciprocal of a value, fallback to 0. If the resulting reciprocal is non-finite (e....
Definition math.h:117
TAABB3D< real > AABB3D
Definition TAABB3D.h:21
TVector3< real > Vector3R
Definition math_fwd.h:52
std::pair< std::size_t, std::size_t > ith_evenly_divided_range(const std::size_t rangeIndex, const std::size_t totalSize, const std::size_t numDivisions)
Gets the i-th evenly divided range.
Definition math.h:379
T clamp(const T value, const T lowerBound, const T upperBound)
Clamps a value to [lowerBound, upperBound]. None of value, lowerBound and upperBound can be NaN,...
Definition math.h:77
std::span< const T, EXTENT > TSpanView
Same as TSpan, except that the objects are const-qualified. Note that for pointer types,...
Definition TSpan.h:19
std::span< T, EXTENT > TSpan
A contiguous sequence of objects of type T. Effectively the same as std::span.
Definition TSpan.h:12