123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- // 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_msmblur.h"
- #include "../builders/heuristic_binning_array_aligned.h"
- #include "../builders/heuristic_binning_array_unaligned.h"
- #include "../builders/heuristic_timesplit_array.h"
- namespace embree
- {
- namespace isa
- {
- struct BVHBuilderHairMSMBlur
- {
- /*! settings for msmblur builder */
- struct Settings
- {
- /*! default settings */
- Settings ()
- : branchingFactor(2), maxDepth(32), logBlockSize(0), minLeafSize(1), maxLeafSize(8) {}
- 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
- };
- struct BuildRecord
- {
- public:
- __forceinline BuildRecord () {}
- __forceinline BuildRecord (size_t depth)
- : depth(depth) {}
- __forceinline BuildRecord (const SetMB& prims, size_t depth)
- : depth(depth), prims(prims) {}
- __forceinline size_t size() const {
- return prims.size();
- }
- public:
- size_t depth; //!< depth of the root of this subtree
- SetMB prims; //!< the list of primitives
- };
- template<typename NodeRef,
- typename RecalculatePrimRef,
- typename CreateAllocFunc,
- typename CreateAABBNodeMBFunc,
- typename SetAABBNodeMBFunc,
- typename CreateOBBNodeMBFunc,
- typename SetOBBNodeMBFunc,
- typename CreateLeafFunc,
- typename ProgressMonitor>
- class BuilderT
- {
- ALIGNED_CLASS_(16);
- 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
- typedef BVHNodeRecordMB<NodeRef> NodeRecordMB;
- typedef BVHNodeRecordMB4D<NodeRef> NodeRecordMB4D;
- typedef FastAllocator::CachedAllocator Allocator;
- typedef LocalChildListT<BuildRecord,MAX_BRANCHING_FACTOR> LocalChildList;
- typedef HeuristicMBlurTemporalSplit<PrimRefMB,RecalculatePrimRef,MBLUR_NUM_TEMPORAL_BINS> HeuristicTemporal;
- typedef HeuristicArrayBinningMB<PrimRefMB,MBLUR_NUM_OBJECT_BINS> HeuristicBinning;
- typedef UnalignedHeuristicArrayBinningMB<PrimRefMB,MBLUR_NUM_OBJECT_BINS> UnalignedHeuristicBinning;
- public:
- BuilderT (Scene* scene,
- const RecalculatePrimRef& recalculatePrimRef,
- const CreateAllocFunc& createAlloc,
- const CreateAABBNodeMBFunc& createAABBNodeMB,
- const SetAABBNodeMBFunc& setAABBNodeMB,
- const CreateOBBNodeMBFunc& createOBBNodeMB,
- const SetOBBNodeMBFunc& setOBBNodeMB,
- const CreateLeafFunc& createLeaf,
- const ProgressMonitor& progressMonitor,
- const Settings settings)
- : cfg(settings),
- scene(scene),
- recalculatePrimRef(recalculatePrimRef),
- createAlloc(createAlloc),
- createAABBNodeMB(createAABBNodeMB), setAABBNodeMB(setAABBNodeMB),
- createOBBNodeMB(createOBBNodeMB), setOBBNodeMB(setOBBNodeMB),
- createLeaf(createLeaf),
- progressMonitor(progressMonitor),
- unalignedHeuristic(scene),
- temporalSplitHeuristic(scene->device,recalculatePrimRef) {}
- private:
- /*! checks if all primitives are from the same geometry */
- __forceinline bool sameGeometry(const SetMB& set)
- {
- mvector<PrimRefMB>& prims = *set.prims;
- unsigned int firstGeomID = prims[set.begin()].geomID();
- for (size_t i=set.begin()+1; i<set.end(); i++) {
- if (prims[i].geomID() != firstGeomID){
- return false;
- }
- }
- return true;
- }
-
- /*! performs some split if SAH approaches fail */
- void splitFallback(const SetMB& set, SetMB& lset, SetMB& rset)
- {
- mvector<PrimRefMB>& prims = *set.prims;
- const size_t begin = set.begin();
- const size_t end = set.end();
- const size_t center = (begin + end)/2;
- PrimInfoMB linfo = empty;
- for (size_t i=begin; i<center; i++)
- linfo.add_primref(prims[i]);
- PrimInfoMB rinfo = empty;
- for (size_t i=center; i<end; i++)
- rinfo.add_primref(prims[i]);
- new (&lset) SetMB(linfo,set.prims,range<size_t>(begin,center),set.time_range);
- new (&rset) SetMB(rinfo,set.prims,range<size_t>(center,end ),set.time_range);
- }
- void splitByGeometry(const SetMB& set, SetMB& lset, SetMB& rset)
- {
- assert(set.size() > 1);
- const size_t begin = set.begin();
- const size_t end = set.end();
- PrimInfoMB linfo(empty);
- PrimInfoMB rinfo(empty);
- unsigned int geomID = (*set.prims)[begin].geomID();
- size_t center = serial_partitioning(set.prims->data(),begin,end,linfo,rinfo,
- [&] ( const PrimRefMB& prim ) { return prim.geomID() == geomID; },
- [ ] ( PrimInfoMB& a, const PrimRefMB& ref ) { a.add_primref(ref); });
- new (&lset) SetMB(linfo,set.prims,range<size_t>(begin,center),set.time_range);
- new (&rset) SetMB(rinfo,set.prims,range<size_t>(center,end ),set.time_range);
- }
- /*! creates a large leaf that could be larger than supported by the BVH */
- NodeRecordMB4D createLargeLeaf(BuildRecord& current, Allocator alloc)
- {
- /* this should never occur but is a fatal error */
- if (current.depth > cfg.maxDepth)
- throw_RTCError(RTC_ERROR_UNKNOWN,"depth limit reached");
- /* special case when directly creating leaf without any splits that could shrink time_range */
- bool force_split = false;
- if (current.depth == 1 && current.size() > 0)
- {
- BBox1f c = empty;
- BBox1f p = current.prims.time_range;
- for (size_t i=current.prims.begin(); i<current.prims.end(); i++) {
- mvector<PrimRefMB>& prims = *current.prims.prims;
- c.extend(prims[i].time_range);
- }
-
- force_split = c.lower > p.lower || c.upper < p.upper;
- }
- /* create leaf for few primitives */
- if (current.size() <= cfg.maxLeafSize && sameGeometry(current.prims) && !force_split)
- return createLeaf(current.prims,alloc);
- /* fill all children by always splitting the largest one */
- LocalChildList children(current);
- NodeRecordMB4D values[MAX_BRANCHING_FACTOR];
- do {
- /* find best child with largest bounding box area */
- int bestChild = -1;
- size_t bestSize = 0;
- for (unsigned i=0; i<children.size(); i++)
- {
- /* ignore leaves as they cannot get split */
- if (children[i].size() <= cfg.maxLeafSize && sameGeometry(children[i].prims) && !force_split)
- continue;
- force_split = false;
- /* remember child with largest size */
- if (children[i].size() > bestSize) {
- bestSize = children[i].size();
- bestChild = i;
- }
- }
- if (bestChild == -1) break;
- /*! split best child into left and right child */
- BuildRecord left(current.depth+1);
- BuildRecord right(current.depth+1);
- if (!sameGeometry(children[bestChild].prims)) {
- splitByGeometry(children[bestChild].prims,left.prims,right.prims);
- } else {
- splitFallback(children[bestChild].prims,left.prims,right.prims);
- }
- children.split(bestChild,left,right,std::unique_ptr<mvector<PrimRefMB>>());
- } while (children.size() < cfg.branchingFactor);
- /* detect time_ranges that have shrunken */
- bool timesplit = false;
- for (size_t i=0; i<children.size(); i++) {
- const BBox1f c = children[i].prims.time_range;
- const BBox1f p = current.prims.time_range;
- timesplit |= c.lower > p.lower || c.upper < p.upper;
- }
-
- /* create node */
- NodeRef node = createAABBNodeMB(children.children.data(),children.numChildren,alloc,timesplit);
- LBBox3fa bounds = empty;
- for (size_t i=0; i<children.size(); i++) {
- values[i] = createLargeLeaf(children[i],alloc);
- bounds.extend(values[i].lbounds);
- }
- setAABBNodeMB(current,children.children.data(),node,values,children.numChildren);
- if (timesplit)
- bounds = current.prims.linearBounds(recalculatePrimRef);
-
- return NodeRecordMB4D(node,bounds,current.prims.time_range);
- }
- /*! performs split */
- std::unique_ptr<mvector<PrimRefMB>> split(const BuildRecord& current, BuildRecord& lrecord, BuildRecord& rrecord, bool& aligned, bool& timesplit)
- {
- /* variable to track the SAH of the best splitting approach */
- float bestSAH = inf;
- const float leafSAH = current.prims.leafSAH(cfg.logBlockSize);
- /* perform standard binning in aligned space */
- HeuristicBinning::Split alignedObjectSplit = alignedHeuristic.find(current.prims,cfg.logBlockSize);
- float alignedObjectSAH = alignedObjectSplit.splitSAH();
- bestSAH = min(alignedObjectSAH,bestSAH);
- /* perform standard binning in unaligned space */
- UnalignedHeuristicBinning::Split unalignedObjectSplit;
- LinearSpace3fa uspace;
- float unalignedObjectSAH = inf;
- if (alignedObjectSAH > 0.7f*leafSAH) {
- uspace = unalignedHeuristic.computeAlignedSpaceMB(scene,current.prims);
- const SetMB sset = current.prims.primInfo(recalculatePrimRef,uspace);
- unalignedObjectSplit = unalignedHeuristic.find(sset,cfg.logBlockSize,uspace);
- unalignedObjectSAH = 1.3f*unalignedObjectSplit.splitSAH(); // makes unaligned splits more expensive
- bestSAH = min(unalignedObjectSAH,bestSAH);
- }
- /* do temporal splits only if previous approaches failed to produce good SAH and the the time range is large enough */
- float temporal_split_sah = inf;
- typename HeuristicTemporal::Split temporal_split;
- if (bestSAH > 0.5f*leafSAH) {
- if (current.prims.time_range.size() > 1.01f/float(current.prims.max_num_time_segments)) {
- temporal_split = temporalSplitHeuristic.find(current.prims,cfg.logBlockSize);
- temporal_split_sah = temporal_split.splitSAH();
- bestSAH = min(temporal_split_sah,bestSAH);
- }
- }
- /* perform fallback split if SAH heuristics failed */
- if (unlikely(!std::isfinite(bestSAH))) {
- current.prims.deterministic_order();
- splitFallback(current.prims,lrecord.prims,rrecord.prims);
- }
- /* perform aligned split if this is best */
- else if (likely(bestSAH == alignedObjectSAH)) {
- alignedHeuristic.split(alignedObjectSplit,current.prims,lrecord.prims,rrecord.prims);
- }
- /* perform unaligned split if this is best */
- else if (likely(bestSAH == unalignedObjectSAH)) {
- unalignedHeuristic.split(unalignedObjectSplit,uspace,current.prims,lrecord.prims,rrecord.prims);
- aligned = false;
- }
- /* perform temporal split if this is best */
- else if (likely(bestSAH == temporal_split_sah)) {
- timesplit = true;
- return temporalSplitHeuristic.split(temporal_split,current.prims,lrecord.prims,rrecord.prims);
- }
- else
- assert(false);
- return std::unique_ptr<mvector<PrimRefMB>>();
- }
- /*! recursive build */
- NodeRecordMB4D recurse(BuildRecord& current, Allocator alloc, bool toplevel)
- {
- /* get thread local allocator */
- if (!alloc)
- alloc = createAlloc();
- /* call memory monitor function to signal progress */
- if (toplevel && current.size() <= SINGLE_THREADED_THRESHOLD)
- progressMonitor(current.size());
- /* create leaf node */
- if (current.depth+MIN_LARGE_LEAF_LEVELS >= cfg.maxDepth || current.size() <= cfg.minLeafSize) {
- current.prims.deterministic_order();
- return createLargeLeaf(current,alloc);
- }
- /* fill all children by always splitting the one with the largest surface area */
- NodeRecordMB4D values[MAX_BRANCHING_FACTOR];
- LocalChildList children(current);
- bool aligned = true;
- bool timesplit = false;
- do {
- /* find best child with largest bounding box area */
- ssize_t bestChild = -1;
- float bestArea = neg_inf;
- for (size_t i=0; i<children.size(); i++)
- {
- /* ignore leaves as they cannot get split */
- if (children[i].size() <= cfg.minLeafSize)
- continue;
- /* remember child with largest area */
- const float A = children[i].prims.halfArea();
- if (A > bestArea) {
- bestArea = children[i].prims.halfArea();
- bestChild = i;
- }
- }
- if (bestChild == -1) break;
- /*! split best child into left and right child */
- BuildRecord left(current.depth+1);
- BuildRecord right(current.depth+1);
- std::unique_ptr<mvector<PrimRefMB>> new_vector = split(children[bestChild],left,right,aligned,timesplit);
- children.split(bestChild,left,right,std::move(new_vector));
- } while (children.size() < cfg.branchingFactor);
- /* detect time_ranges that have shrunken */
- for (size_t i=0; i<children.size(); i++) {
- const BBox1f c = children[i].prims.time_range;
- const BBox1f p = current.prims.time_range;
- timesplit |= c.lower > p.lower || c.upper < p.upper;
- }
- /* create time split node */
- if (timesplit)
- {
- const NodeRef node = createAABBNodeMB(children.children.data(),children.numChildren,alloc,true);
- /* spawn tasks or ... */
- if (current.size() > SINGLE_THREADED_THRESHOLD)
- {
- parallel_for(size_t(0), children.size(), [&] (const range<size_t>& r) {
- for (size_t i=r.begin(); i<r.end(); i++) {
- values[i] = recurse(children[i],nullptr,true);
- _mm_mfence(); // to allow non-temporal stores during build
- }
- });
- }
- /* ... continue sequential */
- else {
- for (size_t i=0; i<children.size(); i++) {
- values[i] = recurse(children[i],alloc,false);
- }
- }
- setAABBNodeMB(current,children.children.data(),node,values,children.numChildren);
- const LBBox3fa bounds = current.prims.linearBounds(recalculatePrimRef);
- return NodeRecordMB4D(node,bounds,current.prims.time_range);
- }
- /* create aligned node */
- else if (aligned)
- {
- const NodeRef node = createAABBNodeMB(children.children.data(),children.numChildren,alloc,true);
- /* spawn tasks or ... */
- if (current.size() > SINGLE_THREADED_THRESHOLD)
- {
- LBBox3fa cbounds[MAX_BRANCHING_FACTOR];
- parallel_for(size_t(0), children.size(), [&] (const range<size_t>& r) {
- for (size_t i=r.begin(); i<r.end(); i++) {
- values[i] = recurse(children[i],nullptr,true);
- cbounds[i] = values[i].lbounds;
- _mm_mfence(); // to allow non-temporal stores during build
- }
- });
- LBBox3fa bounds = empty;
- for (size_t i=0; i<children.size(); i++)
- bounds.extend(cbounds[i]);
- setAABBNodeMB(current,children.children.data(),node,values,children.numChildren);
- return NodeRecordMB4D(node,bounds,current.prims.time_range);
- }
- /* ... continue sequentially */
- else
- {
- LBBox3fa bounds = empty;
- for (size_t i=0; i<children.size(); i++) {
- values[i] = recurse(children[i],alloc,false);
- bounds.extend(values[i].lbounds);
- }
- setAABBNodeMB(current,children.children.data(),node,values,children.numChildren);
- return NodeRecordMB4D(node,bounds,current.prims.time_range);
- }
- }
- /* create unaligned node */
- else
- {
- const NodeRef node = createOBBNodeMB(alloc);
- /* spawn tasks or ... */
- if (current.size() > SINGLE_THREADED_THRESHOLD)
- {
- parallel_for(size_t(0), children.size(), [&] (const range<size_t>& r) {
- for (size_t i=r.begin(); i<r.end(); i++) {
- const LinearSpace3fa space = unalignedHeuristic.computeAlignedSpaceMB(scene,children[i].prims);
- const LBBox3fa lbounds = children[i].prims.linearBounds(recalculatePrimRef,space);
- const auto child = recurse(children[i],nullptr,true);
- setOBBNodeMB(node,i,child.ref,space,lbounds,children[i].prims.time_range);
- _mm_mfence(); // to allow non-temporal stores during build
- }
- });
- }
- /* ... continue sequentially */
- else
- {
- for (size_t i=0; i<children.size(); i++) {
- const LinearSpace3fa space = unalignedHeuristic.computeAlignedSpaceMB(scene,children[i].prims);
- const LBBox3fa lbounds = children[i].prims.linearBounds(recalculatePrimRef,space);
- const auto child = recurse(children[i],alloc,false);
- setOBBNodeMB(node,i,child.ref,space,lbounds,children[i].prims.time_range);
- }
- }
- const LBBox3fa bounds = current.prims.linearBounds(recalculatePrimRef);
- return NodeRecordMB4D(node,bounds,current.prims.time_range);
- }
- }
- public:
- /*! entry point into builder */
- NodeRecordMB4D operator() (mvector<PrimRefMB>& prims, const PrimInfoMB& pinfo)
- {
- BuildRecord record(SetMB(pinfo,&prims),1);
- auto root = recurse(record,nullptr,true);
- _mm_mfence(); // to allow non-temporal stores during build
- return root;
- }
- private:
- Settings cfg;
- Scene* scene;
- const RecalculatePrimRef& recalculatePrimRef;
- const CreateAllocFunc& createAlloc;
- const CreateAABBNodeMBFunc& createAABBNodeMB;
- const SetAABBNodeMBFunc& setAABBNodeMB;
- const CreateOBBNodeMBFunc& createOBBNodeMB;
- const SetOBBNodeMBFunc& setOBBNodeMB;
- const CreateLeafFunc& createLeaf;
- const ProgressMonitor& progressMonitor;
- private:
- HeuristicBinning alignedHeuristic;
- UnalignedHeuristicBinning unalignedHeuristic;
- HeuristicTemporal temporalSplitHeuristic;
- };
- template<typename NodeRef,
- typename RecalculatePrimRef,
- typename CreateAllocFunc,
- typename CreateAABBNodeMBFunc,
- typename SetAABBNodeMBFunc,
- typename CreateOBBNodeMBFunc,
- typename SetOBBNodeMBFunc,
- typename CreateLeafFunc,
- typename ProgressMonitor>
- static BVHNodeRecordMB4D<NodeRef> build (Scene* scene, mvector<PrimRefMB>& prims, const PrimInfoMB& pinfo,
- const RecalculatePrimRef& recalculatePrimRef,
- const CreateAllocFunc& createAlloc,
- const CreateAABBNodeMBFunc& createAABBNodeMB,
- const SetAABBNodeMBFunc& setAABBNodeMB,
- const CreateOBBNodeMBFunc& createOBBNodeMB,
- const SetOBBNodeMBFunc& setOBBNodeMB,
- const CreateLeafFunc& createLeaf,
- const ProgressMonitor& progressMonitor,
- const Settings settings)
- {
- typedef BuilderT<NodeRef,RecalculatePrimRef,CreateAllocFunc,
- CreateAABBNodeMBFunc,SetAABBNodeMBFunc,
- CreateOBBNodeMBFunc,SetOBBNodeMBFunc,
- CreateLeafFunc,ProgressMonitor> Builder;
- Builder builder(scene,recalculatePrimRef,createAlloc,
- createAABBNodeMB,setAABBNodeMB,
- createOBBNodeMB,setOBBNodeMB,
- createLeaf,progressMonitor,settings);
- return builder(prims,pinfo);
- }
- };
- }
- }
|