primrefgen_presplit.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. // Copyright 2009-2021 Intel Corporation
  2. // SPDX-License-Identifier: Apache-2.0
  3. #pragma once
  4. #include "../../common/algorithms/parallel_reduce.h"
  5. #include "../../common/algorithms/parallel_sort.h"
  6. #include "../builders/heuristic_spatial.h"
  7. #include "../builders/splitter.h"
  8. #include "../../common/algorithms/parallel_partition.h"
  9. #include "../../common/algorithms/parallel_for_for.h"
  10. #include "../../common/algorithms/parallel_for_for_prefix_sum.h"
  11. #define DBG_PRESPLIT(x)
  12. #define CHECK_PRESPLIT(x)
  13. #define GRID_SIZE 1024
  14. //#define MAX_PRESPLITS_PER_PRIMITIVE_LOG 6
  15. #define MAX_PRESPLITS_PER_PRIMITIVE_LOG 5
  16. #define MAX_PRESPLITS_PER_PRIMITIVE (1<<MAX_PRESPLITS_PER_PRIMITIVE_LOG)
  17. //#define PRIORITY_CUTOFF_THRESHOLD 2.0f
  18. #define PRIORITY_SPLIT_POS_WEIGHT 1.5f
  19. namespace embree
  20. {
  21. namespace isa
  22. {
  23. struct SplittingGrid
  24. {
  25. __forceinline SplittingGrid(const BBox3fa& bounds)
  26. {
  27. base = bounds.lower;
  28. const Vec3fa diag = bounds.size();
  29. extend = max(diag.x,max(diag.y,diag.z));
  30. scale = extend == 0.0f ? 0.0f : GRID_SIZE / extend;
  31. }
  32. __forceinline bool split_pos(const PrimRef& prim, unsigned int& dim_o, float& fsplit_o) const
  33. {
  34. /* compute morton code */
  35. const Vec3fa lower = prim.lower;
  36. const Vec3fa upper = prim.upper;
  37. const Vec3fa glower = (lower-base)*Vec3fa(scale)+Vec3fa(0.2f);
  38. const Vec3fa gupper = (upper-base)*Vec3fa(scale)-Vec3fa(0.2f);
  39. Vec3ia ilower(floor(glower));
  40. Vec3ia iupper(floor(gupper));
  41. /* this ignores dimensions that are empty */
  42. iupper = (Vec3ia)select(vint4(glower) >= vint4(gupper),vint4(ilower),vint4(iupper));
  43. /* compute a morton code for the lower and upper grid coordinates. */
  44. const unsigned int lower_code = bitInterleave(ilower.x,ilower.y,ilower.z);
  45. const unsigned int upper_code = bitInterleave(iupper.x,iupper.y,iupper.z);
  46. /* if all bits are equal then we cannot split */
  47. if (unlikely(lower_code == upper_code))
  48. return false;
  49. /* compute octree level and dimension to perform the split in */
  50. const unsigned int diff = 31 - lzcnt(lower_code^upper_code);
  51. const unsigned int level = diff / 3;
  52. const unsigned int dim = diff % 3;
  53. /* now we compute the grid position of the split */
  54. const unsigned int isplit = iupper[dim] & ~((1<<level)-1);
  55. /* compute world space position of split */
  56. const float inv_grid_size = 1.0f / GRID_SIZE;
  57. const float fsplit = base[dim] + isplit * inv_grid_size * extend;
  58. assert(prim.lower[dim] <= fsplit && prim.upper[dim] >= fsplit);
  59. dim_o = dim;
  60. fsplit_o = fsplit;
  61. return true;
  62. }
  63. __forceinline Vec2i computeMC(const PrimRef& ref) const
  64. {
  65. const Vec3fa lower = ref.lower;
  66. const Vec3fa upper = ref.upper;
  67. const Vec3fa glower = (lower-base)*Vec3fa(scale)+Vec3fa(0.2f);
  68. const Vec3fa gupper = (upper-base)*Vec3fa(scale)-Vec3fa(0.2f);
  69. Vec3ia ilower(floor(glower));
  70. Vec3ia iupper(floor(gupper));
  71. /* this ignores dimensions that are empty */
  72. iupper = (Vec3ia)select(vint4(glower) >= vint4(gupper),vint4(ilower),vint4(iupper));
  73. /* compute a morton code for the lower and upper grid coordinates. */
  74. const unsigned int lower_code = bitInterleave(ilower.x,ilower.y,ilower.z);
  75. const unsigned int upper_code = bitInterleave(iupper.x,iupper.y,iupper.z);
  76. return Vec2i(lower_code,upper_code);
  77. }
  78. Vec3fa base;
  79. float scale;
  80. float extend;
  81. };
  82. struct PresplitItem
  83. {
  84. union {
  85. float priority;
  86. unsigned int data;
  87. };
  88. unsigned int index;
  89. __forceinline operator unsigned() const {
  90. return data;
  91. }
  92. template<typename ProjectedPrimitiveAreaFunc>
  93. __forceinline static float compute_priority(const ProjectedPrimitiveAreaFunc& primitiveArea, const PrimRef &ref, const Vec2i &mc)
  94. {
  95. const float area_aabb = area(ref.bounds());
  96. const float area_prim = primitiveArea(ref);
  97. if (area_prim == 0.0f) return 0.0f;
  98. const unsigned int diff = 31 - lzcnt(mc.x^mc.y);
  99. //assert(area_prim <= area_aabb); // may trigger due to numerical issues
  100. const float area_diff = max(0.0f, area_aabb - area_prim);
  101. //const float priority = powf(area_diff * powf(PRIORITY_SPLIT_POS_WEIGHT,(float)diff),1.0f/4.0f);
  102. const float priority = sqrtf(sqrtf( area_diff * powf(PRIORITY_SPLIT_POS_WEIGHT,(float)diff) ));
  103. //const float priority = sqrtf(sqrtf( area_diff ) );
  104. //const float priority = sqrtfarea_diff;
  105. //const float priority = area_diff; // 104 fps !!!!!!!!!!
  106. //const float priority = 0.2f*area_aabb + 0.8f*area_diff; // 104 fps
  107. //const float priority = area_aabb * max(area_aabb/area_prim,32.0f);
  108. //const float priority = area_prim;
  109. assert(priority >= 0.0f && priority < FLT_LARGE);
  110. return priority;
  111. }
  112. };
  113. inline std::ostream &operator<<(std::ostream &cout, const PresplitItem& item) {
  114. return cout << "index " << item.index << " priority " << item.priority;
  115. };
  116. #if 1
  117. template<typename Splitter>
  118. void splitPrimitive(const Splitter& splitter,
  119. const PrimRef& prim,
  120. const unsigned int splitprims,
  121. const SplittingGrid& grid,
  122. PrimRef subPrims[MAX_PRESPLITS_PER_PRIMITIVE],
  123. unsigned int& numSubPrims)
  124. {
  125. assert(splitprims > 0 && splitprims <= MAX_PRESPLITS_PER_PRIMITIVE);
  126. if (splitprims == 1)
  127. {
  128. assert(numSubPrims < MAX_PRESPLITS_PER_PRIMITIVE);
  129. subPrims[numSubPrims++] = prim;
  130. }
  131. else
  132. {
  133. unsigned int dim; float fsplit;
  134. if (!grid.split_pos(prim, dim, fsplit))
  135. {
  136. assert(numSubPrims < MAX_PRESPLITS_PER_PRIMITIVE);
  137. subPrims[numSubPrims++] = prim;
  138. return;
  139. }
  140. /* split primitive */
  141. PrimRef left,right;
  142. splitter(prim,dim,fsplit,left,right);
  143. assert(!left.bounds().empty());
  144. assert(!right.bounds().empty());
  145. const unsigned int splitprims_left = splitprims/2;
  146. const unsigned int splitprims_right = splitprims - splitprims_left;
  147. splitPrimitive(splitter,left,splitprims_left,grid,subPrims,numSubPrims);
  148. splitPrimitive(splitter,right,splitprims_right,grid,subPrims,numSubPrims);
  149. }
  150. }
  151. #else
  152. template<typename Splitter>
  153. void splitPrimitive(const Splitter& splitter,
  154. const PrimRef& prim,
  155. const unsigned int targetSubPrims,
  156. const SplittingGrid& grid,
  157. PrimRef subPrims[MAX_PRESPLITS_PER_PRIMITIVE],
  158. unsigned int& numSubPrims)
  159. {
  160. assert(targetSubPrims > 0 && targetSubPrims <= MAX_PRESPLITS_PER_PRIMITIVE);
  161. auto compare = [] ( const PrimRef& a, const PrimRef& b ) {
  162. return area(a.bounds()) < area(b.bounds());
  163. };
  164. subPrims[numSubPrims++] = prim;
  165. while (numSubPrims < targetSubPrims)
  166. {
  167. /* get top heap element */
  168. std::pop_heap(subPrims+0,subPrims+numSubPrims, compare);
  169. PrimRef top = subPrims[--numSubPrims];
  170. unsigned int dim; float fsplit;
  171. if (!grid.split_pos(top, dim, fsplit))
  172. {
  173. assert(numSubPrims < MAX_PRESPLITS_PER_PRIMITIVE);
  174. subPrims[numSubPrims++] = top;
  175. return;
  176. }
  177. /* split primitive */
  178. PrimRef left,right;
  179. splitter(top,dim,fsplit,left,right);
  180. assert(!left.bounds().empty());
  181. assert(!right.bounds().empty());
  182. subPrims[numSubPrims++] = left;
  183. std::push_heap(subPrims+0, subPrims+numSubPrims, compare);
  184. subPrims[numSubPrims++] = right;
  185. std::push_heap(subPrims+0, subPrims+numSubPrims, compare);
  186. }
  187. }
  188. #endif
  189. #if !defined(RTHWIF_STANDALONE)
  190. template<typename Mesh, typename SplitterFactory>
  191. PrimInfo createPrimRefArray_presplit(Geometry* geometry, unsigned int geomID, size_t numPrimRefs, mvector<PrimRef>& prims, BuildProgressMonitor& progressMonitor)
  192. {
  193. ParallelPrefixSumState<PrimInfo> pstate;
  194. /* first try */
  195. progressMonitor(0);
  196. PrimInfo pinfo = parallel_prefix_sum( pstate, size_t(0), geometry->size(), size_t(1024), PrimInfo(empty), [&](const range<size_t>& r, const PrimInfo& base) -> PrimInfo {
  197. return geometry->createPrimRefArray(prims,r,r.begin(),geomID);
  198. }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); });
  199. /* if we need to filter out geometry, run again */
  200. if (pinfo.size() != numPrimRefs)
  201. {
  202. progressMonitor(0);
  203. pinfo = parallel_prefix_sum( pstate, size_t(0), geometry->size(), size_t(1024), PrimInfo(empty), [&](const range<size_t>& r, const PrimInfo& base) -> PrimInfo {
  204. return geometry->createPrimRefArray(prims,r,base.size(),geomID);
  205. }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); });
  206. }
  207. return pinfo;
  208. }
  209. #endif
  210. template<typename SplitPrimitiveFunc, typename ProjectedPrimitiveAreaFunc, typename PrimVector>
  211. PrimInfo createPrimRefArray_presplit(size_t numPrimRefs,
  212. PrimVector& prims,
  213. const PrimInfo& pinfo,
  214. const SplitPrimitiveFunc& splitPrimitive,
  215. const ProjectedPrimitiveAreaFunc& primitiveArea)
  216. {
  217. static const size_t MIN_STEP_SIZE = 128;
  218. /* use correct number of primitives */
  219. size_t numPrimitives = pinfo.size();
  220. const size_t numPrimitivesExt = prims.size();
  221. const size_t numSplitPrimitivesBudget = numPrimitivesExt - numPrimitives;
  222. /* allocate double buffer presplit items */
  223. avector<PresplitItem> preSplitItem0(numPrimitivesExt);
  224. avector<PresplitItem> preSplitItem1(numPrimitivesExt);
  225. /* compute grid */
  226. SplittingGrid grid(pinfo.geomBounds);
  227. /* init presplit items and get total sum */
  228. const float psum = parallel_reduce( size_t(0), numPrimitives, size_t(MIN_STEP_SIZE), 0.0f, [&](const range<size_t>& r) -> float {
  229. float sum = 0.0f;
  230. for (size_t i=r.begin(); i<r.end(); i++)
  231. {
  232. preSplitItem0[i].index = (unsigned int)i;
  233. const Vec2i mc = grid.computeMC(prims[i]);
  234. /* if all bits are equal then we cannot split */
  235. preSplitItem0[i].priority = (mc.x != mc.y) ? PresplitItem::compute_priority(primitiveArea,prims[i],mc) : 0.0f;
  236. /* FIXME: sum undeterministic */
  237. sum += preSplitItem0[i].priority;
  238. }
  239. return sum;
  240. },[](const float& a, const float& b) -> float { return a+b; });
  241. /* compute number of splits per primitive */
  242. const float inv_psum = 1.0f / psum;
  243. parallel_for( size_t(0), numPrimitives, size_t(MIN_STEP_SIZE), [&](const range<size_t>& r) -> void {
  244. for (size_t i=r.begin(); i<r.end(); i++)
  245. {
  246. if (preSplitItem0[i].priority <= 0.0f) {
  247. preSplitItem0[i].data = 1;
  248. continue;
  249. }
  250. const float rel_p = (float)numSplitPrimitivesBudget * preSplitItem0[i].priority * inv_psum;
  251. if (rel_p < 1) {
  252. preSplitItem0[i].data = 1;
  253. continue;
  254. }
  255. //preSplitItem0[i].data = max(min(ceilf(rel_p),(float)MAX_PRESPLITS_PER_PRIMITIVE),1.0f);
  256. preSplitItem0[i].data = max(min(ceilf(logf(rel_p)/logf(2.0f)),(float)MAX_PRESPLITS_PER_PRIMITIVE_LOG),1.0f);
  257. preSplitItem0[i].data = 1 << preSplitItem0[i].data;
  258. assert(preSplitItem0[i].data <= MAX_PRESPLITS_PER_PRIMITIVE);
  259. }
  260. });
  261. auto isLeft = [&] (const PresplitItem &ref) { return ref.data <= 1; };
  262. size_t center = parallel_partitioning(preSplitItem0.data(),0,numPrimitives,isLeft,1024);
  263. assert(center <= numPrimitives);
  264. /* anything to split ? */
  265. if (center >= numPrimitives)
  266. return pinfo;
  267. size_t numPrimitivesToSplit = numPrimitives - center;
  268. assert(preSplitItem0[center].data >= 1.0f);
  269. /* sort presplit items in ascending order */
  270. radix_sort_u32(preSplitItem0.data() + center,preSplitItem1.data() + center,numPrimitivesToSplit,1024);
  271. CHECK_PRESPLIT(
  272. parallel_for( size_t(center+1), numPrimitives, size_t(MIN_STEP_SIZE), [&](const range<size_t>& r) -> void {
  273. for (size_t i=r.begin(); i<r.end(); i++)
  274. assert(preSplitItem0[i-1].data <= preSplitItem0[i].data);
  275. });
  276. );
  277. unsigned int* primOffset0 = (unsigned int*)preSplitItem1.data();
  278. unsigned int* primOffset1 = (unsigned int*)preSplitItem1.data() + numPrimitivesToSplit;
  279. /* compute actual number of sub-primitives generated within the [center;numPrimitives-1] range */
  280. const size_t totalNumSubPrims = parallel_reduce( size_t(center), numPrimitives, size_t(MIN_STEP_SIZE), size_t(0), [&](const range<size_t>& t) -> size_t {
  281. size_t sum = 0;
  282. for (size_t i=t.begin(); i<t.end(); i++)
  283. {
  284. const unsigned int primrefID = preSplitItem0[i].index;
  285. const unsigned int splitprims = preSplitItem0[i].data;
  286. assert(splitprims >= 1 && splitprims <= MAX_PRESPLITS_PER_PRIMITIVE);
  287. unsigned int numSubPrims = 0;
  288. PrimRef subPrims[MAX_PRESPLITS_PER_PRIMITIVE];
  289. splitPrimitive(prims[primrefID],splitprims,grid,subPrims,numSubPrims);
  290. assert(numSubPrims);
  291. numSubPrims--; // can reuse slot
  292. sum+=numSubPrims;
  293. preSplitItem0[i].data = (numSubPrims << 16) | splitprims;
  294. primOffset0[i-center] = numSubPrims;
  295. }
  296. return sum;
  297. },[](const size_t& a, const size_t& b) -> size_t { return a+b; });
  298. /* if we are over budget, need to shrink the range */
  299. if (totalNumSubPrims > numSplitPrimitivesBudget)
  300. {
  301. size_t new_center = numPrimitives-1;
  302. size_t sum = 0;
  303. for (;new_center>=center;new_center--)
  304. {
  305. const unsigned int numSubPrims = preSplitItem0[new_center].data >> 16;
  306. if (unlikely(sum + numSubPrims >= numSplitPrimitivesBudget)) break;
  307. sum += numSubPrims;
  308. }
  309. new_center++;
  310. primOffset0 += new_center - center;
  311. numPrimitivesToSplit -= new_center - center;
  312. center = new_center;
  313. assert(numPrimitivesToSplit == (numPrimitives - center));
  314. }
  315. /* parallel prefix sum to compute offsets for storing sub-primitives */
  316. const unsigned int offset = parallel_prefix_sum(primOffset0,primOffset1,numPrimitivesToSplit,(unsigned int)0,std::plus<unsigned int>());
  317. assert(numPrimitives+offset <= numPrimitivesExt);
  318. /* iterate over range, and split primitives into sub primitives and append them to prims array */
  319. parallel_for( size_t(center), numPrimitives, size_t(MIN_STEP_SIZE), [&](const range<size_t>& rn) -> void {
  320. for (size_t j=rn.begin(); j<rn.end(); j++)
  321. {
  322. const unsigned int primrefID = preSplitItem0[j].index;
  323. const unsigned int splitprims = preSplitItem0[j].data & 0xFFFF;
  324. assert(splitprims >= 1 && splitprims <= MAX_PRESPLITS_PER_PRIMITIVE);
  325. unsigned int numSubPrims = 0;
  326. PrimRef subPrims[MAX_PRESPLITS_PER_PRIMITIVE];
  327. splitPrimitive(prims[primrefID],splitprims,grid,subPrims,numSubPrims);
  328. const unsigned int numSubPrimsExpected MAYBE_UNUSED = preSplitItem0[j].data >> 16;
  329. assert(numSubPrims-1 == numSubPrimsExpected);
  330. const size_t newID = numPrimitives + primOffset1[j-center];
  331. assert(newID+numSubPrims-1 <= numPrimitivesExt);
  332. prims[primrefID] = subPrims[0];
  333. for (size_t i=1;i<numSubPrims;i++)
  334. prims[newID+i-1] = subPrims[i];
  335. }
  336. });
  337. numPrimitives += offset;
  338. /* recompute centroid bounding boxes */
  339. const PrimInfo pinfo1 = parallel_reduce(size_t(0),numPrimitives,size_t(MIN_STEP_SIZE),PrimInfo(empty),[&] (const range<size_t>& r) -> PrimInfo {
  340. PrimInfo p(empty);
  341. for (size_t j=r.begin(); j<r.end(); j++)
  342. p.add_center2(prims[j]);
  343. return p;
  344. }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); });
  345. assert(pinfo1.size() == numPrimitives);
  346. return pinfo1;
  347. }
  348. #if !defined(RTHWIF_STANDALONE)
  349. template<typename Mesh, typename SplitterFactory>
  350. PrimInfo createPrimRefArray_presplit(Scene* scene, Geometry::GTypeMask types, bool mblur, size_t numPrimRefs, mvector<PrimRef>& prims, BuildProgressMonitor& progressMonitor)
  351. {
  352. ParallelForForPrefixSumState<PrimInfo> pstate;
  353. Scene::Iterator2 iter(scene,types,mblur);
  354. /* first try */
  355. progressMonitor(0);
  356. pstate.init(iter,size_t(1024));
  357. PrimInfo pinfo = parallel_for_for_prefix_sum0( pstate, iter, PrimInfo(empty), [&](Geometry* mesh, const range<size_t>& r, size_t k, size_t geomID) -> PrimInfo {
  358. return mesh->createPrimRefArray(prims,r,k,(unsigned)geomID);
  359. }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); });
  360. /* if we need to filter out geometry, run again */
  361. if (pinfo.size() != numPrimRefs)
  362. {
  363. progressMonitor(0);
  364. pinfo = parallel_for_for_prefix_sum1( pstate, iter, PrimInfo(empty), [&](Geometry* mesh, const range<size_t>& r, size_t k, size_t geomID, const PrimInfo& base) -> PrimInfo {
  365. return mesh->createPrimRefArray(prims,r,base.size(),(unsigned)geomID);
  366. }, [](const PrimInfo& a, const PrimInfo& b) -> PrimInfo { return PrimInfo::merge(a,b); });
  367. }
  368. SplitterFactory Splitter(scene);
  369. auto split_primitive = [&] (const PrimRef &prim,
  370. const unsigned int splitprims,
  371. const SplittingGrid& grid,
  372. PrimRef subPrims[MAX_PRESPLITS_PER_PRIMITIVE],
  373. unsigned int& numSubPrims)
  374. {
  375. const auto splitter = Splitter(prim);
  376. splitPrimitive(splitter,prim,splitprims,grid,subPrims,numSubPrims);
  377. };
  378. auto primitiveArea = [&] (const PrimRef &ref) {
  379. const unsigned int geomID = ref.geomID();
  380. const unsigned int primID = ref.primID();
  381. return ((Mesh*)scene->get(geomID))->projectedPrimitiveArea(primID);
  382. };
  383. return createPrimRefArray_presplit(numPrimRefs,prims,pinfo,split_primitive,primitiveArea);
  384. }
  385. #endif
  386. }
  387. }