public BroadPhase(BufferPool pool, int initialActiveLeafCapacity = 4096, int initialStaticLeafCapacity = 8192) { Pool = pool; ActiveTree = new Tree(pool, initialActiveLeafCapacity); StaticTree = new Tree(pool, initialStaticLeafCapacity); pool.TakeAtLeast(initialActiveLeafCapacity, out activeLeaves); pool.TakeAtLeast(initialStaticLeafCapacity, out staticLeaves); activeRefineContext = new Tree.RefitAndRefineMultithreadedContext(); staticRefineContext = new Tree.RefitAndRefineMultithreadedContext(); }
public static void Test() { var pool = new BufferPool(); var tree = new Tree(pool, 128); const int leafCountAlongXAxis = 11; const int leafCountAlongYAxis = 13; const int leafCountAlongZAxis = 15; var leafCount = leafCountAlongXAxis * leafCountAlongYAxis * leafCountAlongZAxis; pool.Take <BoundingBox>(leafCount, out var leafBounds); const float boundsSpan = 2; const float spanRange = 2; const float boundsSpacing = 3; var random = new Random(5); for (int i = 0; i < leafCountAlongXAxis; ++i) { for (int j = 0; j < leafCountAlongYAxis; ++j) { for (int k = 0; k < leafCountAlongZAxis; ++k) { var index = leafCountAlongXAxis * leafCountAlongYAxis * k + leafCountAlongXAxis * j + i; leafBounds[index].Min = new Vector3(i, j, k) * boundsSpacing; leafBounds[index].Max = leafBounds[index].Min + new Vector3(boundsSpan) + spanRange * new Vector3((float)random.NextDouble(), (float)random.NextDouble(), (float)random.NextDouble()); } } } var prebuiltCount = Math.Max(leafCount / 2, 1); tree.SweepBuild(pool, leafBounds.Slice(0, prebuiltCount)); tree.Validate(); for (int i = prebuiltCount; i < leafCount; ++i) { tree.Add(ref leafBounds[i], pool); } tree.Validate(); pool.Take <int>(leafCount, out var handleToLeafIndex); pool.Take <int>(leafCount, out var leafIndexToHandle); for (int i = 0; i < leafCount; ++i) { handleToLeafIndex[i] = i; leafIndexToHandle[i] = i; } const int iterations = 100000; const int maximumChangesPerIteration = 20; var threadDispatcher = new SimpleThreadDispatcher(Environment.ProcessorCount); var refineContext = new Tree.RefitAndRefineMultithreadedContext(); var selfTestContext = new Tree.MultithreadedSelfTest <OverlapHandler>(pool); var overlapHandlers = new OverlapHandler[threadDispatcher.ThreadCount]; Action <int> pairTestAction = selfTestContext.PairTest; QuickList <int, Buffer <int> > .Create(pool.SpecializeFor <int>(), leafCount, out var removedLeafHandles); for (int i = 0; i < iterations; ++i) { var changeCount = random.Next(maximumChangesPerIteration); for (int j = 0; j <= changeCount; ++j) { var addedFraction = tree.LeafCount / (float)leafCount; if (random.NextDouble() < addedFraction) { //Remove a leaf. var leafIndexToRemove = random.Next(tree.LeafCount); var handleToRemove = leafIndexToHandle[leafIndexToRemove]; var movedLeafIndex = tree.RemoveAt(leafIndexToRemove); if (movedLeafIndex >= 0) { var movedHandle = leafIndexToHandle[movedLeafIndex]; handleToLeafIndex[movedHandle] = leafIndexToRemove; leafIndexToHandle[leafIndexToRemove] = movedHandle; leafIndexToHandle[movedLeafIndex] = -1; } else { //The removed leaf was the last one. This leaf index is no longer associated with any existing leaf. leafIndexToHandle[leafIndexToRemove] = -1; } handleToLeafIndex[handleToRemove] = -1; removedLeafHandles.AddUnsafely(handleToRemove); tree.Validate(); } else { //Add a leaf. var indexInRemovedList = random.Next(removedLeafHandles.Count); var handleToAdd = removedLeafHandles[indexInRemovedList]; removedLeafHandles.FastRemoveAt(indexInRemovedList); var leafIndex = tree.Add(ref leafBounds[handleToAdd], pool); leafIndexToHandle[leafIndex] = handleToAdd; handleToLeafIndex[handleToAdd] = leafIndex; tree.Validate(); } } tree.Refit(); tree.Validate(); tree.RefitAndRefine(pool, i); tree.Validate(); var handler = new OverlapHandler(); tree.GetSelfOverlaps(ref handler); tree.Validate(); refineContext.RefitAndRefine(ref tree, pool, threadDispatcher, i); tree.Validate(); for (int k = 0; k < threadDispatcher.ThreadCount; ++k) { overlapHandlers[k] = new OverlapHandler(); } selfTestContext.PrepareJobs(ref tree, overlapHandlers, threadDispatcher.ThreadCount); threadDispatcher.DispatchWorkers(pairTestAction); selfTestContext.CompleteSelfTest(); tree.Validate(); if (i % 50 == 0) { Console.WriteLine($"Cost: {tree.MeasureCostMetric()}"); Console.WriteLine($"Cache Quality: {tree.MeasureCacheQuality()}"); Console.WriteLine($"Overlap Count: {handler.OverlapCount}"); } } threadDispatcher.Dispose(); pool.Clear(); }
public unsafe static TestResults TestSingleArray(TestCollidable[] leaves, BoundingBox[] queries, BoundingBox positionBounds, int queryCount, int selfTestCount, int refitCount, int frameCount, float dt, ParallelLooper looper) { { var warmLeaves = GetLeaves(10, 10, 10, 10, 10); Tree tree = new Tree(); //for (int i = 0; i < leaves.Length; ++i) //{ // BoundingBox box; // leaves[i].GetBoundingBox(out box); // //tree.Insert(i, ref box); // tree.AddGlobal(i, ref box); //} int[] leafIds = new int[warmLeaves.Length]; BoundingBox[] leafBounds = new BoundingBox[warmLeaves.Length]; for (int i = 0; i < warmLeaves.Length; ++i) { leafIds[i] = i; warmLeaves[i].GetBoundingBox(out leafBounds[i]); } //tree.BuildMedianSplit(leafIds, leafBounds); //tree.BuildVolumeHeuristic(leafIds, leafBounds); tree.SweepBuild(leafIds, leafBounds); Console.WriteLine($"SingleArray Cachewarm Build: {tree.LeafCount}"); tree.Refit(); //tree.BottomUpAgglomerativeRefine(); //tree.TopDownAgglomerativeRefine(); //tree.BottomUpSweepRefine(); //tree.TopDownSweepRefine(); tree.RefitAndRefine(0); var context = new Tree.RefitAndRefineMultithreadedContext(tree); tree.RefitAndRefine(0, looper, context); var selfTestContext = new Tree.SelfTestMultithreadedContext(looper.ThreadCount, BufferPools <Overlap> .Locking); tree.GetSelfOverlaps(looper, selfTestContext); var list = new QuickList <int>(new BufferPool <int>()); BoundingBox aabb = new BoundingBox { Min = new Vector3(0, 0, 0), Max = new Vector3(1, 1, 1) }; tree.QueryRecursive(ref aabb, ref list); list.Dispose(); var overlaps = new QuickList <Overlap>(new BufferPool <Overlap>()); tree.GetSelfOverlaps(ref overlaps); overlaps = new QuickList <Overlap>(new BufferPool <Overlap>()); tree.GetSelfOverlapsArityDedicated(ref overlaps); tree.IncrementalCacheOptimize(0); overlaps = new QuickList <Overlap>(new BufferPool <Overlap>()); tree.GetSelfOverlapsViaQueries(ref overlaps); Console.WriteLine($"Cachewarm overlaps: {overlaps.Count}"); tree.Dispose(); } { Console.WriteLine($"SingleArray arity: {Tree.ChildrenCapacity}"); Tree tree = new Tree(Math.Max(1, leaves.Length)); var startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < leaves.Length; ++i) { var leafIndex = (int)((982451653L * i) % leaves.Length); BoundingBox box; leaves[leafIndex].GetBoundingBox(out box); tree.Add(leafIndex, ref box); //tree.AddGlobal(leafIndex, ref box); } //int[] leafIds = new int[leaves.Length]; //BoundingBox[] leafBounds = new BoundingBox[leaves.Length]; //for (int i = 0; i < leaves.Length; ++i) //{ // leafIds[i] = i; // leaves[i].GetBoundingBox(out leafBounds[i]); //} ////tree.BuildMedianSplit(leafIds, leafBounds); ////tree.BuildVolumeHeuristic(leafIds, leafBounds); //tree.SweepBuild(leafIds, leafBounds); var endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray Build Time: {endTime - startTime}, depth: {tree.ComputeMaximumDepth()}"); int nodeCount, childCount; tree.MeasureNodeOccupancy(out nodeCount, out childCount); Console.WriteLine($"SingleArray Occupancy: {childCount / (double)nodeCount}"); Console.WriteLine($"Cost metric: {tree.MeasureCostMetric()}"); Console.WriteLine($"Cache Quality: {tree.MeasureCacheQuality()}"); tree.Validate(); startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < refitCount; ++i) { //for (int i = 0; i < tree.LeafCount; ++i) //{ // BoundingBox box; // leaves[tree.Leaves[i].Id].GetBoundingBox(out box); // tree.UpdateLeafBoundingBox(i, ref box); //} tree.Refit(); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray Refit Time1: {endTime - startTime}"); var overlaps = new QuickList <Overlap>(new BufferPool <Overlap>()); startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < selfTestCount; ++i) { overlaps.Count = 0; tree.GetSelfOverlaps(ref overlaps); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray SelfTree Time1: {endTime - startTime}, overlaps: {overlaps.Count}"); int[] buffer; MemoryRegion region; BinnedResources resources; const int maximumSubtrees = 262144; var spareNodes = new QuickList <int>(new BufferPool <int>(), 8); var subtreeReferences = new QuickList <int>(BufferPools <int> .Thread, BufferPool <int> .GetPoolIndex(maximumSubtrees)); var treeletInternalNodes = new QuickList <int>(BufferPools <int> .Thread, BufferPool <int> .GetPoolIndex(maximumSubtrees)); Tree.CreateBinnedResources(BufferPools <int> .Thread, maximumSubtrees, out buffer, out region, out resources); bool nodesInvalidated; overlaps = new QuickList <Overlap>(new BufferPool <Overlap>()); var refineContext = new Tree.RefitAndRefineMultithreadedContext(tree); var selfTestContext = new Tree.SelfTestMultithreadedContext(looper.ThreadCount, BufferPools <Overlap> .Locking); var visitedNodes = new QuickSet <int>(BufferPools <int> .Thread, BufferPools <int> .Thread); //**************** Dynamic Testing Random random = new Random(5); TestResults results = new TestResults("New", frameCount); startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int t = 0; t < frameCount; ++t) { //Update the positions of objects. for (int i = 0; i < tree.LeafCount; ++i) { var leafId = tree.Leaves[i].Id; var leaf = leaves[leafId]; //Bounce off the walls. if (leaf.Position.X < positionBounds.Min.X && leaf.Velocity.X < 0) { leaf.Velocity.X = -leaf.Velocity.X; } if (leaf.Position.Y < positionBounds.Min.Y && leaf.Velocity.Y < 0) { leaf.Velocity.Y = -leaf.Velocity.Y; } if (leaf.Position.Z < positionBounds.Min.Z && leaf.Velocity.Z < 0) { leaf.Velocity.Z = -leaf.Velocity.Z; } if (leaf.Position.X > positionBounds.Max.X && leaf.Velocity.X > 0) { leaf.Velocity.X = -leaf.Velocity.X; } if (leaf.Position.Y > positionBounds.Max.Y && leaf.Velocity.Y > 0) { leaf.Velocity.Y = -leaf.Velocity.Y; } if (leaf.Position.Z > positionBounds.Max.Z && leaf.Velocity.Z > 0) { leaf.Velocity.Z = -leaf.Velocity.Z; } leaf.Position += leaf.Velocity * dt; BoundingBox boundingBox; leaf.GetBoundingBox(out boundingBox); tree.SetLeafBoundingBox(i, ref boundingBox); } var refineStartTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; int refinementCount; if (looper.ThreadCount > 1) { refinementCount = tree.RefitAndRefine(t, looper, refineContext); } else { refinementCount = tree.RefitAndRefine(t); } var refineEndTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; int overlapsCount; if (looper.ThreadCount > 1) { tree.GetSelfOverlaps(looper, selfTestContext); overlapsCount = 0; for (int i = 0; i < selfTestContext.WorkerOverlaps.Length; ++i) { overlapsCount += selfTestContext.WorkerOverlaps[i].Count; } } else { overlaps.Count = 0; tree.GetSelfOverlapsArityDedicated(ref overlaps); overlapsCount = overlaps.Count; } var testEndTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; results.Refine[t] = 1000 * (refineEndTime - refineStartTime); results.SelfTest[t] = 1000 * (testEndTime - refineEndTime); results.Total[t] = 1000 * (testEndTime - refineStartTime); results.OverlapCounts[t] = overlapsCount; results.TreeCosts[t] = tree.MeasureCostMetric(); if (t % 16 == 0) { Console.WriteLine($"_________________{t}_________________"); Console.WriteLine($"Refinement count: {refinementCount}"); Console.WriteLine($"Refine time: {results.Refine[t]}"); Console.WriteLine($"Test time: {results.SelfTest[t]}"); Console.WriteLine($"TIME: {results.Total[t]}"); Console.WriteLine($"Cost metric: {results.TreeCosts[t]}"); Console.WriteLine($"Overlaps: {results.OverlapCounts[t]}"); Console.WriteLine($"Cache Quality: {tree.MeasureCacheQuality()}"); GC.Collect(); } tree.Validate(); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; tree.Validate(); Console.WriteLine($"SingleArray Cache Quality: {tree.MeasureCacheQuality()}"); Console.WriteLine($"Cost metric: {tree.MeasureCostMetric()}"); region.Dispose(); tree.RemoveUnusedInternalNodes(ref spareNodes); BufferPools <int> .Thread.GiveBack(buffer); //******************** tree.MeasureNodeOccupancy(out nodeCount, out childCount); Console.WriteLine($"SingleArray Occupancy: {childCount / (double)nodeCount}"); startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < refitCount; ++i) { //for (int i = 0; i < tree.LeafCount; ++i) //{ // BoundingBox box; // leaves[tree.Leaves[i].Id].GetBoundingBox(out box); // tree.UpdateLeafBoundingBox(i, ref box); //} tree.Refit(); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray Refit Time2: {endTime - startTime}"); var list = new QuickList <int>(new BufferPool <int>()); var queryMask = queries.Length - 1; startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < queryCount; ++i) { list.Count = 0; //tree.Query2(ref queries[i & queryMask], ref list); tree.QueryRecursive(ref queries[i & queryMask], ref list); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray Query Time: {endTime - startTime}, overlaps: {list.Count}"); Array.Clear(list.Elements, 0, list.Elements.Length); list.Dispose(); startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < selfTestCount; ++i) { overlaps.Count = 0; tree.GetSelfOverlaps(ref overlaps); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray SelfTree Time: {endTime - startTime}, overlaps: {overlaps.Count}"); startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < selfTestCount; ++i) { overlaps.Count = 0; tree.GetSelfOverlapsArityDedicated(ref overlaps); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray Arity-Dedicated SelfTree Time: {endTime - startTime}, overlaps: {overlaps.Count}"); startTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; for (int i = 0; i < selfTestCount; ++i) { overlaps.Count = 0; tree.GetSelfOverlapsViaQueries(ref overlaps); } endTime = Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency; Console.WriteLine($"SingleArray SelfQuery Time: {endTime - startTime}, overlaps: {overlaps.Count}"); tree.Dispose(); return(results); } }