// This function encodes a Region as a 64-bit integer so that it can be used as a key to access chunk data in the SQLite database. // A region actually contains more than 64-bits of data so some has to be lost here. Specifically we assume that we already know // the size of the region (so we only have to encode it's lower corner and not its upper corner or extents), and we also restrict // the range of valid coordinates. A Region's coordinates are represented by 3-bits of data, but we only support converting to a key // if every coordinate can be represented by 21 bits of data. This way we can fit three coordinates only 63 bits of data. This limits // the range of values to +/- 2^20, which is enough for our purposes. uint64_t regionToKey(const PolyVox::Region& region) { // Cast to unsigned values so that bit shifting works predictably. uint32_t x = static_cast<uint32_t>(region.getLowerX()); uint32_t y = static_cast<uint32_t>(region.getLowerY()); uint32_t z = static_cast<uint32_t>(region.getLowerZ()); // The magnitude of our input values is fairly restricted, but the values could stil be negative. This means the sign bit could // be set and this needs to be encoded as well. We therefore perform a left rotate on the bits to bring the sign bit into the LSB. x = rotateLeft(x); y = rotateLeft(y); z = rotateLeft(z); // Now convert to 64-bits uint64_t x64 = x; uint64_t y64 = y; uint64_t z64 = z; // Morten-encode the components to give our final key uint64_t result = EncodeMorton3(x64, y64, z64); // Return the combined value return result; }
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); }