// Copyright 2009-2021 Intel Corporation // SPDX-License-Identifier: Apache-2.0 #pragma once #include "../builders/primrefgen.h" #include "../builders/heuristic_spatial.h" #include "../builders/splitter.h" #include "../../common/algorithms/parallel_for_for.h" #include "../../common/algorithms/parallel_for_for_prefix_sum.h" #define DBG_PRESPLIT(x) #define CHECK_PRESPLIT(x) #define GRID_SIZE 1024 #define MAX_PRESPLITS_PER_PRIMITIVE_LOG 5 #define MAX_PRESPLITS_PER_PRIMITIVE (1<(priority); } __forceinline bool operator < (const PresplitItem& item) const { return (priority < item.priority); } template __forceinline static float compute_priority(const PrimRef &ref, Scene *scene, const Vec2i &mc) { const unsigned int geomID = ref.geomID(); const unsigned int primID = ref.primID(); const float area_aabb = area(ref.bounds()); const float area_prim = ((Mesh*)scene->get(geomID))->projectedPrimitiveArea(primID); const unsigned int diff = 31 - lzcnt(mc.x^mc.y); assert(area_prim <= area_aabb); //const float priority = powf((area_aabb - area_prim) * powf(PRIORITY_SPLIT_POS_WEIGHT,(float)diff),1.0f/4.0f); const float priority = sqrtf(sqrtf( (area_aabb - area_prim) * powf(PRIORITY_SPLIT_POS_WEIGHT,(float)diff) )); assert(priority >= 0.0f && priority < FLT_LARGE); return priority; } }; inline std::ostream &operator<<(std::ostream &cout, const PresplitItem& item) { return cout << "index " << item.index << " priority " << item.priority; }; template void splitPrimitive(SplitterFactory &Splitter, const PrimRef &prim, const unsigned int geomID, const unsigned int primID, const unsigned int split_level, const Vec3fa &grid_base, const float grid_scale, const float grid_extend, PrimRef subPrims[MAX_PRESPLITS_PER_PRIMITIVE], unsigned int& numSubPrims) { assert(split_level <= MAX_PRESPLITS_PER_PRIMITIVE_LOG); if (split_level == 0) { assert(numSubPrims < MAX_PRESPLITS_PER_PRIMITIVE); subPrims[numSubPrims++] = prim; } else { const Vec3fa lower = prim.lower; const Vec3fa upper = prim.upper; const Vec3fa glower = (lower-grid_base)*Vec3fa(grid_scale)+Vec3fa(0.2f); const Vec3fa gupper = (upper-grid_base)*Vec3fa(grid_scale)-Vec3fa(0.2f); Vec3ia ilower(floor(glower)); Vec3ia iupper(floor(gupper)); /* this ignores dimensions that are empty */ iupper = (Vec3ia)(select(vint4(glower) >= vint4(gupper),vint4(ilower),vint4(iupper))); /* compute a morton code for the lower and upper grid coordinates. */ const unsigned int lower_code = bitInterleave(ilower.x,ilower.y,ilower.z); const unsigned int upper_code = bitInterleave(iupper.x,iupper.y,iupper.z); /* if all bits are equal then we cannot split */ if(unlikely(lower_code == upper_code)) { assert(numSubPrims < MAX_PRESPLITS_PER_PRIMITIVE); subPrims[numSubPrims++] = prim; return; } /* compute octree level and dimension to perform the split in */ const unsigned int diff = 31 - lzcnt(lower_code^upper_code); const unsigned int level = diff / 3; const unsigned int dim = diff % 3; /* now we compute the grid position of the split */ const unsigned int isplit = iupper[dim] & ~((1<= fsplit); /* split primitive */ const auto splitter = Splitter(prim); BBox3fa left,right; splitter(prim.bounds(),dim,fsplit,left,right); assert(!left.empty()); assert(!right.empty()); splitPrimitive(Splitter,PrimRef(left ,geomID,primID),geomID,primID,split_level-1,grid_base,grid_scale,grid_extend,subPrims,numSubPrims); splitPrimitive(Splitter,PrimRef(right,geomID,primID),geomID,primID,split_level-1,grid_base,grid_scale,grid_extend,subPrims,numSubPrims); } } template PrimInfo createPrimRefArray_presplit(Geometry* geometry, unsigned int geomID, size_t numPrimRefs, mvector& prims, BuildProgressMonitor& progressMonitor) { ParallelPrefixSumState pstate; /* first try */ progressMonitor(0); PrimInfo pinfo = parallel_prefix_sum( pstate, size_t(0), geometry->size(), size_t(1024), PrimInfo(empty), [&](const range& r, const PrimInfo& base) -> PrimInfo { return geometry->createPrimRefArray(prims,r,r.begin(),geomID); }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); }); /* if we need to filter out geometry, run again */ if (pinfo.size() != numPrimRefs) { progressMonitor(0); pinfo = parallel_prefix_sum( pstate, size_t(0), geometry->size(), size_t(1024), PrimInfo(empty), [&](const range& r, const PrimInfo& base) -> PrimInfo { return geometry->createPrimRefArray(prims,r,base.size(),geomID); }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); }); } return pinfo; } __forceinline Vec2i computeMC(const Vec3fa &grid_base, const float grid_scale, const PrimRef &ref) { const Vec3fa lower = ref.lower; const Vec3fa upper = ref.upper; const Vec3fa glower = (lower-grid_base)*Vec3fa(grid_scale)+Vec3fa(0.2f); const Vec3fa gupper = (upper-grid_base)*Vec3fa(grid_scale)-Vec3fa(0.2f); Vec3ia ilower(floor(glower)); Vec3ia iupper(floor(gupper)); /* this ignores dimensions that are empty */ iupper = (Vec3ia)select(vint4(glower) >= vint4(gupper),vint4(ilower),vint4(iupper)); /* compute a morton code for the lower and upper grid coordinates. */ const unsigned int lower_code = bitInterleave(ilower.x,ilower.y,ilower.z); const unsigned int upper_code = bitInterleave(iupper.x,iupper.y,iupper.z); return Vec2i(lower_code,upper_code); } template PrimInfo createPrimRefArray_presplit(Scene* scene, Geometry::GTypeMask types, bool mblur, size_t numPrimRefs, mvector& prims, BuildProgressMonitor& progressMonitor) { static const size_t MIN_STEP_SIZE = 128; ParallelForForPrefixSumState pstate; Scene::Iterator2 iter(scene,types,mblur); /* first try */ progressMonitor(0); pstate.init(iter,size_t(1024)); PrimInfo pinfo = parallel_for_for_prefix_sum0( pstate, iter, PrimInfo(empty), [&](Geometry* mesh, const range& r, size_t k, size_t geomID) -> PrimInfo { return mesh->createPrimRefArray(prims,r,k,(unsigned)geomID); }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); }); /* if we need to filter out geometry, run again */ if (pinfo.size() != numPrimRefs) { progressMonitor(0); pinfo = parallel_for_for_prefix_sum1( pstate, iter, PrimInfo(empty), [&](Geometry* mesh, const range& r, size_t k, size_t geomID, const PrimInfo& base) -> PrimInfo { return mesh->createPrimRefArray(prims,r,base.size(),(unsigned)geomID); }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); }); } /* use correct number of primitives */ size_t numPrimitives = pinfo.size(); const size_t alloc_numPrimitives = prims.size(); const size_t numSplitPrimitivesBudget = alloc_numPrimitives - numPrimitives; /* set up primitive splitter */ SplitterFactory Splitter(scene); DBG_PRESPLIT( const size_t org_numPrimitives = pinfo.size(); PRINT(numPrimitives); PRINT(alloc_numPrimitives); PRINT(numSplitPrimitivesBudget); ); /* allocate double buffer presplit items */ const size_t presplit_allocation_size = sizeof(PresplitItem)*alloc_numPrimitives; PresplitItem *presplitItem = (PresplitItem*)alignedMalloc(presplit_allocation_size,64); PresplitItem *tmp_presplitItem = (PresplitItem*)alignedMalloc(presplit_allocation_size,64); /* compute grid */ const Vec3fa grid_base = pinfo.geomBounds.lower; const Vec3fa grid_diag = pinfo.geomBounds.size(); const float grid_extend = max(grid_diag.x,max(grid_diag.y,grid_diag.z)); const float grid_scale = grid_extend == 0.0f ? 0.0f : GRID_SIZE / grid_extend; /* init presplit items and get total sum */ const float psum = parallel_reduce( size_t(0), numPrimitives, size_t(MIN_STEP_SIZE), 0.0f, [&](const range& r) -> float { float sum = 0.0f; for (size_t i=r.begin(); i(prims[i],scene,mc) : 0.0f; /* FIXME: sum undeterministic */ sum += presplitItem[i].priority; } return sum; },[](const float& a, const float& b) -> float { return a+b; }); /* compute number of splits per primitive */ const float inv_psum = 1.0f / psum; parallel_for( size_t(0), numPrimitives, size_t(MIN_STEP_SIZE), [&](const range& r) -> void { for (size_t i=r.begin(); i 0.0f) { const float rel_p = (float)numSplitPrimitivesBudget * presplitItem[i].priority * inv_psum; if (rel_p >= PRIORITY_CUTOFF_THRESHOLD) // need at least a split budget that generates two sub-prims { presplitItem[i].priority = max(min(ceilf(logf(rel_p)/logf(2.0f)),(float)MAX_PRESPLITS_PER_PRIMITIVE_LOG),1.0f); //presplitItem[i].priority = min(floorf(logf(rel_p)/logf(2.0f)),(float)MAX_PRESPLITS_PER_PRIMITIVE_LOG); assert(presplitItem[i].priority >= 0.0f && presplitItem[i].priority <= (float)MAX_PRESPLITS_PER_PRIMITIVE_LOG); } else presplitItem[i].priority = 0.0f; } } }); auto isLeft = [&] (const PresplitItem &ref) { return ref.priority < PRIORITY_CUTOFF_THRESHOLD; }; size_t center = parallel_partitioning(presplitItem,0,numPrimitives,isLeft,1024); /* anything to split ? */ if (center < numPrimitives) { size_t numPrimitivesToSplit = numPrimitives - center; assert(presplitItem[center].priority >= 1.0f); /* sort presplit items in ascending order */ radix_sort_u32(presplitItem + center,tmp_presplitItem + center,numPrimitivesToSplit,1024); CHECK_PRESPLIT( parallel_for( size_t(center+1), numPrimitives, size_t(MIN_STEP_SIZE), [&](const range& r) -> void { for (size_t i=r.begin(); i& t) -> size_t { size_t sum = 0; for (size_t i=t.begin(); i= 1.0f); const unsigned int primrefID = presplitItem[i].index; const float prio = presplitItem[i].priority; const unsigned int geomID = prims[primrefID].geomID(); const unsigned int primID = prims[primrefID].primID(); const unsigned int split_levels = (unsigned int)prio; unsigned int numSubPrims = 0; splitPrimitive(Splitter,prims[primrefID],geomID,primID,split_levels,grid_base,grid_scale,grid_extend,subPrims,numSubPrims); assert(numSubPrims); numSubPrims--; // can reuse slot sum+=numSubPrims; presplitItem[i].data = (numSubPrims << MAX_PRESPLITS_PER_PRIMITIVE_LOG) | split_levels; primOffset0[i-center] = numSubPrims; } return sum; },[](const size_t& a, const size_t& b) -> size_t { return a+b; }); /* if we are over budget, need to shrink the range */ if (totalNumSubPrims > numSplitPrimitivesBudget) { size_t new_center = numPrimitives-1; size_t sum = 0; for (;new_center>=center;new_center--) { const unsigned int numSubPrims = presplitItem[new_center].data >> MAX_PRESPLITS_PER_PRIMITIVE_LOG; if (unlikely(sum + numSubPrims >= numSplitPrimitivesBudget)) break; sum += numSubPrims; } new_center++; primOffset0 += new_center - center; numPrimitivesToSplit -= new_center - center; center = new_center; assert(numPrimitivesToSplit == (numPrimitives - center)); } /* parallel prefix sum to compute offsets for storing sub-primitives */ const unsigned int offset = parallel_prefix_sum(primOffset0,primOffset1,numPrimitivesToSplit,(unsigned int)0,std::plus()); assert(numPrimitives+offset <= alloc_numPrimitives); /* iterate over range, and split primitives into sub primitives and append them to prims array */ parallel_for( size_t(center), numPrimitives, size_t(MIN_STEP_SIZE), [&](const range& rn) -> void { for (size_t j=rn.begin(); j& r) -> PrimInfo { PrimInfo p(empty); for (size_t j=r.begin(); j PrimInfo { return PrimInfo::merge(a,b); }); assert(pinfo.size() == numPrimitives); /* free double buffer presplit items */ alignedFree(tmp_presplitItem); alignedFree(presplitItem); return pinfo; } } }