BVHBuildNode *CBVH_PBRT::emitLBVH( BVHBuildNode *&buildNodes, const std::vector<BVHPrimitiveInfo> &primitiveInfo, MortonPrimitive *mortonPrims, int nPrimitives, int *totalNodes, CONST_VECTOR_OBJECT &orderedPrims, int *orderedPrimsOffset, int bit) { wxASSERT( nPrimitives > 0 ); wxASSERT( totalNodes != NULL ); wxASSERT( orderedPrimsOffset != NULL ); wxASSERT( nPrimitives > 0 ); wxASSERT( mortonPrims != NULL ); if( (bit == -1) || (nPrimitives < m_maxPrimsInNode) ) { // Create and return leaf node of LBVH treelet (*totalNodes)++; BVHBuildNode *node = buildNodes++; CBBOX bounds; bounds.Reset(); int firstPrimOffset = *orderedPrimsOffset; *orderedPrimsOffset += nPrimitives; wxASSERT( (firstPrimOffset + (nPrimitives - 1)) < (int)orderedPrims.size() ); for( int i = 0; i < nPrimitives; ++i ) { const int primitiveIndex = mortonPrims[i].primitiveIndex; wxASSERT( primitiveIndex < (int)m_primitives.size() ); orderedPrims[firstPrimOffset + i] = m_primitives[primitiveIndex]; bounds.Union( primitiveInfo[primitiveIndex].bounds ); } node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); return node; } else { int mask = 1 << bit; // Advance to next subtree level if there's no LBVH split for this bit if( (mortonPrims[0].mortonCode & mask) == (mortonPrims[nPrimitives - 1].mortonCode & mask) ) return emitLBVH( buildNodes, primitiveInfo, mortonPrims, nPrimitives, totalNodes, orderedPrims, orderedPrimsOffset, bit - 1 ); // Find LBVH split point for this dimension int searchStart = 0; int searchEnd = nPrimitives - 1; while( searchStart + 1 != searchEnd ) { wxASSERT(searchStart != searchEnd); const int mid = (searchStart + searchEnd) / 2; if( (mortonPrims[searchStart].mortonCode & mask) == (mortonPrims[mid].mortonCode & mask) ) searchStart = mid; else { wxASSERT( (mortonPrims[mid].mortonCode & mask) == (mortonPrims[searchEnd].mortonCode & mask) ); searchEnd = mid; } } const int splitOffset = searchEnd; wxASSERT( splitOffset <= (nPrimitives - 1) ); wxASSERT( (mortonPrims[splitOffset - 1].mortonCode & mask) != (mortonPrims[splitOffset].mortonCode & mask) ); // Create and return interior LBVH node (*totalNodes)++; BVHBuildNode *node = buildNodes++; BVHBuildNode *lbvh[2]; lbvh[0] = emitLBVH( buildNodes, primitiveInfo, mortonPrims, splitOffset, totalNodes, orderedPrims, orderedPrimsOffset, bit - 1 ); lbvh[1] = emitLBVH( buildNodes, primitiveInfo, &mortonPrims[splitOffset], nPrimitives - splitOffset, totalNodes, orderedPrims, orderedPrimsOffset, bit - 1 ); const int axis = bit % 3; node->InitInterior( axis, lbvh[0], lbvh[1] ); return node; } }
BVHBuildNode *BVHAccel::emitLBVH( BVHBuildNode *&buildNodes, const std::vector<BVHPrimitiveInfo> &primitiveInfo, MortonPrimitive *mortonPrims, int nPrimitives, int *totalNodes, std::vector<std::shared_ptr<Primitive>> &orderedPrims, std::atomic<int> *orderedPrimsOffset, int bitIndex) const { Assert(nPrimitives > 0); if (bitIndex == -1 || nPrimitives < maxPrimsInNode) { // Create and return leaf node of LBVH treelet (*totalNodes)++; BVHBuildNode *node = buildNodes++; Bounds3f bounds; int firstPrimOffset = orderedPrimsOffset->fetch_add(nPrimitives); for (int i = 0; i < nPrimitives; ++i) { int primitiveIndex = mortonPrims[i].primitiveIndex; orderedPrims[firstPrimOffset + i] = primitives[primitiveIndex]; bounds = Union(bounds, primitiveInfo[primitiveIndex].bounds); } node->InitLeaf(firstPrimOffset, nPrimitives, bounds); return node; } else { int mask = 1 << bitIndex; // Advance to next subtree level if there's no LBVH split for this bit if ((mortonPrims[0].mortonCode & mask) == (mortonPrims[nPrimitives - 1].mortonCode & mask)) return emitLBVH(buildNodes, primitiveInfo, mortonPrims, nPrimitives, totalNodes, orderedPrims, orderedPrimsOffset, bitIndex - 1); // Find LBVH split point for this dimension int searchStart = 0, searchEnd = nPrimitives - 1; while (searchStart + 1 != searchEnd) { Assert(searchStart != searchEnd); int mid = (searchStart + searchEnd) / 2; if ((mortonPrims[searchStart].mortonCode & mask) == (mortonPrims[mid].mortonCode & mask)) searchStart = mid; else { Assert((mortonPrims[mid].mortonCode & mask) == (mortonPrims[searchEnd].mortonCode & mask)); searchEnd = mid; } } int splitOffset = searchEnd; Assert(splitOffset <= nPrimitives - 1); Assert((mortonPrims[splitOffset - 1].mortonCode & mask) != (mortonPrims[splitOffset].mortonCode & mask)); // Create and return interior LBVH node (*totalNodes)++; BVHBuildNode *node = buildNodes++; BVHBuildNode *lbvh[2] = { emitLBVH(buildNodes, primitiveInfo, mortonPrims, splitOffset, totalNodes, orderedPrims, orderedPrimsOffset, bitIndex - 1), emitLBVH(buildNodes, primitiveInfo, &mortonPrims[splitOffset], nPrimitives - splitOffset, totalNodes, orderedPrims, orderedPrimsOffset, bitIndex - 1)}; int axis = bitIndex % 3; node->InitInterior(axis, lbvh[0], lbvh[1]); return node; } }
BVHBuildNode *CBVH_PBRT::HLBVHBuild( const std::vector<BVHPrimitiveInfo> &primitiveInfo, int *totalNodes, CONST_VECTOR_OBJECT &orderedPrims ) { // Compute bounding box of all primitive centroids CBBOX bounds; bounds.Reset(); for( unsigned int i = 0; i < primitiveInfo.size(); ++i ) bounds.Union( primitiveInfo[i].centroid ); // Compute Morton indices of primitives std::vector<MortonPrimitive> mortonPrims( primitiveInfo.size() ); for( int i = 0; i < (int)primitiveInfo.size(); ++i ) { // Initialize _mortonPrims[i]_ for _i_th primitive const int mortonBits = 10; const int mortonScale = 1 << mortonBits; wxASSERT( primitiveInfo[i].primitiveNumber < (int)primitiveInfo.size() ); mortonPrims[i].primitiveIndex = primitiveInfo[i].primitiveNumber; const SFVEC3F centroidOffset = bounds.Offset( primitiveInfo[i].centroid ); wxASSERT( (centroidOffset.x >= 0.0f) && (centroidOffset.x <= 1.0f) ); wxASSERT( (centroidOffset.y >= 0.0f) && (centroidOffset.y <= 1.0f) ); wxASSERT( (centroidOffset.z >= 0.0f) && (centroidOffset.z <= 1.0f) ); mortonPrims[i].mortonCode = EncodeMorton3( centroidOffset * SFVEC3F( (float)mortonScale ) ); } // Radix sort primitive Morton indices RadixSort( &mortonPrims ); // Create LBVH treelets at bottom of BVH // Find intervals of primitives for each treelet std::vector<LBVHTreelet> treeletsToBuild; for( int start = 0, end = 1; end <= (int)mortonPrims.size(); ++end ) { const uint32_t mask = 0b00111111111111000000000000000000; if( (end == (int)mortonPrims.size()) || ( (mortonPrims[start].mortonCode & mask) != (mortonPrims[end].mortonCode & mask) ) ) { // Add entry to _treeletsToBuild_ for this treelet const int numPrimitives = end - start; const int maxBVHNodes = 2 * numPrimitives; // !TODO: implement a memory arena BVHBuildNode *nodes = static_cast<BVHBuildNode *>( _mm_malloc( maxBVHNodes * sizeof( BVHBuildNode ), L1_CACHE_LINE_SIZE ) ); m_addresses_pointer_to_mm_free.push_back( nodes ); for( int i = 0; i < maxBVHNodes; ++i ) { nodes[i].bounds.Reset(); nodes[i].firstPrimOffset = 0; nodes[i].nPrimitives = 0; nodes[i].splitAxis = 0; nodes[i].children[0] = NULL; nodes[i].children[1] = NULL; } LBVHTreelet tmpTreelet; tmpTreelet.startIndex = start; tmpTreelet.numPrimitives = numPrimitives; tmpTreelet.buildNodes = nodes; treeletsToBuild.push_back( tmpTreelet ); start = end; } } // Create LBVHs for treelets in parallel int atomicTotal = 0; int orderedPrimsOffset = 0; orderedPrims.resize( m_primitives.size() ); for( int index = 0; index < (int)treeletsToBuild.size(); ++index ) { // Generate _index_th LBVH treelet int nodesCreated = 0; const int firstBit = 29 - 12; LBVHTreelet &tr = treeletsToBuild[index]; wxASSERT( tr.startIndex < (int)mortonPrims.size() ); tr.buildNodes = emitLBVH( tr.buildNodes, primitiveInfo, &mortonPrims[tr.startIndex], tr.numPrimitives, &nodesCreated, orderedPrims, &orderedPrimsOffset, firstBit ); atomicTotal += nodesCreated; } *totalNodes = atomicTotal; // Initialize _finishedTreelets_ with treelet root node pointers std::vector<BVHBuildNode *> finishedTreelets; finishedTreelets.reserve( treeletsToBuild.size() ); for( int index = 0; index < (int)treeletsToBuild.size(); ++index ) finishedTreelets.push_back( treeletsToBuild[index].buildNodes ); // Create and return SAH BVH from LBVH treelets return buildUpperSAH( finishedTreelets, 0, finishedTreelets.size(), totalNodes ); }
BVHBuildNode *BVHAccel::HLBVHBuild( MemoryArena &arena, const std::vector<BVHPrimitiveInfo> &primitiveInfo, int *totalNodes, std::vector<std::shared_ptr<Primitive>> &orderedPrims) const { // Compute bounding box of all primitive centroids Bounds3f bounds; for (const BVHPrimitiveInfo &pi : primitiveInfo) bounds = Union(bounds, pi.centroid); // Compute Morton indices of primitives std::vector<MortonPrimitive> mortonPrims(primitiveInfo.size()); ParallelFor([&](int i) { // Initialize _mortionPrims[i]_ for _i_th primitive constexpr int mortonBits = 10; constexpr int mortonScale = 1 << mortonBits; mortonPrims[i].primitiveIndex = primitiveInfo[i].primitiveNumber; Vector3f centroidOffset = bounds.Offset(primitiveInfo[i].centroid); mortonPrims[i].mortonCode = EncodeMorton3(centroidOffset * mortonScale); }, primitiveInfo.size(), 512); // Radix sort primitive Morton indices RadixSort(&mortonPrims); // Create LBVH treelets at bottom of BVH // Find intervals of primitives for each treelet std::vector<LBVHTreelet> treeletsToBuild; for (int start = 0, end = 1; end <= (int)mortonPrims.size(); ++end) { uint32_t mask = 0b00111111111111000000000000000000; if (end == (int)mortonPrims.size() || ((mortonPrims[start].mortonCode & mask) != (mortonPrims[end].mortonCode & mask))) { // Add entry to _treeletsToBuild_ for this treelet int nPrimitives = end - start; int maxBVHNodes = 2 * nPrimitives; BVHBuildNode *nodes = arena.Alloc<BVHBuildNode>(maxBVHNodes, false); treeletsToBuild.push_back({start, nPrimitives, nodes}); start = end; } } // Create LBVHs for treelets in parallel std::atomic<int> atomicTotal(0), orderedPrimsOffset(0); orderedPrims.resize(primitives.size()); ParallelFor([&](int i) { // Generate _i_th LBVH treelet int nodesCreated = 0; const int firstBitIndex = 29 - 12; LBVHTreelet &tr = treeletsToBuild[i]; tr.buildNodes = emitLBVH(tr.buildNodes, primitiveInfo, &mortonPrims[tr.startIndex], tr.nPrimitives, &nodesCreated, orderedPrims, &orderedPrimsOffset, firstBitIndex); atomicTotal += nodesCreated; }, treeletsToBuild.size()); *totalNodes = atomicTotal; // Create and return SAH BVH from LBVH treelets std::vector<BVHBuildNode *> finishedTreelets; finishedTreelets.reserve(treeletsToBuild.size()); for (LBVHTreelet &treelet : treeletsToBuild) finishedTreelets.push_back(treelet.buildNodes); return buildUpperSAH(arena, finishedTreelets, 0, finishedTreelets.size(), totalNodes); }