Path: blob/master/thirdparty/embree/kernels/builders/bvh_builder_msmblur.h
9912 views
// Copyright 2009-2021 Intel Corporation1// SPDX-License-Identifier: Apache-2.023#pragma once45#define MBLUR_NUM_TEMPORAL_BINS 26#define MBLUR_NUM_OBJECT_BINS 3278#include "../bvh/bvh.h"9#include "../builders/primref_mb.h"10#include "heuristic_binning_array_aligned.h"11#include "heuristic_timesplit_array.h"1213namespace embree14{15namespace isa16{17template<typename T>18struct SharedVector19{20__forceinline SharedVector() {}2122__forceinline SharedVector(T* ptr, size_t refCount = 1)23: prims(ptr), refCount(refCount) {}2425__forceinline void incRef() {26refCount++;27}2829__forceinline void decRef()30{31if (--refCount == 0)32delete prims;33}3435T* prims;36size_t refCount;37};3839template<typename BuildRecord, int MAX_BRANCHING_FACTOR>40struct LocalChildListT41{42typedef SharedVector<mvector<PrimRefMB>> SharedPrimRefVector;4344__forceinline LocalChildListT (const BuildRecord& record)45: numChildren(1), numSharedPrimVecs(1)46{47/* the local root will be freed in the ancestor where it was created (thus refCount is 2) */48children[0] = record;49primvecs[0] = new (&sharedPrimVecs[0]) SharedPrimRefVector(record.prims.prims, 2);50}5152__forceinline ~LocalChildListT()53{54for (size_t i = 0; i < numChildren; i++)55primvecs[i]->decRef();56}5758__forceinline BuildRecord& operator[] ( const size_t i ) {59return children[i];60}6162__forceinline size_t size() const {63return numChildren;64}6566__forceinline void split(ssize_t bestChild, const BuildRecord& lrecord, const BuildRecord& rrecord, std::unique_ptr<mvector<PrimRefMB>> new_vector)67{68SharedPrimRefVector* bsharedPrimVec = primvecs[bestChild];69if (lrecord.prims.prims == bsharedPrimVec->prims) {70primvecs[bestChild] = bsharedPrimVec;71bsharedPrimVec->incRef();72}73else {74primvecs[bestChild] = new (&sharedPrimVecs[numSharedPrimVecs++]) SharedPrimRefVector(lrecord.prims.prims);75}7677if (rrecord.prims.prims == bsharedPrimVec->prims) {78primvecs[numChildren] = bsharedPrimVec;79bsharedPrimVec->incRef();80}81else {82primvecs[numChildren] = new (&sharedPrimVecs[numSharedPrimVecs++]) SharedPrimRefVector(rrecord.prims.prims);83}84bsharedPrimVec->decRef();85new_vector.release();8687children[bestChild] = lrecord;88children[numChildren] = rrecord;89numChildren++;90}9192public:93array_t<BuildRecord,MAX_BRANCHING_FACTOR> children;94array_t<SharedPrimRefVector*,MAX_BRANCHING_FACTOR> primvecs;95size_t numChildren;9697array_t<SharedPrimRefVector,2*MAX_BRANCHING_FACTOR> sharedPrimVecs;98size_t numSharedPrimVecs;99};100101template<typename Mesh>102struct RecalculatePrimRef103{104Scene* scene;105106__forceinline RecalculatePrimRef (Scene* scene)107: scene(scene) {}108109__forceinline PrimRefMB operator() (const PrimRefMB& prim, const BBox1f time_range) const110{111const unsigned geomID = prim.geomID();112const unsigned primID = prim.primID();113const Mesh* mesh = scene->get<Mesh>(geomID);114const LBBox3fa lbounds = mesh->linearBounds(primID, time_range);115const range<int> tbounds = mesh->timeSegmentRange(time_range);116return PrimRefMB (lbounds, tbounds.size(), mesh->time_range, mesh->numTimeSegments(), geomID, primID);117}118119// __noinline is workaround for ICC16 bug under MacOSX120__noinline PrimRefMB operator() (const PrimRefMB& prim, const BBox1f time_range, const LinearSpace3fa& space) const121{122const unsigned geomID = prim.geomID();123const unsigned primID = prim.primID();124const Mesh* mesh = scene->get<Mesh>(geomID);125const LBBox3fa lbounds = mesh->linearBounds(space, primID, time_range);126const range<int> tbounds = mesh->timeSegmentRange(time_range);127return PrimRefMB (lbounds, tbounds.size(), mesh->time_range, mesh->numTimeSegments(), geomID, primID);128}129130__forceinline LBBox3fa linearBounds(const PrimRefMB& prim, const BBox1f time_range) const {131return scene->get<Mesh>(prim.geomID())->linearBounds(prim.primID(), time_range);132}133134// __noinline is workaround for ICC16 bug under MacOSX135__noinline LBBox3fa linearBounds(const PrimRefMB& prim, const BBox1f time_range, const LinearSpace3fa& space) const {136return scene->get<Mesh>(prim.geomID())->linearBounds(space, prim.primID(), time_range);137}138};139140struct VirtualRecalculatePrimRef141{142Scene* scene;143const SubGridBuildData * const sgrids;144145__forceinline VirtualRecalculatePrimRef (Scene* scene, const SubGridBuildData * const sgrids = nullptr)146: scene(scene), sgrids(sgrids) {}147148__forceinline PrimRefMB operator() (const PrimRefMB& prim, const BBox1f time_range) const149{150const unsigned geomID = prim.geomID();151const unsigned primID = prim.primID();152const Geometry* mesh = scene->get(geomID);153const LBBox3fa lbounds = mesh->vlinearBounds(primID, time_range, sgrids);154const range<int> tbounds = mesh->timeSegmentRange(time_range);155return PrimRefMB (lbounds, tbounds.size(), mesh->time_range, mesh->numTimeSegments(), geomID, primID);156}157158__forceinline PrimRefMB operator() (const PrimRefMB& prim, const BBox1f time_range, const LinearSpace3fa& space) const159{160const unsigned geomID = prim.geomID();161const unsigned primID = prim.primID();162const Geometry* mesh = scene->get(geomID);163const LBBox3fa lbounds = mesh->vlinearBounds(space, primID, time_range);164const range<int> tbounds = mesh->timeSegmentRange(time_range);165return PrimRefMB (lbounds, tbounds.size(), mesh->time_range, mesh->numTimeSegments(), geomID, primID);166}167168__forceinline LBBox3fa linearBounds(const PrimRefMB& prim, const BBox1f time_range) const {169return scene->get(prim.geomID())->vlinearBounds(prim.primID(), time_range, sgrids);170}171172__forceinline LBBox3fa linearBounds(const PrimRefMB& prim, const BBox1f time_range, const LinearSpace3fa& space) const {173return scene->get(prim.geomID())->vlinearBounds(space, prim.primID(), time_range);174}175};176177struct BVHBuilderMSMBlur178{179/*! settings for msmblur builder */180struct Settings181{182/*! default settings */183Settings ()184: branchingFactor(2), maxDepth(32), logBlockSize(0), minLeafSize(1), maxLeafSize(8),185travCost(1.0f), intCost(1.0f), singleLeafTimeSegment(false),186singleThreadThreshold(1024) {}187188189Settings (size_t sahBlockSize, size_t minLeafSize, size_t maxLeafSize, float travCost, float intCost, size_t singleThreadThreshold)190: branchingFactor(2), maxDepth(32), logBlockSize(bsr(sahBlockSize)), minLeafSize(minLeafSize), maxLeafSize(maxLeafSize),191travCost(travCost), intCost(intCost), singleThreadThreshold(singleThreadThreshold)192{193minLeafSize = min(minLeafSize,maxLeafSize);194}195196public:197size_t branchingFactor; //!< branching factor of BVH to build198size_t maxDepth; //!< maximum depth of BVH to build199size_t logBlockSize; //!< log2 of blocksize for SAH heuristic200size_t minLeafSize; //!< minimum size of a leaf201size_t maxLeafSize; //!< maximum size of a leaf202float travCost; //!< estimated cost of one traversal step203float intCost; //!< estimated cost of one primitive intersection204bool singleLeafTimeSegment; //!< split time to single time range205size_t singleThreadThreshold; //!< threshold when we switch to single threaded build206};207208struct BuildRecord209{210public:211__forceinline BuildRecord () {}212213__forceinline BuildRecord (size_t depth)214: depth(depth) {}215216__forceinline BuildRecord (const SetMB& prims, size_t depth)217: depth(depth), prims(prims) {}218219__forceinline friend bool operator< (const BuildRecord& a, const BuildRecord& b) {220return a.prims.size() < b.prims.size();221}222223__forceinline size_t size() const {224return prims.size();225}226227public:228size_t depth; //!< Depth of the root of this subtree.229SetMB prims; //!< The list of primitives.230};231232struct BuildRecordSplit : public BuildRecord233{234__forceinline BuildRecordSplit () {}235236__forceinline BuildRecordSplit (size_t depth)237: BuildRecord(depth) {}238239__forceinline BuildRecordSplit (const BuildRecord& record, const BinSplit<MBLUR_NUM_OBJECT_BINS>& split)240: BuildRecord(record), split(split) {}241242BinSplit<MBLUR_NUM_OBJECT_BINS> split;243};244245template<246typename NodeRef,247typename RecalculatePrimRef,248typename Allocator,249typename CreateAllocFunc,250typename CreateNodeFunc,251typename SetNodeFunc,252typename CreateLeafFunc,253typename ProgressMonitor>254255class BuilderT256{257ALIGNED_CLASS_(16);258static const size_t MAX_BRANCHING_FACTOR = 16; //!< maximum supported BVH branching factor259static const size_t MIN_LARGE_LEAF_LEVELS = 8; //!< create balanced tree if we are that many levels before the maximum tree depth260261typedef BVHNodeRecordMB4D<NodeRef> NodeRecordMB4D;262typedef BinSplit<MBLUR_NUM_OBJECT_BINS> Split;263typedef mvector<PrimRefMB>* PrimRefVector;264typedef SharedVector<mvector<PrimRefMB>> SharedPrimRefVector;265typedef LocalChildListT<BuildRecord,MAX_BRANCHING_FACTOR> LocalChildList;266typedef LocalChildListT<BuildRecordSplit,MAX_BRANCHING_FACTOR> LocalChildListSplit;267268public:269270BuilderT (MemoryMonitorInterface* device,271const RecalculatePrimRef recalculatePrimRef,272const CreateAllocFunc createAlloc,273const CreateNodeFunc createNode,274const SetNodeFunc setNode,275const CreateLeafFunc createLeaf,276const ProgressMonitor progressMonitor,277const Settings& settings)278: cfg(settings),279heuristicObjectSplit(),280heuristicTemporalSplit(device, recalculatePrimRef),281recalculatePrimRef(recalculatePrimRef), createAlloc(createAlloc), createNode(createNode), setNode(setNode), createLeaf(createLeaf),282progressMonitor(progressMonitor)283{284if (cfg.branchingFactor > MAX_BRANCHING_FACTOR)285throw_RTCError(RTC_ERROR_UNKNOWN,"bvh_builder: branching factor too large");286}287288/*! finds the best split */289const Split find(const SetMB& set)290{291/* first try standard object split */292const Split object_split = heuristicObjectSplit.find(set,cfg.logBlockSize);293const float object_split_sah = object_split.splitSAH();294295/* test temporal splits only when object split was bad */296const float leaf_sah = set.leafSAH(cfg.logBlockSize);297if (object_split_sah < 0.50f*leaf_sah)298return object_split;299300/* do temporal splits only if the time range is big enough */301if (set.time_range.size() > 1.01f/float(set.max_num_time_segments))302{303const Split temporal_split = heuristicTemporalSplit.find(set,cfg.logBlockSize);304const float temporal_split_sah = temporal_split.splitSAH();305306/* take temporal split if it improved SAH */307if (temporal_split_sah < object_split_sah)308return temporal_split;309}310311return object_split;312}313314/*! array partitioning */315__forceinline std::unique_ptr<mvector<PrimRefMB>> split(const Split& split, const SetMB& set, SetMB& lset, SetMB& rset)316{317/* perform object split */318if (likely(split.data == Split::SPLIT_OBJECT)) {319heuristicObjectSplit.split(split,set,lset,rset);320}321/* perform temporal split */322else if (likely(split.data == Split::SPLIT_TEMPORAL)) {323return heuristicTemporalSplit.split(split,set,lset,rset);324}325/* perform fallback split */326else if (unlikely(split.data == Split::SPLIT_FALLBACK)) {327set.deterministic_order();328splitFallback(set,lset,rset);329}330/* split by geometry */331else if (unlikely(split.data == Split::SPLIT_GEOMID)) {332set.deterministic_order();333splitByGeometry(set,lset,rset);334}335else336assert(false);337338return std::unique_ptr<mvector<PrimRefMB>>();339}340341/*! finds the best fallback split */342__noinline Split findFallback(const SetMB& set)343{344/* split if primitives are not from same geometry */345if (!sameGeometry(set))346return Split(0.0f,Split::SPLIT_GEOMID);347348/* if a leaf can only hold a single time-segment, we might have to do additional temporal splits */349if (cfg.singleLeafTimeSegment)350{351/* test if one primitive has more than one time segment in time range, if so split time */352for (size_t i=set.begin(); i<set.end(); i++)353{354const PrimRefMB& prim = (*set.prims)[i];355const range<int> itime_range = prim.timeSegmentRange(set.time_range);356const int localTimeSegments = itime_range.size();357assert(localTimeSegments > 0);358if (localTimeSegments > 1) {359const int icenter = (itime_range.begin() + itime_range.end())/2;360const float splitTime = prim.timeStep(icenter);361return Split(0.0f,(unsigned)Split::SPLIT_TEMPORAL,0,splitTime);362}363}364}365366/* otherwise return fallback split */367return Split(0.0f,Split::SPLIT_FALLBACK);368}369370/*! performs fallback split */371void splitFallback(const SetMB& set, SetMB& lset, SetMB& rset)372{373mvector<PrimRefMB>& prims = *set.prims;374375const size_t begin = set.begin();376const size_t end = set.end();377const size_t center = (begin + end + 1) / 2;378379PrimInfoMB linfo = empty;380for (size_t i=begin; i<center; i++)381linfo.add_primref(prims[i]);382383PrimInfoMB rinfo = empty;384for (size_t i=center; i<end; i++)385rinfo.add_primref(prims[i]);386387new (&lset) SetMB(linfo,set.prims,range<size_t>(begin,center),set.time_range);388new (&rset) SetMB(rinfo,set.prims,range<size_t>(center,end ),set.time_range);389}390391/*! checks if all primitives are from the same geometry */392__forceinline bool sameGeometry(const SetMB& set)393{394if (set.size() == 0) return true;395mvector<PrimRefMB>& prims = *set.prims;396const size_t begin = set.begin();397const size_t end = set.end();398unsigned int firstGeomID = prims[begin].geomID();399for (size_t i=begin+1; i<end; i++) {400if (prims[i].geomID() != firstGeomID){401return false;402}403}404return true;405}406407/* split by geometry ID */408void splitByGeometry(const SetMB& set, SetMB& lset, SetMB& rset)409{410assert(set.size() > 1);411412mvector<PrimRefMB>& prims = *set.prims;413const size_t begin = set.begin();414const size_t end = set.end();415416PrimInfoMB left(empty);417PrimInfoMB right(empty);418unsigned int geomID = prims[begin].geomID();419size_t center = serial_partitioning(prims.data(),begin,end,left,right,420[&] ( const PrimRefMB& prim ) { return prim.geomID() == geomID; },421[ ] ( PrimInfoMB& dst, const PrimRefMB& prim ) { dst.add_primref(prim); });422423new (&lset) SetMB(left, set.prims,range<size_t>(begin,center),set.time_range);424new (&rset) SetMB(right,set.prims,range<size_t>(center,end ),set.time_range);425}426427const NodeRecordMB4D createLargeLeaf(const BuildRecord& in, Allocator alloc)428{429/* this should never occur but is a fatal error */430if (in.depth > cfg.maxDepth)431throw_RTCError(RTC_ERROR_UNKNOWN,"depth limit reached");432433/* replace already found split by fallback split */434const BuildRecordSplit current(BuildRecord(in.prims,in.depth),findFallback(in.prims));435436/* special case when directly creating leaf without any splits that could shrink time_range */437bool force_split = false;438if (current.depth == 1 && current.size() > 0)439{440BBox1f c = empty;441BBox1f p = current.prims.time_range;442for (size_t i=current.prims.begin(); i<current.prims.end(); i++) {443mvector<PrimRefMB>& prims = *current.prims.prims;444c.extend(prims[i].time_range);445}446447force_split = c.lower > p.lower || c.upper < p.upper;448}449450/* create leaf for few primitives */451if (current.size() <= cfg.maxLeafSize && current.split.data < Split::SPLIT_ENFORCE && !force_split)452return createLeaf(current,alloc);453454/* fill all children by always splitting the largest one */455bool hasTimeSplits = false;456NodeRecordMB4D values[MAX_BRANCHING_FACTOR];457LocalChildListSplit children(current);458459do {460/* find best child with largest bounding box area */461size_t bestChild = -1;462size_t bestSize = 0;463for (size_t i=0; i<children.size(); i++)464{465/* ignore leaves as they cannot get split */466if (children[i].size() <= cfg.maxLeafSize && children[i].split.data < Split::SPLIT_ENFORCE && !force_split)467continue;468469force_split = false;470471/* remember child with largest size */472if (children[i].size() > bestSize) {473bestSize = children[i].size();474bestChild = i;475}476}477if (bestChild == -1) break;478479/* perform best found split */480BuildRecordSplit& brecord = children[bestChild];481BuildRecordSplit lrecord(current.depth+1);482BuildRecordSplit rrecord(current.depth+1);483std::unique_ptr<mvector<PrimRefMB>> new_vector = split(brecord.split,brecord.prims,lrecord.prims,rrecord.prims);484hasTimeSplits |= new_vector != nullptr;485486/* find new splits */487lrecord.split = findFallback(lrecord.prims);488rrecord.split = findFallback(rrecord.prims);489children.split(bestChild,lrecord,rrecord,std::move(new_vector));490491} while (children.size() < cfg.branchingFactor);492493/* detect time_ranges that have shrunken */494for (size_t i=0; i<children.size(); i++) {495const BBox1f c = children[i].prims.time_range;496const BBox1f p = in.prims.time_range;497hasTimeSplits |= c.lower > p.lower || c.upper < p.upper;498}499500/* create node */501auto node = createNode(children.children.data(),children.numChildren,alloc,hasTimeSplits);502503/* recurse into each child and perform reduction */504LBBox3fa gbounds = empty;505for (size_t i=0; i<children.size(); i++) {506values[i] = createLargeLeaf(children[i],alloc);507gbounds.extend(values[i].lbounds);508}509510setNode(current,children.children.data(),node,values,children.numChildren);511512/* calculate geometry bounds of this node */513if (hasTimeSplits)514return NodeRecordMB4D(node,current.prims.linearBounds(recalculatePrimRef),current.prims.time_range);515else516return NodeRecordMB4D(node,gbounds,current.prims.time_range);517}518519const NodeRecordMB4D recurse(const BuildRecord& current, Allocator alloc, bool toplevel)520{521/* get thread local allocator */522if (!alloc)523alloc = createAlloc();524525/* call memory monitor function to signal progress */526if (toplevel && current.size() <= cfg.singleThreadThreshold)527progressMonitor(current.size());528529/*! find best split */530const Split csplit = find(current.prims);531532/*! compute leaf and split cost */533const float leafSAH = cfg.intCost*current.prims.leafSAH(cfg.logBlockSize);534const float splitSAH = cfg.travCost*current.prims.halfArea()+cfg.intCost*csplit.splitSAH();535assert((current.size() == 0) || ((leafSAH >= 0) && (splitSAH >= 0)));536537/*! create a leaf node when threshold reached or SAH tells us to stop */538if (current.size() <= cfg.minLeafSize || current.depth+MIN_LARGE_LEAF_LEVELS >= cfg.maxDepth || (current.size() <= cfg.maxLeafSize && leafSAH <= splitSAH)) {539current.prims.deterministic_order();540return createLargeLeaf(current,alloc);541}542543/*! perform initial split */544SetMB lprims,rprims;545std::unique_ptr<mvector<PrimRefMB>> new_vector = split(csplit,current.prims,lprims,rprims);546bool hasTimeSplits = new_vector != nullptr;547NodeRecordMB4D values[MAX_BRANCHING_FACTOR];548LocalChildList children(current);549{550BuildRecord lrecord(lprims,current.depth+1);551BuildRecord rrecord(rprims,current.depth+1);552children.split(0,lrecord,rrecord,std::move(new_vector));553}554555/*! split until node is full or SAH tells us to stop */556while (children.size() < cfg.branchingFactor)557{558/*! find best child to split */559float bestArea = neg_inf;560ssize_t bestChild = -1;561for (size_t i=0; i<children.size(); i++)562{563if (children[i].size() <= cfg.minLeafSize) continue;564if (expectedApproxHalfArea(children[i].prims.geomBounds) > bestArea) {565bestChild = i; bestArea = expectedApproxHalfArea(children[i].prims.geomBounds);566}567}568if (bestChild == -1) break;569570/* perform split */571BuildRecord& brecord = children[bestChild];572BuildRecord lrecord(current.depth+1);573BuildRecord rrecord(current.depth+1);574Split csplit = find(brecord.prims);575std::unique_ptr<mvector<PrimRefMB>> new_vector = split(csplit,brecord.prims,lrecord.prims,rrecord.prims);576hasTimeSplits |= new_vector != nullptr;577children.split(bestChild,lrecord,rrecord,std::move(new_vector));578}579580/* detect time_ranges that have shrunken */581for (size_t i=0; i<children.size(); i++) {582const BBox1f c = children[i].prims.time_range;583const BBox1f p = current.prims.time_range;584hasTimeSplits |= c.lower > p.lower || c.upper < p.upper;585}586587/* sort buildrecords for simpler shadow ray traversal */588//std::sort(&children[0],&children[children.size()],std::greater<BuildRecord>()); // FIXME: reduces traversal performance of bvh8.triangle4 (need to verified) !!589590/*! create an inner node */591auto node = createNode(children.children.data(), children.numChildren, alloc, hasTimeSplits);592LBBox3fa gbounds = empty;593594/* spawn tasks */595if (unlikely(current.size() > cfg.singleThreadThreshold))596{597/*! parallel_for is faster than spawning sub-tasks */598parallel_for(size_t(0), children.size(), [&] (const range<size_t>& r) {599for (size_t i=r.begin(); i<r.end(); i++) {600values[i] = recurse(children[i],nullptr,true);601_mm_mfence(); // to allow non-temporal stores during build602}603});604605/*! merge bounding boxes */606for (size_t i=0; i<children.size(); i++)607gbounds.extend(values[i].lbounds);608}609/* recurse into each child */610else611{612//for (size_t i=0; i<children.size(); i++)613for (ssize_t i=children.size()-1; i>=0; i--) {614values[i] = recurse(children[i],alloc,false);615gbounds.extend(values[i].lbounds);616}617}618619setNode(current,children.children.data(),node,values,children.numChildren);620621/* calculate geometry bounds of this node */622if (unlikely(hasTimeSplits))623return NodeRecordMB4D(node,current.prims.linearBounds(recalculatePrimRef),current.prims.time_range);624else625return NodeRecordMB4D(node,gbounds,current.prims.time_range);626}627628/*! builder entry function */629__forceinline const NodeRecordMB4D operator() (mvector<PrimRefMB>& prims, const PrimInfoMB& pinfo)630{631const SetMB set(pinfo,&prims);632auto ret = recurse(BuildRecord(set,1),nullptr,true);633_mm_mfence(); // to allow non-temporal stores during build634return ret;635}636637private:638Settings cfg;639HeuristicArrayBinningMB<PrimRefMB,MBLUR_NUM_OBJECT_BINS> heuristicObjectSplit;640HeuristicMBlurTemporalSplit<PrimRefMB,RecalculatePrimRef,MBLUR_NUM_TEMPORAL_BINS> heuristicTemporalSplit;641const RecalculatePrimRef recalculatePrimRef;642const CreateAllocFunc createAlloc;643const CreateNodeFunc createNode;644const SetNodeFunc setNode;645const CreateLeafFunc createLeaf;646const ProgressMonitor progressMonitor;647};648649template<typename NodeRef,650typename RecalculatePrimRef,651typename CreateAllocFunc,652typename CreateNodeFunc,653typename SetNodeFunc,654typename CreateLeafFunc,655typename ProgressMonitorFunc>656657static const BVHNodeRecordMB4D<NodeRef> build(mvector<PrimRefMB>& prims,658const PrimInfoMB& pinfo,659MemoryMonitorInterface* device,660const RecalculatePrimRef recalculatePrimRef,661const CreateAllocFunc createAlloc,662const CreateNodeFunc createNode,663const SetNodeFunc setNode,664const CreateLeafFunc createLeaf,665const ProgressMonitorFunc progressMonitor,666const Settings& settings)667{668typedef BuilderT<669NodeRef,670RecalculatePrimRef,671decltype(createAlloc()),672CreateAllocFunc,673CreateNodeFunc,674SetNodeFunc,675CreateLeafFunc,676ProgressMonitorFunc> Builder;677678Builder builder(device,679recalculatePrimRef,680createAlloc,681createNode,682setNode,683createLeaf,684progressMonitor,685settings);686687688return builder(prims,pinfo);689}690};691}692}693694695