unsafe void PairTest(int workerIndex) { Debug.Assert(!disposed); int nextNodePairIndex; //To minimize the number of worker overlap lists, perform direct load balancing by manually grabbing the next indices. while ((nextNodePairIndex = Interlocked.Increment(ref NextNodePair)) < NodePairsToTest.Count) { var overlap = NodePairsToTest[nextNodePairIndex]; if (overlap.A >= 0) { if (overlap.A == overlap.B) { //Same node. Tree.GetOverlapsInNode2(Tree.nodes + overlap.A, ref WorkerOverlaps[workerIndex]); } else if (overlap.B >= 0) { //Different nodes. Tree.GetOverlapsBetweenDifferentNodes2(Tree.nodes + overlap.A, Tree.nodes + overlap.B, ref WorkerOverlaps[workerIndex]); } else { //A is an internal node, B is a leaf. var leafIndex = Tree.Encode(overlap.B); var leaf = Tree.leaves + leafIndex; Tree.TestLeafAgainstNode2(leafIndex, ref (&Tree.nodes[leaf->NodeIndex].A)[leaf->ChildIndex], overlap.A, ref WorkerOverlaps[workerIndex]); } } else { //A is a leaf, B is internal. var leafIndex = Tree.Encode(overlap.A); var leaf = Tree.leaves + leafIndex; Tree.TestLeafAgainstNode2(leafIndex, ref (&Tree.nodes[leaf->NodeIndex].A)[leaf->ChildIndex], overlap.B, ref WorkerOverlaps[workerIndex]); //NOTE THAT WE DO NOT HANDLE THE CASE THAT BOTH A AND B ARE LEAVES HERE. //The collection routine should take care of that, since it has more convenient access to bounding boxes and because a single test isn't worth an atomic increment. } } }
unsafe void RefitAndMark(int workerIndex) { int refitIndex; while ((refitIndex = Interlocked.Increment(ref RefitNodeIndex)) < RefitNodes.Count) { var nodeIndex = RefitNodes.Elements[refitIndex]; bool shouldUseMark; if (nodeIndex < 0) { //Node was already marked as a wavefront. Should proceed with a RefitAndMeasure instead of RefitAndMark. nodeIndex = Tree.Encode(nodeIndex); shouldUseMark = false; } else { shouldUseMark = true; } var node = Tree.nodes + nodeIndex; Debug.Assert(node->Parent >= 0, "The root should not be marked for refit."); var parent = Tree.nodes + node->Parent; var boundingBoxInParent = &parent->A + node->IndexInParent; if (shouldUseMark) { var costChange = Tree.RefitAndMark(nodeIndex, LeafCountThreshold, ref RefinementCandidates.Elements[workerIndex], ref *boundingBoxInParent); node->LocalCostChange = costChange; } else { var costChange = Tree.RefitAndMeasure(nodeIndex, ref *boundingBoxInParent); node->LocalCostChange = costChange; } //int foundLeafCount; //Tree.Validate(RefitNodes.Elements[refitNodeIndex], node->Parent, node->IndexInParent, ref *boundingBoxInParent, out foundLeafCount); //Walk up the tree. node = parent; while (true) { if (Interlocked.Decrement(ref node->RefineFlag) == 0) { //Compute the child contributions to this node's volume change. var children = &node->ChildA; node->LocalCostChange = 0; for (int i = 0; i < node->ChildCount; ++i) { if (children[i] >= 0) { var child = Tree.nodes + children[i]; node->LocalCostChange += child->LocalCostChange; //Clear the refine flag (unioned). child->RefineFlag = 0; } } //This thread is the last thread to visit this node, so it must handle this node. //Merge all the child bounding boxes into one. if (node->Parent < 0) { //Root node. //Don't bother including the root's change in volume. //Refinement can't change the root's bounds, so the fact that the world got bigger or smaller //doesn't really have any bearing on how much refinement should be done. //We do, however, need to divide by root volume so that we get the change in cost metric rather than volume. var merged = new BoundingBox { Min = new Vector3(float.MaxValue), Max = new Vector3(float.MinValue) }; var bounds = &node->A; for (int i = 0; i < node->ChildCount; ++i) { BoundingBox.Merge(ref bounds[i], ref merged, out merged); } var postmetric = ComputeBoundsMetric(ref merged); if (postmetric > 1e-9f) { RefitCostChange = node->LocalCostChange / postmetric; } else { RefitCostChange = 0; } //Clear the root's refine flag (unioned). node->RefineFlag = 0; break; } else { parent = Tree.nodes + node->Parent; boundingBoxInParent = &parent->A + node->IndexInParent; var premetric = ComputeBoundsMetric(ref *boundingBoxInParent); * boundingBoxInParent = new BoundingBox { Min = new Vector3(float.MaxValue), Max = new Vector3(float.MinValue) }; var bounds = &node->A; for (int i = 0; i < node->ChildCount; ++i) { BoundingBox.Merge(ref bounds[i], ref *boundingBoxInParent, out *boundingBoxInParent); } var postmetric = ComputeBoundsMetric(ref *boundingBoxInParent); node->LocalCostChange += postmetric - premetric; node = parent; } } else { //This thread wasn't the last to visit this node, so it should die. Some other thread will handle it later. break; } } } }