BVH4Statistics::BVH4Statistics (BVH4* bvh) : bvh(bvh) { numAlignedNodes = numUnalignedNodes = 0; numAlignedNodesMB = numUnalignedNodesMB = 0; numLeaves = numPrims = depth = 0; childrenAlignedNodes = childrenUnalignedNodes = 0; childrenAlignedNodesMB = childrenUnalignedNodesMB = 0; bvhSAH = 0.0f; hash = 0; float A = max(0.0f,halfArea(bvh->bounds)); statistics(bvh->root,A,depth); bvhSAH /= halfArea(bvh->bounds); assert(depth <= BVH4::maxDepth); }
BVHNStatistics<N>::BVHNStatistics (BVH* bvh) : bvh(bvh) { numAlignedNodes = numUnalignedNodes = 0; numAlignedNodesMB = numUnalignedNodesMB = 0; numTransformNodes = 0; numLeaves = numPrims = numPrimBlocks = depth = 0; childrenAlignedNodes = childrenUnalignedNodes = 0; childrenAlignedNodesMB = childrenUnalignedNodesMB = 0; bvhSAH = 0.0f; leafSAH = 0.0f; float A = max(0.0f,halfArea(bvh->bounds)); statistics(bvh->root,A,depth); bvhSAH /= halfArea(bvh->bounds); leafSAH /= halfArea(bvh->bounds); assert(depth <= BVH::maxDepth); }
void BVH2Builder::SplitTask::split() { /*! compute leaf and split cost */ size_t N = job.size(); float leafSAH = BVH2<Triangle4>::intCost*job.leafSAH; float splitSAH = BVH2<Triangle4>::travCost*halfArea(job.geomBounds)+BVH2<Triangle4>::intCost*job.splitSAH; /*! make leaf node when threshold reached or SAH tells us */ if (N <= 1 || depth > BVH2<Triangle4>::maxDepth || (N <= BVH2<Triangle4>::maxLeafSize && leafSAH < splitSAH)) { this->nodeID = parent->bvh->createLeaf(parent->prims,parent->triangles,parent->globalAllocPrimitives(blocks(N)),job.start(),N); delete this; return; } /*! perform split */ ObjectBinning<2> left,right; job.split(parent->prims,left,right); /*! create an inner node */ int nodeID = (int)parent->globalAllocNodes(1); this->nodeID = BVH2<Triangle4>::id2offset(nodeID); BVH2<Triangle4>::Node& node = parent->bvh->nodes[nodeID].clear(); node.set(0,left .geomBounds,0); node.set(1,right.geomBounds,0); parent->recurse(node.child[0],depth+1,left ); parent->recurse(node.child[1],depth+1,right); delete this; }
void BVH2Builder<Heuristic>::SplitTask::recurse(const TaskScheduler::ThreadInfo& thread) { /*! compute leaf and split cost */ const float leafSAH = parent->trity.intCost*pinfo.sah(); const float splitSAH = BVH2::travCost*halfArea(pinfo.geomBounds)+parent->trity.intCost*split.sah(); assert(atomic_set<PrimRefBlock>::block_iterator_unsafe(prims).size() == pinfo.size()); assert(leafSAH >= 0 && splitSAH >= 0); /*! create a leaf node when threshold reached or SAH tells us to stop */ if (pinfo.size() <= 1 || depth > BVH2::maxDepth || (pinfo.size() <= parent->bvh->maxLeafTris && leafSAH <= splitSAH)) { dst = parent->createLeaf(thread,prims,pinfo); delete this; return; } /*! perform best found split and find new splits */ SplitterNormal splitter(thread,&parent->alloc,parent->triangles,parent->vertices,prims,pinfo,split); /*! create an inner node */ BVH2::Node* node = (BVH2::Node*) parent->bvh->alloc.malloc(thread,sizeof(BVH2::Node),1 << BVH2::alignment); node->clear(); dst = BVH2::Base::encodeNode(node); node->set(1,splitter.rinfo.geomBounds,0); node->set(0,splitter.linfo.geomBounds,0); parent->recurse(thread,node->child[0],depth+1,splitter.lprims,splitter.linfo,splitter.lsplit); parent->recurse(thread,node->child[1],depth+1,splitter.rprims,splitter.rinfo,splitter.rsplit); delete this; }
const StrandSplit::Split StrandSplit::find<false>(size_t threadIndex, size_t threadCount, LockStepTaskScheduler* scheduler, BezierRefList& prims) { /* first curve determines first axis */ BezierRefList::block_iterator_unsafe i = prims; Vec3fa axis0 = normalize(i->p3 - i->p0); /* find 2nd axis that is most misaligned with first axis */ float bestCos = 1.0f; Vec3fa axis1 = axis0; for (; i; i++) { Vec3fa axisi = i->p3 - i->p0; float leni = length(axisi); if (leni == 0.0f) continue; axisi /= leni; float cos = abs(dot(axisi,axis0)); if (cos < bestCos) { bestCos = cos; axis1 = axisi; } } /* partition the two strands */ size_t lnum = 0, rnum = 0; BBox3fa lbounds = empty, rbounds = empty; const LinearSpace3fa space0 = frame(axis0).transposed(); const LinearSpace3fa space1 = frame(axis1).transposed(); for (BezierRefList::block_iterator_unsafe i = prims; i; i++) { BezierPrim& prim = *i; const Vec3fa axisi = normalize(prim.p3-prim.p0); const float cos0 = abs(dot(axisi,axis0)); const float cos1 = abs(dot(axisi,axis1)); if (cos0 > cos1) { lnum++; lbounds.extend(prim.bounds(space0)); } else { rnum++; rbounds.extend(prim.bounds(space1)); } } /*! return an invalid split if we do not partition */ if (lnum == 0 || rnum == 0) return Split(inf,axis0,axis1); /*! calculate sah for the split */ const float sah = float(lnum)*halfArea(lbounds) + float(rnum)*halfArea(rbounds); return Split(sah,axis0,axis1); }
__forceinline SpatialSplit::Split SpatialSplit::BinInfo::best(const PrimInfo& pinfo, const Mapping& mapping, const size_t blocks_shift) { /* sweep from right to left and compute parallel prefix of merged bounds */ ssef rAreas[BINS]; ssei rCounts[BINS]; ssei count = 0; BBox3fa bx = empty; BBox3fa by = empty; BBox3fa bz = empty; for (size_t i=BINS-1; i>0; i--) { count += numEnd[i]; rCounts[i] = count; bx.extend(bounds[i][0]); rAreas[i][0] = halfArea(bx); by.extend(bounds[i][1]); rAreas[i][1] = halfArea(by); bz.extend(bounds[i][2]); rAreas[i][2] = halfArea(bz); } /* sweep from left to right and compute SAH */ ssei blocks_add = (1 << blocks_shift)-1; ssei ii = 1; ssef vbestSAH = pos_inf; ssei vbestPos = 0; count = 0; bx = empty; by = empty; bz = empty; for (size_t i=1; i<BINS; i++, ii+=1) { count += numBegin[i-1]; bx.extend(bounds[i-1][0]); float Ax = halfArea(bx); by.extend(bounds[i-1][1]); float Ay = halfArea(by); bz.extend(bounds[i-1][2]); float Az = halfArea(bz); const ssef lArea = ssef(Ax,Ay,Az,Az); const ssef rArea = rAreas[i]; const ssei lCount = (count +blocks_add) >> blocks_shift; const ssei rCount = (rCounts[i]+blocks_add) >> blocks_shift; const ssef sah = lArea*ssef(lCount) + rArea*ssef(rCount); vbestPos = select(sah < vbestSAH,ii ,vbestPos); vbestSAH = select(sah < vbestSAH,sah,vbestSAH); } /* find best dimension */ float bestSAH = inf; int bestDim = -1; int bestPos = 0; for (size_t dim=0; dim<3; dim++) { /* ignore zero sized dimensions */ if (unlikely(mapping.invalid(dim))) continue; /* test if this is a better dimension */ if (vbestSAH[dim] < bestSAH && vbestPos[dim] != 0) { bestDim = dim; bestPos = vbestPos[dim]; bestSAH = vbestSAH[dim]; } } /* return invalid split if no split found */ if (bestDim == -1) return Split(inf,-1,0,mapping); /* return best found split */ return Split(bestSAH,bestDim,bestPos,mapping); }
StrandSplit::TaskFindParallel::TaskFindParallel(size_t threadIndex, size_t threadCount, LockStepTaskScheduler* scheduler, BezierRefList& prims) { /* first curve determines first axis */ BezierRefList::block_iterator_unsafe i = prims; axis0 = axis1 = normalize(i->p3 - i->p0); /* parallel calculation of 2nd axis */ size_t numTasks = min(maxTasks,threadCount); scheduler->dispatchTask(threadIndex,numTasks,_task_bound_parallel,this,numTasks,"build::task_find_parallel"); /* select best 2nd axis */ float bestCos = 1.0f; for (size_t i=0; i<numTasks; i++) { if (task_cos[i] < bestCos) { bestCos = task_cos[i]; axis1 = task_axis1[i]; } } /* parallel calculation of unaligned bounds */ scheduler->dispatchTask(threadIndex,numTasks,_task_bound_parallel,this,numTasks,"build::task_find_parallel"); /* reduce bounds calculates by tasks */ size_t lnum = 0; BBox3fa lbounds = empty; size_t rnum = 0; BBox3fa rbounds = empty; for (size_t i=0; i<numTasks; i++) { lnum += task_lnum[i]; lbounds.extend(task_lbounds[i]); rnum += task_rnum[i]; rbounds.extend(task_rbounds[i]); } /*! return an invalid split if we do not partition */ if (lnum == 0 || rnum == 0) { split = Split(inf,axis0,axis1); return; } /*! calculate sah for the split */ const float sah = float(lnum)*halfArea(lbounds) + float(lnum)*halfArea(rbounds); split = Split(sah,axis0,axis1); }
BVHNStatistics<N>::BVHNStatistics (BVH* bvh) : bvh(bvh) { double A = max(0.0f,halfArea(bvh->getBounds())); if (bvh->msmblur) { NodeRef* roots = (NodeRef*)(size_t)bvh->root; for (size_t i=0; i<bvh->numTimeSteps-1; i++) { const BBox1f t0t1(float(i+0)/float(bvh->numTimeSteps-1), float(i+1)/float(bvh->numTimeSteps-1)); stat = stat + statistics(roots[i],A,t0t1); } } else { stat = statistics(bvh->root,A,BBox1f(0.0f,1.0f)); } }
__forceinline BVH2Builder<Heuristic>::ParallelSplitTask::ParallelSplitTask(const TaskScheduler::ThreadInfo& thread, BVH2Builder* parent, BVH2::Base*& node, size_t depth, atomic_set<PrimRefBlock>& prims, const PrimInfo& pinfo, const Split& split) : parent(parent), dst(node), depth(depth), split(split) { /*! compute leaf and split cost */ const float leafSAH = parent->trity.intCost*pinfo.sah(); const float splitSAH = BVH2::travCost*halfArea(pinfo.geomBounds)+parent->trity.intCost*split.sah(); assert(atomic_set<PrimRefBlock>::block_iterator_unsafe(prims).size() == pinfo.size()); assert(leafSAH >= 0 && splitSAH >= 0); /*! create a leaf node when threshold reached or SAH tells us to stop */ if (pinfo.size() <= 1 || depth > BVH2::maxDepth || (pinfo.size() <= parent->bvh->maxLeafTris && leafSAH <= splitSAH)) { dst = parent->createLeaf(thread,prims,pinfo); delete this; return; } /*! start parallel splitting */ new (&splitter) MultiThreadedSplitterNormal(thread, &parent->alloc,parent->triangles,parent->vertices,prims,pinfo,split, (TaskScheduler::completeFunction)_createNode,this); }
int BVH2Builder::BuildTask::recurse(size_t depth, ObjectBinning<2>& job) { /*! compute leaf and split cost */ size_t N = job.size(); float leafSAH = BVH2<Triangle4>::intCost*job.leafSAH; float splitSAH = BVH2<Triangle4>::travCost*halfArea(job.geomBounds)+BVH2<Triangle4>::intCost*job.splitSAH; /*! make leaf node when threshold reached or SAH tells us */ if (N <= 1 || depth > BVH2<Triangle4>::maxDepth || (N <= BVH2<Triangle4>::maxLeafSize && leafSAH < splitSAH)) return parent->bvh->createLeaf(parent->prims,parent->triangles,parent->threadAllocPrimitives(tid,blocks(N)),job.start(),N); /*! perform split */ ObjectBinning<2> left,right; job.split(parent->prims,left,right); /*! create an inner node */ int nodeID = (int)parent->threadAllocNodes(tid,1); BVH2<Triangle4>::Node& node = parent->bvh->nodes[nodeID].clear(); node.set(0,left .geomBounds,recurse(depth+1,left )); node.set(1,right.geomBounds,recurse(depth+1,right)); return BVH2<Triangle4>::id2offset(nodeID); }
typename BVHNStatistics<N>::Statistics BVHNStatistics<N>::statistics(NodeRef node, const double A, const BBox1f t0t1) { Statistics s; double dt = max(0.0f,t0t1.size()); if (node.isAlignedNode()) { AlignedNode* n = node.alignedNode(); for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; s.statAlignedNodes.numChildren++; const double Ai = max(0.0f,halfArea(n->extend(i))); s = s + statistics(n->child(i),Ai,t0t1); } s.statAlignedNodes.numNodes++; s.statAlignedNodes.nodeSAH += dt*A; s.depth++; } else if (node.isUnalignedNode()) { UnalignedNode* n = node.unalignedNode(); for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; s.statUnalignedNodes.numChildren++; const double Ai = max(0.0f,halfArea(n->extend(i))); s = s + statistics(n->child(i),Ai,t0t1); } s.statUnalignedNodes.numNodes++; s.statUnalignedNodes.nodeSAH += dt*A; s.depth++; } else if (node.isAlignedNodeMB()) { AlignedNodeMB* n = node.alignedNodeMB(); for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; s.statAlignedNodesMB.numChildren++; const double Ai = max(0.0f,halfArea(n->extend0(i))); s = s + statistics(n->child(i),Ai,t0t1); } s.statAlignedNodesMB.numNodes++; s.statAlignedNodesMB.nodeSAH += dt*A; s.depth++; } else if (node.isUnalignedNodeMB()) { UnalignedNodeMB* n = node.unalignedNodeMB(); for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; s.statUnalignedNodesMB.numChildren++; const double Ai = max(0.0f,halfArea(n->extend0(i))); s = s + statistics(n->child(i),Ai,t0t1); } s.statUnalignedNodesMB.numNodes++; s.statUnalignedNodesMB.nodeSAH += dt*A; s.depth++; } else if (node.isTransformNode()) { s.statTransformNodes.numNodes++; s.statTransformNodes.nodeSAH += dt*A; s.depth++; } else if (node.isQuantizedNode()) { QuantizedNode* n = node.quantizedNode(); for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; s.statQuantizedNodes.numChildren++; const double Ai = max(0.0f,halfArea(n->extend(i))); s = s + statistics(n->child(i),Ai,t0t1); } s.statQuantizedNodes.numNodes++; s.statQuantizedNodes.nodeSAH += dt*A; s.depth++; } else if (node.isLeaf()) { size_t num; const char* tri = node.leaf(num); if (num) { for (size_t i=0; i<num; i++) { s.statLeaf.numPrims += bvh->primTy.size(tri+i*bvh->primTy.bytes); } s.statLeaf.numLeaves++; s.statLeaf.numPrimBlocks += num; s.statLeaf.leafSAH += dt*A*num; if (num-1 < Statistics::LeafStat::NHIST) { s.statLeaf.numPrimBlocksHistogram[num-1]++; } } } else { throw std::runtime_error("not supported node type in bvh_statistics"); } return s; }
void BVHNStatistics<N>::statistics(NodeRef node, const float A, size_t& depth) { if (node.isNode()) { numAlignedNodes++; AlignedNode* n = node.node(); bvhSAH += A*travCostAligned; depth = 0; for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; childrenAlignedNodes++; const float Ai = max(0.0f,halfArea(n->extend(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; } else if (node.isUnalignedNode()) { numUnalignedNodes++; UnalignedNode* n = node.unalignedNode(); bvhSAH += A*travCostUnaligned; depth = 0; for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; childrenUnalignedNodes++; const float Ai = max(0.0f,halfArea(n->extend(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; } else if (node.isNodeMB()) { numAlignedNodesMB++; AlignedNodeMB* n = node.nodeMB(); bvhSAH += A*travCostAligned; depth = 0; for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; childrenAlignedNodesMB++; const float Ai = max(0.0f,halfArea(n->extend0(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; } else if (node.isUnalignedNodeMB()) { numUnalignedNodesMB++; UnalignedNodeMB* n = node.unalignedNodeMB(); bvhSAH += A*travCostUnaligned; depth = 0; for (size_t i=0; i<N; i++) { if (n->child(i) == BVH::emptyNode) continue; childrenUnalignedNodesMB++; const float Ai = max(0.0f,halfArea(n->extend0(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; } else if (node.isTransformNode()) { numTransformNodes++; TransformNode* n = node.transformNode(); bvhSAH += A*travCostTransform; depth = 0; const BBox3fa worldBounds = xfmBounds(n->local2world,n->localBounds); const float Ai = max(0.0f,halfArea(worldBounds)); //size_t cdepth; statistics(n->child,Ai,cdepth); //depth=max(depth,cdepth)+1; } else { depth = 0; size_t num; const char* tri = node.leaf(num); if (!num) return; numLeaves++; numPrimBlocks += num; for (size_t i=0; i<num; i++) numPrims += bvh->primTy.size(tri+i*bvh->primTy.bytes); float sah = A * intCost * num; leafSAH += sah; } }
void BVH4Statistics::statistics(NodeRef node, const float A, size_t& depth) { if (node.isNode()) { hash += 0x1234; numAlignedNodes++; AlignedNode* n = node.node(); bvhSAH += A*BVH4::travCostAligned; depth = 0; for (size_t i=0; i<BVH4::N; i++) { if (n->child(i) == BVH4::emptyNode) continue; childrenAlignedNodes++; const float Ai = max(0.0f,halfArea(n->extend(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; hash += 0x76767*depth; } else if (node.isUnalignedNode()) { hash += 0x1232344; numUnalignedNodes++; UnalignedNode* n = node.unalignedNode(); bvhSAH += A*BVH4::travCostUnaligned; depth = 0; for (size_t i=0; i<BVH4::N; i++) { if (n->child(i) == BVH4::emptyNode) continue; childrenUnalignedNodes++; const float Ai = max(0.0f,halfArea(n->extend(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; hash += 0x76767*depth; } else if (node.isNodeMB()) { hash += 0xEF343; numAlignedNodesMB++; BVH4::NodeMB* n = node.nodeMB(); bvhSAH += A*BVH4::travCostAligned; depth = 0; for (size_t i=0; i<BVH4::N; i++) { if (n->child(i) == BVH4::emptyNode) continue; childrenAlignedNodesMB++; const float Ai = max(0.0f,halfArea(n->extend0(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; hash += 0x76767*depth; } else if (node.isUnalignedNodeMB()) { hash += 0x1EEF4; numUnalignedNodesMB++; BVH4::UnalignedNodeMB* n = node.unalignedNodeMB(); bvhSAH += A*BVH4::travCostUnaligned; depth = 0; for (size_t i=0; i<BVH4::N; i++) { if (n->child(i) == BVH4::emptyNode) continue; childrenUnalignedNodesMB++; const float Ai = max(0.0f,halfArea(n->extend0(i))); size_t cdepth; statistics(n->child(i),Ai,cdepth); depth=max(depth,cdepth); } depth++; hash += 0x76767*depth; } else { depth = 0; size_t num; const char* tri = node.leaf(num); hash += 0xDD776*num+0x878; if (!num) return; hash += bvh->primTy.hash(tri,num); numLeaves++; numPrims += num; float sah = A * BVH4::intCost * num; bvhSAH += sah; } }