// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "../bvh/bvh.h" #include "../geometry/primitive.h" #include "../builders/bvh_builder_sah.h" #include "../builders/heuristic_binning_array_aligned.h" #include "../builders/heuristic_binning_array_unaligned.h" #include "../builders/heuristic_strand_array.h" #define NUM_HAIR_OBJECT_BINS 32 namespace embree { namespace isa { struct BVHBuilderHair { /*! settings for builder */ struct Settings { /*! default settings */ Settings () : branchingFactor(2), maxDepth(32), logBlockSize(0), minLeafSize(1), maxLeafSize(7), finished_range_threshold(inf) {} public: size_t branchingFactor; //!< branching factor of BVH to build size_t maxDepth; //!< maximum depth of BVH to build size_t logBlockSize; //!< log2 of blocksize for SAH heuristic size_t minLeafSize; //!< minimum size of a leaf size_t maxLeafSize; //!< maximum size of a leaf size_t finished_range_threshold; //!< finished range threshold }; template class BuilderT { ALIGNED_CLASS_(16); friend struct BVHBuilderHair; typedef FastAllocator::CachedAllocator Allocator; typedef HeuristicArrayBinningSAH HeuristicBinningSAH; typedef UnalignedHeuristicArrayBinningSAH UnalignedHeuristicBinningSAH; typedef HeuristicStrandSplit HeuristicStrandSplitSAH; static const size_t MAX_BRANCHING_FACTOR = 8; //!< maximum supported BVH branching factor static const size_t MIN_LARGE_LEAF_LEVELS = 8; //!< create balanced tree if we are that many levels before the maximum tree depth static const size_t SINGLE_THREADED_THRESHOLD = 4096; //!< threshold to switch to single threaded build static const size_t travCostAligned = 1; static const size_t travCostUnaligned = 5; static const size_t intCost = 6; BuilderT (Scene* scene, PrimRef* prims, const CreateAllocFunc& createAlloc, const CreateAABBNodeFunc& createAABBNode, const SetAABBNodeFunc& setAABBNode, const CreateOBBNodeFunc& createOBBNode, const SetOBBNodeFunc& setOBBNode, const CreateLeafFunc& createLeaf, const ProgressMonitor& progressMonitor, const ReportFinishedRangeFunc& reportFinishedRange, const Settings settings) : cfg(settings), prims(prims), createAlloc(createAlloc), createAABBNode(createAABBNode), setAABBNode(setAABBNode), createOBBNode(createOBBNode), setOBBNode(setOBBNode), createLeaf(createLeaf), progressMonitor(progressMonitor), reportFinishedRange(reportFinishedRange), alignedHeuristic(prims), unalignedHeuristic(scene,prims), strandHeuristic(scene,prims) {} /*! checks if all primitives are from the same geometry */ __forceinline bool sameGeometry(const PrimInfoRange& range) { if (range.size() == 0) return true; unsigned int firstGeomID = prims[range.begin()].geomID(); for (size_t i=range.begin()+1; i cfg.maxDepth) throw_RTCError(RTC_ERROR_UNKNOWN,"depth limit reached"); /* create leaf for few primitives */ if (pinfo.size() <= cfg.maxLeafSize && sameGeometry(pinfo)) return createLeaf(prims,pinfo,alloc); /* fill all children by always splitting the largest one */ PrimInfoRange children[MAX_BRANCHING_FACTOR]; unsigned numChildren = 1; children[0] = pinfo; do { /* find best child with largest bounding box area */ int bestChild = -1; size_t bestSize = 0; for (unsigned i=0; i bestSize) { bestSize = children[i].size(); bestChild = i; } } if (bestChild == -1) break; /*! split best child into left and right child */ __aligned(64) PrimInfoRange left, right; if (!sameGeometry(children[bestChild])) { alignedHeuristic.splitByGeometry(children[bestChild],left,right); } else { alignedHeuristic.splitFallback(children[bestChild],left,right); } /* add new children left and right */ children[bestChild] = children[numChildren-1]; children[numChildren-1] = left; children[numChildren+0] = right; numChildren++; } while (numChildren < cfg.branchingFactor); /* create node */ auto node = createAABBNode(alloc); for (size_t i=0; i> cfg.logBlockSize; const float leafSAH = intCost*float(blocks)*halfArea(pinfo.geomBounds); /* try standard binning in aligned space */ float alignedObjectSAH = inf; HeuristicBinningSAH::Split alignedObjectSplit; if (aligned) { alignedObjectSplit = alignedHeuristic.find(pinfo,cfg.logBlockSize); alignedObjectSAH = travCostAligned*halfArea(pinfo.geomBounds) + intCost*alignedObjectSplit.splitSAH(); bestSAH = min(alignedObjectSAH,bestSAH); } /* try standard binning in unaligned space */ UnalignedHeuristicBinningSAH::Split unalignedObjectSplit; LinearSpace3fa uspace; float unalignedObjectSAH = inf; if (bestSAH > 0.7f*leafSAH) { uspace = unalignedHeuristic.computeAlignedSpace(pinfo); const PrimInfoRange sinfo = unalignedHeuristic.computePrimInfo(pinfo,uspace); unalignedObjectSplit = unalignedHeuristic.find(sinfo,cfg.logBlockSize,uspace); unalignedObjectSAH = travCostUnaligned*halfArea(pinfo.geomBounds) + intCost*unalignedObjectSplit.splitSAH(); bestSAH = min(unalignedObjectSAH,bestSAH); } /* try splitting into two strands */ HeuristicStrandSplitSAH::Split strandSplit; float strandSAH = inf; if (bestSAH > 0.7f*leafSAH && pinfo.size() <= 256) { strandSplit = strandHeuristic.find(pinfo,cfg.logBlockSize); strandSAH = travCostUnaligned*halfArea(pinfo.geomBounds) + intCost*strandSplit.splitSAH(); bestSAH = min(strandSAH,bestSAH); } /* fallback if SAH heuristics failed */ if (unlikely(!std::isfinite(bestSAH))) { alignedHeuristic.deterministic_order(pinfo); alignedHeuristic.splitFallback(pinfo,linfo,rinfo); } /* perform aligned split if this is best */ else if (bestSAH == alignedObjectSAH) { alignedHeuristic.split(alignedObjectSplit,pinfo,linfo,rinfo); } /* perform unaligned split if this is best */ else if (bestSAH == unalignedObjectSAH) { unalignedHeuristic.split(unalignedObjectSplit,uspace,pinfo,linfo,rinfo); aligned = false; } /* perform strand split if this is best */ else if (bestSAH == strandSAH) { strandHeuristic.split(strandSplit,pinfo,linfo,rinfo); aligned = false; } /* can never happen */ else assert(false); } /*! recursive build */ NodeRef recurse(size_t depth, const PrimInfoRange& pinfo, Allocator alloc, bool toplevel, bool alloc_barrier) { /* get thread local allocator */ if (!alloc) alloc = createAlloc(); /* call memory monitor function to signal progress */ if (toplevel && pinfo.size() <= SINGLE_THREADED_THRESHOLD) progressMonitor(pinfo.size()); PrimInfoRange children[MAX_BRANCHING_FACTOR]; /* create leaf node */ if (depth+MIN_LARGE_LEAF_LEVELS >= cfg.maxDepth || pinfo.size() <= cfg.minLeafSize) { alignedHeuristic.deterministic_order(pinfo); return createLargeLeaf(depth,pinfo,alloc); } /* fill all children by always splitting the one with the largest surface area */ size_t numChildren = 1; children[0] = pinfo; bool aligned = true; do { /* find best child with largest bounding box area */ ssize_t bestChild = -1; float bestArea = neg_inf; for (size_t i=0; i bestArea) { bestArea = area(children[i].geomBounds); bestChild = i; } } if (bestChild == -1) break; /*! split best child into left and right child */ PrimInfoRange left, right; split(children[bestChild],left,right,aligned); /* add new children left and right */ children[bestChild] = children[numChildren-1]; children[numChildren-1] = left; children[numChildren+0] = right; numChildren++; } while (numChildren < cfg.branchingFactor); NodeRef node; /* create aligned node */ if (aligned) { node = createAABBNode(alloc); /* spawn tasks or ... */ if (pinfo.size() > SINGLE_THREADED_THRESHOLD) { parallel_for(size_t(0), numChildren, [&] (const range& r) { for (size_t i=r.begin(); i cfg.finished_range_threshold && children[i].size() <= cfg.finished_range_threshold; setAABBNode(node,i,recurse(depth+1,children[i],nullptr,true,child_alloc_barrier),children[i].geomBounds); _mm_mfence(); // to allow non-temporal stores during build } }); } /* ... continue sequentially */ else { for (size_t i=0; i cfg.finished_range_threshold && children[i].size() <= cfg.finished_range_threshold; setAABBNode(node,i,recurse(depth+1,children[i],alloc,false,child_alloc_barrier),children[i].geomBounds); } } } /* create unaligned node */ else { node = createOBBNode(alloc); /* spawn tasks or ... */ if (pinfo.size() > SINGLE_THREADED_THRESHOLD) { parallel_for(size_t(0), numChildren, [&] (const range& r) { for (size_t i=r.begin(); i cfg.finished_range_threshold && children[i].size() <= cfg.finished_range_threshold; setOBBNode(node,i,recurse(depth+1,children[i],nullptr,true,child_alloc_barrier),obounds); _mm_mfence(); // to allow non-temporal stores during build } }); } /* ... continue sequentially */ else { for (size_t i=0; i cfg.finished_range_threshold && children[i].size() <= cfg.finished_range_threshold; setOBBNode(node,i,recurse(depth+1,children[i],alloc,false,child_alloc_barrier),obounds); } } } /* reports a finished range of primrefs */ if (unlikely(alloc_barrier)) reportFinishedRange(pinfo); return node; } private: Settings cfg; PrimRef* prims; const CreateAllocFunc& createAlloc; const CreateAABBNodeFunc& createAABBNode; const SetAABBNodeFunc& setAABBNode; const CreateOBBNodeFunc& createOBBNode; const SetOBBNodeFunc& setOBBNode; const CreateLeafFunc& createLeaf; const ProgressMonitor& progressMonitor; const ReportFinishedRangeFunc& reportFinishedRange; private: HeuristicBinningSAH alignedHeuristic; UnalignedHeuristicBinningSAH unalignedHeuristic; HeuristicStrandSplitSAH strandHeuristic; }; template static NodeRef build (const CreateAllocFunc& createAlloc, const CreateAABBNodeFunc& createAABBNode, const SetAABBNodeFunc& setAABBNode, const CreateOBBNodeFunc& createOBBNode, const SetOBBNodeFunc& setOBBNode, const CreateLeafFunc& createLeaf, const ProgressMonitor& progressMonitor, const ReportFinishedRangeFunc& reportFinishedRange, Scene* scene, PrimRef* prims, const PrimInfo& pinfo, const Settings settings) { typedef BuilderT Builder; Builder builder(scene,prims,createAlloc, createAABBNode,setAABBNode, createOBBNode,setOBBNode, createLeaf,progressMonitor,reportFinishedRange,settings); NodeRef root = builder.recurse(1,pinfo,nullptr,true,false); _mm_mfence(); // to allow non-temporal stores during build return root; } }; } }