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 *CBVH_PBRT::recursiveBuild ( std::vector<BVHPrimitiveInfo> &primitiveInfo, int start, int end, int *totalNodes, CONST_VECTOR_OBJECT &orderedPrims ) { wxASSERT( totalNodes != NULL ); wxASSERT( start >= 0 ); wxASSERT( end >= 0 ); wxASSERT( start != end ); wxASSERT( start < end ); wxASSERT( start <= (int)primitiveInfo.size() ); wxASSERT( end <= (int)primitiveInfo.size() ); (*totalNodes)++; // !TODO: implement an memory Arena BVHBuildNode *node = static_cast<BVHBuildNode *>( _mm_malloc( sizeof( BVHBuildNode ), L1_CACHE_LINE_SIZE ) ); m_addresses_pointer_to_mm_free.push_back( node ); node->bounds.Reset(); node->firstPrimOffset = 0; node->nPrimitives = 0; node->splitAxis = 0; node->children[0] = NULL; node->children[1] = NULL; // Compute bounds of all primitives in BVH node CBBOX bounds; bounds.Reset(); for( int i = start; i < end; ++i ) bounds.Union( primitiveInfo[i].bounds ); int nPrimitives = end - start; if( nPrimitives == 1 ) { // Create leaf _BVHBuildNode_ int firstPrimOffset = orderedPrims.size(); for( int i = start; i < end; ++i ) { int primitiveNr = primitiveInfo[i].primitiveNumber; wxASSERT( primitiveNr < (int)m_primitives.size() ); orderedPrims.push_back( m_primitives[ primitiveNr ] ); } node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); } else { // Compute bound of primitive centroids, choose split dimension _dim_ CBBOX centroidBounds; centroidBounds.Reset(); for( int i = start; i < end; ++i ) centroidBounds.Union( primitiveInfo[i].centroid ); const int dim = centroidBounds.MaxDimension(); // Partition primitives into two sets and build children int mid = (start + end) / 2; if( fabs( centroidBounds.Max()[dim] - centroidBounds.Min()[dim] ) < (FLT_EPSILON + FLT_EPSILON) ) { // Create leaf _BVHBuildNode_ const int firstPrimOffset = orderedPrims.size(); for( int i = start; i < end; ++i ) { int primitiveNr = primitiveInfo[i].primitiveNumber; wxASSERT( (primitiveNr >= 0) && (primitiveNr < (int)m_primitives.size()) ); const COBJECT *obj = static_cast<const COBJECT *>( m_primitives[ primitiveNr ] ); wxASSERT( obj != NULL ); orderedPrims.push_back( obj ); } node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); } else { // Partition primitives based on _splitMethod_ switch( m_splitMethod ) { case SPLIT_MIDDLE: { // Partition primitives through node's midpoint float pmid = centroidBounds.GetCenter( dim ); BVHPrimitiveInfo *midPtr = std::partition( &primitiveInfo[start], &primitiveInfo[end - 1] + 1, CompareToMid( dim, pmid ) ); mid = midPtr - &primitiveInfo[0]; wxASSERT( (mid >= start) && (mid <= end) ); if( (mid != start) && (mid != end) ) // for lots of prims with large overlapping bounding boxes, this // may fail to partition; in that case don't break and fall through // to SPLIT_EQUAL_COUNTS break; } case SPLIT_EQUALCOUNTS: { // Partition primitives into equally-sized subsets mid = (start + end) / 2; std::nth_element( &primitiveInfo[start], &primitiveInfo[mid], &primitiveInfo[end - 1] + 1, ComparePoints( dim ) ); break; } case SPLIT_SAH: default: { // Partition primitives using approximate SAH if( nPrimitives <= 2 ) { // Partition primitives into equally-sized subsets mid = (start + end) / 2; std::nth_element( &primitiveInfo[start], &primitiveInfo[mid], &primitiveInfo[end - 1] + 1, ComparePoints( dim ) ); } else { // Allocate _BucketInfo_ for SAH partition buckets const int nBuckets = 12; BucketInfo buckets[nBuckets]; for( int i = 0; i < nBuckets; ++i ) { buckets[i].count = 0; buckets[i].bounds.Reset(); } // Initialize _BucketInfo_ for SAH partition buckets for( int i = start; i < end; ++i ) { int b = nBuckets * centroidBounds.Offset( primitiveInfo[i].centroid )[dim]; if( b == nBuckets ) b = nBuckets - 1; wxASSERT( b >= 0 && b < nBuckets ); buckets[b].count++; buckets[b].bounds.Union( primitiveInfo[i].bounds ); } // Compute costs for splitting after each bucket float cost[nBuckets - 1]; for( int i = 0; i < (nBuckets - 1); ++i ) { CBBOX b0, b1; b0.Reset(); b1.Reset(); int count0 = 0; int count1 = 0; for( int j = 0; j <= i; ++j ) { if( buckets[j].count ) { count0 += buckets[j].count; b0.Union( buckets[j].bounds ); } } for( int j = i + 1; j < nBuckets; ++j ) { if( buckets[j].count ) { count1 += buckets[j].count; b1.Union( buckets[j].bounds ); } } cost[i] = 1.0f + ( count0 * b0.SurfaceArea() + count1 * b1.SurfaceArea() ) / bounds.SurfaceArea(); } // Find bucket to split at that minimizes SAH metric float minCost = cost[0]; int minCostSplitBucket = 0; for( int i = 1; i < (nBuckets - 1); ++i ) { if( cost[i] < minCost ) { minCost = cost[i]; minCostSplitBucket = i; } } // Either create leaf or split primitives at selected SAH // bucket if( (nPrimitives > m_maxPrimsInNode) || (minCost < (float)nPrimitives) ) { BVHPrimitiveInfo *pmid = std::partition( &primitiveInfo[start], &primitiveInfo[end - 1] + 1, CompareToBucket( minCostSplitBucket, nBuckets, dim, centroidBounds ) ); mid = pmid - &primitiveInfo[0]; wxASSERT( (mid >= start) && (mid <= end) ); } else { // Create leaf _BVHBuildNode_ const int firstPrimOffset = orderedPrims.size(); for( int i = start; i < end; ++i ) { const int primitiveNr = primitiveInfo[i].primitiveNumber; wxASSERT( primitiveNr < (int)m_primitives.size() ); orderedPrims.push_back( m_primitives[ primitiveNr ] ); } node->InitLeaf( firstPrimOffset, nPrimitives, bounds ); return node; } } break; } } node->InitInterior( dim, recursiveBuild( primitiveInfo, start, mid, totalNodes, orderedPrims ), recursiveBuild( primitiveInfo, mid, end, totalNodes, orderedPrims) ); } } return node; }