| 1 | // Copyright 2009-2021 Intel Corporation | 
| 2 | // SPDX-License-Identifier: Apache-2.0 | 
| 3 |  | 
| 4 | #include "bvh_builder_twolevel.h" | 
| 5 | #include "bvh_statistics.h" | 
| 6 | #include "../builders/bvh_builder_sah.h" | 
| 7 | #include "../common/scene_line_segments.h" | 
| 8 | #include "../common/scene_triangle_mesh.h" | 
| 9 | #include "../common/scene_quad_mesh.h" | 
| 10 |  | 
| 11 | #define PROFILE 0 | 
| 12 |  | 
| 13 | namespace embree | 
| 14 | { | 
| 15 |   namespace isa | 
| 16 |   { | 
| 17 |     template<int N, typename Mesh, typename Primitive> | 
| 18 |     BVHNBuilderTwoLevel<N,Mesh,Primitive>::BVHNBuilderTwoLevel (BVH* bvh, Scene* scene, Geometry::GTypeMask gtype, bool useMortonBuilder, const size_t singleThreadThreshold) | 
| 19 |       : bvh(bvh), scene(scene), refs(scene->device,0), prims(scene->device,0), singleThreadThreshold(singleThreadThreshold), gtype(gtype), useMortonBuilder_(useMortonBuilder) {} | 
| 20 |      | 
| 21 |     template<int N, typename Mesh, typename Primitive> | 
| 22 |     BVHNBuilderTwoLevel<N,Mesh,Primitive>::~BVHNBuilderTwoLevel () { | 
| 23 |     } | 
| 24 |  | 
| 25 |     // =========================================================================== | 
| 26 |     // =========================================================================== | 
| 27 |     // =========================================================================== | 
| 28 |  | 
| 29 |     template<int N, typename Mesh, typename Primitive> | 
| 30 |     void BVHNBuilderTwoLevel<N,Mesh,Primitive>::build() | 
| 31 |     { | 
| 32 |       /* delete some objects */ | 
| 33 |       size_t num = scene->size(); | 
| 34 |       if (num < bvh->objects.size()) { | 
| 35 |         parallel_for(num, bvh->objects.size(), [&] (const range<size_t>& r) { | 
| 36 |             for (size_t i=r.begin(); i<r.end(); i++) { | 
| 37 |               builders[i].reset(); | 
| 38 |               delete bvh->objects[i]; bvh->objects[i] = nullptr; | 
| 39 |             } | 
| 40 |           }); | 
| 41 |       } | 
| 42 |        | 
| 43 | #if PROFILE | 
| 44 |       while(1)  | 
| 45 | #endif | 
| 46 |       { | 
| 47 |       /* reset memory allocator */ | 
| 48 |       bvh->alloc.reset(); | 
| 49 |        | 
| 50 |       /* skip build for empty scene */ | 
| 51 |       const size_t numPrimitives = scene->getNumPrimitives(mask: gtype,mblur: false); | 
| 52 |  | 
| 53 |       if (numPrimitives == 0) { | 
| 54 |         prims.resize(new_size: 0); | 
| 55 |         bvh->set(BVH::emptyNode,empty,0); | 
| 56 |         return; | 
| 57 |       } | 
| 58 |  | 
| 59 |       /* calculate the size of the entire BVH */ | 
| 60 |       const size_t numLeafBlocks = Primitive::blocks(numPrimitives); | 
| 61 |       const size_t node_bytes = 2*numLeafBlocks*sizeof(typename BVH::AABBNode)/N; | 
| 62 |       const size_t leaf_bytes = size_t(1.2*numLeafBlocks*sizeof(Primitive)); | 
| 63 |       bvh->alloc.init_estimate(node_bytes+leaf_bytes);  | 
| 64 |  | 
| 65 |       double t0 = bvh->preBuild(TOSTRING(isa) "::BVH"  + toString(value: N) + "BuilderTwoLevel" ); | 
| 66 |  | 
| 67 |       /* resize object array if scene got larger */ | 
| 68 |       if (bvh->objects.size()  < num) bvh->objects.resize(num); | 
| 69 |       if (builders.size() < num) builders.resize(num); | 
| 70 |       resizeRefsList (); | 
| 71 |       nextRef.store(i: 0); | 
| 72 |        | 
| 73 |       /* create acceleration structures */ | 
| 74 |       parallel_for(size_t(0), num, [&] (const range<size_t>& r) | 
| 75 |       { | 
| 76 |         for (size_t objectID=r.begin(); objectID<r.end(); objectID++) | 
| 77 |         { | 
| 78 |           Mesh* mesh = scene->getSafe<Mesh>(objectID); | 
| 79 |        | 
| 80 |           /* ignore meshes we do not support */ | 
| 81 |           if (mesh == nullptr || mesh->numTimeSteps != 1) | 
| 82 |             continue; | 
| 83 |            | 
| 84 |           if (isSmallGeometry(mesh)) { | 
| 85 |              setupSmallBuildRefBuilder (objectID, mesh); | 
| 86 |           } else { | 
| 87 |             setupLargeBuildRefBuilder (objectID, mesh); | 
| 88 |           } | 
| 89 |         } | 
| 90 |       }); | 
| 91 |  | 
| 92 |       /* parallel build of acceleration structures */ | 
| 93 |       parallel_for(size_t(0), num, [&] (const range<size_t>& r) | 
| 94 |       { | 
| 95 |         for (size_t objectID=r.begin(); objectID<r.end(); objectID++) | 
| 96 |         { | 
| 97 |           /* ignore if no triangle mesh or not enabled */ | 
| 98 |           Mesh* mesh = scene->getSafe<Mesh>(objectID); | 
| 99 |           if (mesh == nullptr || !mesh->isEnabled() || mesh->numTimeSteps != 1)  | 
| 100 |             continue; | 
| 101 |  | 
| 102 |           builders[objectID]->attachBuildRefs (this); | 
| 103 |         } | 
| 104 |       }); | 
| 105 |  | 
| 106 |  | 
| 107 | #if PROFILE | 
| 108 |       double d0 = getSeconds(); | 
| 109 | #endif | 
| 110 |       /* fast path for single geometry scenes */ | 
| 111 |       if (nextRef == 1) {  | 
| 112 |         bvh->set(refs[0].node,LBBox3fa(refs[0].bounds()),numPrimitives); | 
| 113 |       } | 
| 114 |  | 
| 115 |       else | 
| 116 |       {      | 
| 117 |         /* open all large nodes */ | 
| 118 |         refs.resize(nextRef); | 
| 119 |  | 
| 120 |         /* this probably needs some more tuning */ | 
| 121 |         const size_t extSize = max(max((size_t)SPLIT_MIN_EXT_SPACE,refs.size()*SPLIT_MEMORY_RESERVE_SCALE),size_t((float)numPrimitives / SPLIT_MEMORY_RESERVE_FACTOR)); | 
| 122 |   | 
| 123 | #if !ENABLE_DIRECT_SAH_MERGE_BUILDER | 
| 124 |  | 
| 125 | #if ENABLE_OPEN_SEQUENTIAL | 
| 126 |         open_sequential(extSize);  | 
| 127 | #endif | 
| 128 |         /* compute PrimRefs */ | 
| 129 |         prims.resize(refs.size()); | 
| 130 | #endif | 
| 131 |          | 
| 132 |         { | 
| 133 | #if ENABLE_DIRECT_SAH_MERGE_BUILDER | 
| 134 |  | 
| 135 |           const PrimInfo pinfo = parallel_reduce(size_t(0), refs.size(),  PrimInfo(empty), [&] (const range<size_t>& r) -> PrimInfo { | 
| 136 |  | 
| 137 |               PrimInfo pinfo(empty); | 
| 138 |               for (size_t i=r.begin(); i<r.end(); i++) { | 
| 139 |                 pinfo.add_center2(refs[i]); | 
| 140 |               } | 
| 141 |               return pinfo; | 
| 142 |             }, [] (const PrimInfo& a, const PrimInfo& b) { return PrimInfo::merge(a,b); }); | 
| 143 |            | 
| 144 | #else | 
| 145 |           const PrimInfo pinfo = parallel_reduce(size_t(0), refs.size(),  PrimInfo(empty), [&] (const range<size_t>& r) -> PrimInfo { | 
| 146 |  | 
| 147 |               PrimInfo pinfo(empty); | 
| 148 |               for (size_t i=r.begin(); i<r.end(); i++) { | 
| 149 |                 pinfo.add_center2(refs[i]); | 
| 150 |                 prims[i] = PrimRef(refs[i].bounds(),(size_t)refs[i].node); | 
| 151 |               } | 
| 152 |               return pinfo; | 
| 153 |             }, [] (const PrimInfo& a, const PrimInfo& b) { return PrimInfo::merge(a,b); }); | 
| 154 | #endif    | 
| 155 |         | 
| 156 |           /* skip if all objects where empty */ | 
| 157 |           if (pinfo.size() == 0) | 
| 158 |             bvh->set(BVH::emptyNode,empty,0); | 
| 159 |          | 
| 160 |           /* otherwise build toplevel hierarchy */ | 
| 161 |           else | 
| 162 |           { | 
| 163 |             /* settings for BVH build */ | 
| 164 |             GeneralBVHBuilder::Settings settings; | 
| 165 |             settings.branchingFactor = N; | 
| 166 |             settings.maxDepth = BVH::maxBuildDepthLeaf; | 
| 167 |             settings.logBlockSize = bsr(v: N); | 
| 168 |             settings.minLeafSize = 1; | 
| 169 |             settings.maxLeafSize = 1; | 
| 170 |             settings.travCost = 1.0f; | 
| 171 |             settings.intCost = 1.0f; | 
| 172 |             settings.singleThreadThreshold = singleThreadThreshold; | 
| 173 |        | 
| 174 | #if ENABLE_DIRECT_SAH_MERGE_BUILDER | 
| 175 |              | 
| 176 |             refs.resize(extSize);  | 
| 177 |           | 
| 178 |             NodeRef root = BVHBuilderBinnedOpenMergeSAH::build<NodeRef,BuildRef>( | 
| 179 |               typename BVH::CreateAlloc(bvh), | 
| 180 |               typename BVH::AABBNode::Create2(), | 
| 181 |               typename BVH::AABBNode::Set2(), | 
| 182 |                | 
| 183 |               [&] (const BuildRef* refs, const range<size_t>& range, const FastAllocator::CachedAllocator& alloc) -> NodeRef  { | 
| 184 |                 assert(range.size() == 1); | 
| 185 |                 return (NodeRef) refs[range.begin()].node; | 
| 186 |               }, | 
| 187 |               [&] (BuildRef &bref, BuildRef *refs) -> size_t {  | 
| 188 |                 return openBuildRef(bref,refs); | 
| 189 |               },               | 
| 190 |               [&] (size_t dn) { bvh->scene->progressMonitor(0); }, | 
| 191 |               refs.data(),extSize,pinfo,settings); | 
| 192 | #else | 
| 193 |             NodeRef root = BVHBuilderBinnedSAH::build<NodeRef>( | 
| 194 |               typename BVH::CreateAlloc(bvh), | 
| 195 |               typename BVH::AABBNode::Create2(), | 
| 196 |               typename BVH::AABBNode::Set2(), | 
| 197 |                | 
| 198 |               [&] (const PrimRef* prims, const range<size_t>& range, const FastAllocator::CachedAllocator& alloc) -> NodeRef { | 
| 199 |                 assert(range.size() == 1); | 
| 200 |                 return (NodeRef) prims[range.begin()].ID(); | 
| 201 |               }, | 
| 202 |               [&] (size_t dn) { bvh->scene->progressMonitor(0); }, | 
| 203 |               prims.data(),pinfo,settings); | 
| 204 | #endif | 
| 205 |  | 
| 206 |              | 
| 207 |             bvh->set(root,LBBox3fa(pinfo.geomBounds),numPrimitives); | 
| 208 |           } | 
| 209 |         } | 
| 210 |       }   | 
| 211 |          | 
| 212 |       bvh->alloc.cleanup(); | 
| 213 |       bvh->postBuild(t0); | 
| 214 | #if PROFILE | 
| 215 |       double d1 = getSeconds(); | 
| 216 |       std::cout << "TOP_LEVEL OPENING/REBUILD TIME "  << 1000.0*(d1-d0) << " ms"  << std::endl; | 
| 217 | #endif | 
| 218 |       } | 
| 219 |  | 
| 220 |     } | 
| 221 |      | 
| 222 |     template<int N, typename Mesh, typename Primitive> | 
| 223 |     void BVHNBuilderTwoLevel<N,Mesh,Primitive>::deleteGeometry(size_t geomID) | 
| 224 |     { | 
| 225 |       if (geomID >= bvh->objects.size()) return; | 
| 226 |       if (builders[geomID]) builders[geomID].reset(); | 
| 227 |       delete bvh->objects [geomID]; bvh->objects [geomID] = nullptr; | 
| 228 |     } | 
| 229 |  | 
| 230 |     template<int N, typename Mesh, typename Primitive> | 
| 231 |     void BVHNBuilderTwoLevel<N,Mesh,Primitive>::clear() | 
| 232 |     { | 
| 233 |       for (size_t i=0; i<bvh->objects.size(); i++)  | 
| 234 |         if (bvh->objects[i]) bvh->objects[i]->clear(); | 
| 235 |  | 
| 236 |       for (size_t i=0; i<builders.size(); i++)  | 
| 237 |         if (builders[i]) builders[i].reset(); | 
| 238 |  | 
| 239 |       refs.clear(); | 
| 240 |     } | 
| 241 |  | 
| 242 |     template<int N, typename Mesh, typename Primitive> | 
| 243 |     void BVHNBuilderTwoLevel<N,Mesh,Primitive>::open_sequential(const size_t extSize) | 
| 244 |     { | 
| 245 |       if (refs.size() == 0) | 
| 246 | 	return; | 
| 247 |  | 
| 248 |       refs.reserve(extSize); | 
| 249 |  | 
| 250 | #if 1 | 
| 251 |       for (size_t i=0;i<refs.size();i++) | 
| 252 |       { | 
| 253 |         NodeRef ref = refs[i].node; | 
| 254 |         if (ref.isAABBNode()) | 
| 255 |           BVH::prefetch(ref); | 
| 256 |       } | 
| 257 | #endif | 
| 258 |  | 
| 259 |       std::make_heap(refs.begin(),refs.end()); | 
| 260 |       while (refs.size()+N-1 <= extSize) | 
| 261 |       { | 
| 262 |         std::pop_heap (refs.begin(),refs.end());  | 
| 263 |         NodeRef ref = refs.back().node; | 
| 264 |         if (ref.isLeaf()) break; | 
| 265 |         refs.pop_back();     | 
| 266 |          | 
| 267 |         AABBNode* node = ref.getAABBNode(); | 
| 268 |         for (size_t i=0; i<N; i++) { | 
| 269 |           if (node->child(i) == BVH::emptyNode) continue; | 
| 270 |           refs.push_back(BuildRef(node->bounds(i),node->child(i))); | 
| 271 |           | 
| 272 | #if 1 | 
| 273 |           NodeRef ref_pre = node->child(i); | 
| 274 |           if (ref_pre.isAABBNode()) | 
| 275 |             ref_pre.prefetch(); | 
| 276 | #endif | 
| 277 |           std::push_heap (refs.begin(),refs.end());  | 
| 278 |         } | 
| 279 |       } | 
| 280 |     } | 
| 281 |  | 
| 282 |     template<int N, typename Mesh, typename Primitive> | 
| 283 |     void BVHNBuilderTwoLevel<N,Mesh,Primitive>::setupSmallBuildRefBuilder (size_t objectID, Mesh const * const /*mesh*/) | 
| 284 |     { | 
| 285 |       if (builders[objectID] == nullptr ||                                         // new mesh | 
| 286 |           dynamic_cast<RefBuilderSmall*>(builders[objectID].get()) == nullptr)     // size change resulted in large->small change | 
| 287 |       { | 
| 288 |         builders[objectID].reset (new RefBuilderSmall(objectID)); | 
| 289 |       } | 
| 290 |     } | 
| 291 |  | 
| 292 |     template<int N, typename Mesh, typename Primitive> | 
| 293 |     void BVHNBuilderTwoLevel<N,Mesh,Primitive>::setupLargeBuildRefBuilder (size_t objectID, Mesh const * const mesh) | 
| 294 |     { | 
| 295 |       if (bvh->objects[objectID] == nullptr ||                                  // new mesh | 
| 296 |           builders[objectID]->meshQualityChanged (mesh->quality) ||             // changed build quality | 
| 297 |           dynamic_cast<RefBuilderLarge*>(builders[objectID].get()) == nullptr)  // size change resulted in small->large change | 
| 298 |       { | 
| 299 |         Builder* builder = nullptr; | 
| 300 |         delete bvh->objects[objectID];  | 
| 301 |         createMeshAccel(geomID: objectID, builder); | 
| 302 |         builders[objectID].reset (new RefBuilderLarge(objectID, builder, mesh->quality)); | 
| 303 |       } | 
| 304 |     } | 
| 305 |  | 
| 306 | #if defined(EMBREE_GEOMETRY_TRIANGLE) | 
| 307 |     Builder* BVH4BuilderTwoLevelTriangle4MeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 308 |       return new BVHNBuilderTwoLevel<4,TriangleMesh,Triangle4>((BVH4*)bvh,scene,TriangleMesh::geom_type,useMortonBuilder); | 
| 309 |     } | 
| 310 |     Builder* BVH4BuilderTwoLevelTriangle4vMeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 311 |       return new BVHNBuilderTwoLevel<4,TriangleMesh,Triangle4v>((BVH4*)bvh,scene,TriangleMesh::geom_type,useMortonBuilder); | 
| 312 |     } | 
| 313 |     Builder* BVH4BuilderTwoLevelTriangle4iMeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 314 |       return new BVHNBuilderTwoLevel<4,TriangleMesh,Triangle4i>((BVH4*)bvh,scene,TriangleMesh::geom_type,useMortonBuilder); | 
| 315 |     } | 
| 316 | #endif | 
| 317 |  | 
| 318 | #if defined(EMBREE_GEOMETRY_QUAD) | 
| 319 |     Builder* BVH4BuilderTwoLevelQuadMeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 320 |     return new BVHNBuilderTwoLevel<4,QuadMesh,Quad4v>((BVH4*)bvh,scene,QuadMesh::geom_type,useMortonBuilder); | 
| 321 |     } | 
| 322 | #endif | 
| 323 |  | 
| 324 | #if defined(EMBREE_GEOMETRY_USER) | 
| 325 |     Builder* BVH4BuilderTwoLevelVirtualSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 326 |     return new BVHNBuilderTwoLevel<4,UserGeometry,Object>((BVH4*)bvh,scene,UserGeometry::geom_type,useMortonBuilder); | 
| 327 |     } | 
| 328 | #endif | 
| 329 |  | 
| 330 | #if defined(EMBREE_GEOMETRY_INSTANCE) | 
| 331 |     Builder* BVH4BuilderTwoLevelInstanceSAH (void* bvh, Scene* scene, Geometry::GTypeMask gtype, bool useMortonBuilder) { | 
| 332 |       return new BVHNBuilderTwoLevel<4,Instance,InstancePrimitive>((BVH4*)bvh,scene,gtype,useMortonBuilder); | 
| 333 |     } | 
| 334 | #endif | 
| 335 |  | 
| 336 | #if defined(__AVX__) | 
| 337 | #if defined(EMBREE_GEOMETRY_TRIANGLE) | 
| 338 |     Builder* BVH8BuilderTwoLevelTriangle4MeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 339 |       return new BVHNBuilderTwoLevel<8,TriangleMesh,Triangle4>((BVH8*)bvh,scene,TriangleMesh::geom_type,useMortonBuilder); | 
| 340 |     } | 
| 341 |     Builder* BVH8BuilderTwoLevelTriangle4vMeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 342 |       return new BVHNBuilderTwoLevel<8,TriangleMesh,Triangle4v>((BVH8*)bvh,scene,TriangleMesh::geom_type,useMortonBuilder); | 
| 343 |     } | 
| 344 |     Builder* BVH8BuilderTwoLevelTriangle4iMeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 345 |       return new BVHNBuilderTwoLevel<8,TriangleMesh,Triangle4i>((BVH8*)bvh,scene,TriangleMesh::geom_type,useMortonBuilder); | 
| 346 |     } | 
| 347 | #endif | 
| 348 |  | 
| 349 | #if defined(EMBREE_GEOMETRY_QUAD) | 
| 350 |     Builder* BVH8BuilderTwoLevelQuadMeshSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 351 |       return new BVHNBuilderTwoLevel<8,QuadMesh,Quad4v>((BVH8*)bvh,scene,QuadMesh::geom_type,useMortonBuilder); | 
| 352 |     } | 
| 353 | #endif | 
| 354 |  | 
| 355 | #if defined(EMBREE_GEOMETRY_USER) | 
| 356 |     Builder* BVH8BuilderTwoLevelVirtualSAH (void* bvh, Scene* scene, bool useMortonBuilder) { | 
| 357 |       return new BVHNBuilderTwoLevel<8,UserGeometry,Object>((BVH8*)bvh,scene,UserGeometry::geom_type,useMortonBuilder); | 
| 358 |     } | 
| 359 | #endif | 
| 360 |  | 
| 361 | #if defined(EMBREE_GEOMETRY_INSTANCE) | 
| 362 |     Builder* BVH8BuilderTwoLevelInstanceSAH (void* bvh, Scene* scene, Geometry::GTypeMask gtype, bool useMortonBuilder) { | 
| 363 |       return new BVHNBuilderTwoLevel<8,Instance,InstancePrimitive>((BVH8*)bvh,scene,gtype,useMortonBuilder); | 
| 364 |     } | 
| 365 | #endif | 
| 366 |  | 
| 367 | #endif | 
| 368 |   } | 
| 369 | } | 
| 370 |  |