/// <summary> /// Removes redundant points. Two points are redundant if they occupy the same hash grid cell. /// </summary> /// <param name="points">List of points to prune.</param> /// <param name="cellSize">Size of cells to determine redundancy.</param> public static void RemoveRedundantPoints(ref QuickList <Vector3> points, double cellSize) { var set = new QuickSet <Int3>(BufferPools <Int3> .Locking, BufferPools <int> .Locking, BufferPool.GetPoolIndex(points.Count)); for (int i = points.Count - 1; i >= 0; --i) { var element = points.Elements[i]; var cell = new Int3 { X = (int)Math.Floor(element.X / cellSize), Y = (int)Math.Floor(element.Y / cellSize), Z = (int)Math.Floor(element.Z / cellSize) }; if (set.Contains(cell)) { points.FastRemoveAt(i); } else { set.Add(cell); //TODO: Consider adding adjacent cells to guarantee that a point on the border between two cells will still detect the presence //of a point on the opposite side of that border. } } set.Dispose(); }
unsafe static void TestQuickInlining() { { var pool = new PassthroughArrayPool <double>(); var intPool = new PassthroughArrayPool <int>(); QuickSet <double, Array <double>, Array <int>, PrimitiveComparer <double> > .Create(pool, intPool, 2, 3, out var set); set.AddUnsafely(5); var item = set[0]; Console.WriteLine($"Managed Item: {item}"); var comparer = default(PrimitiveComparer <double>); var hash = comparer.Hash(ref item); Console.WriteLine($"Hash: {hash}"); } { var pool = new BufferPool().SpecializeFor <int>(); QuickSet <int, Buffer <int>, Buffer <int>, PrimitiveComparer <int> > .Create(pool, pool, 2, 3, out var set); set.AddUnsafely(5); var item = set[0]; pool.Raw.Clear(); } }
public BoundarySets(int sizePower) { BlockedVertexRegions = new QuickSet <int>(BufferPools <int> .Thread, BufferPools <int> .Thread, sizePower); BlockedEdgeRegions = new QuickSet <Edge>(BufferPools <Edge> .Thread, BufferPools <int> .Thread, sizePower); EdgeContacts = new QuickList <EdgeContact>(BufferPools <EdgeContact> .Thread, sizePower); VertexContacts = new QuickList <VertexContact>(BufferPools <VertexContact> .Thread, sizePower); }
/// <summary> /// Removes redundant points. Two points are redundant if they occupy the same hash grid cell. /// </summary> /// <param name="points">List of points to prune.</param> /// <param name="cellSize">Size of cells to determine redundancy.</param> public static void RemoveRedundantPoints(ref QuickList<Vector3> points, double cellSize) { var set = new QuickSet<Int3>(BufferPools<Int3>.Locking, BufferPools<int>.Locking, BufferPool.GetPoolIndex(points.Count)); for (int i = points.Count - 1; i >= 0; --i) { var element = points.Elements[i]; var cell = new Int3 { X = (int)Math.Floor(element.X / cellSize), Y = (int)Math.Floor(element.Y / cellSize), Z = (int)Math.Floor(element.Z / cellSize) }; if (set.Contains(cell)) { points.FastRemoveAt(i); } else { set.Add(cell); //TODO: Consider adding adjacent cells to guarantee that a point on the border between two cells will still detect the presence //of a point on the opposite side of that border. } } set.Dispose(); }
unsafe void Allocate <TBodyReferenceGetter>(int constraintHandle, ref int constraintBodyHandles, int bodyCount, Bodies3D bodies3D, int typeId, BufferPool pool, TBodyReferenceGetter bodyReferenceGetter, int minimumBodyCapacity, int minimumReferenceCapacity) where TBodyReferenceGetter : struct, IBodyReferenceGetter { EnsureCapacity(Math.Max(bodyConstraintReferences.Count + bodyCount, minimumBodyCapacity), pool); for (int i = 0; i < bodyCount; ++i) { var bodyReference = bodyReferenceGetter.GetBodyReference(bodies3D, Unsafe.Add(ref constraintBodyHandles, i)); var bodyAlreadyListed = bodyConstraintReferences.GetTableIndices(ref bodyReference, out var tableIndex, out var elementIndex); //If an entry for this body does not yet exist, we'll create one. if (!bodyAlreadyListed) { elementIndex = bodyConstraintReferences.Count; } ref var constraintReferences = ref bodyConstraintReferences.Values[elementIndex]; if (!bodyAlreadyListed) { //The body is not already contained. Create a list for it. constraintReferences = new QuickSet <FallbackReference, FallbackReferenceComparer>(minimumReferenceCapacity, pool); bodyConstraintReferences.Keys[elementIndex] = bodyReference; bodyConstraintReferences.Table[tableIndex] = elementIndex + 1; ++bodyConstraintReferences.Count; } var fallbackReference = new FallbackReference { ConstraintHandle = constraintHandle, IndexInConstraint = i }; constraintReferences.AddRef(ref fallbackReference, pool); }
internal IEnumerable <TElement> ResolveEnumerable <TElement>(Func <Type, string, ImplicitRegistration, object> resolve, string name) { TElement value; var set = new QuickSet <Type>(); int hash = typeof(TElement).GetHashCode(); // Iterate over hierarchy for (var container = this; null != container; container = container._parent) { // Skip to parent if no data if (null == container._metadata) { continue; } // Hold on to registries var registry = container._registry; // Get indexes and iterate over them var length = container._metadata.GetEntries <TElement>(hash, out int[] data); for (var i = 1; i < length; i++) { var index = data[i]; if (set.Add(registry.Entries[index].HashCode, registry.Entries[index].Key.Type)) { try { var registration = (ExplicitRegistration)registry.Entries[index].Value; value = (TElement)resolve(typeof(TElement), registry.Entries[index].Key.Name, registration); } catch (ArgumentException ex) when(ex.InnerException is TypeLoadException) { continue; } yield return(value); } } } // If nothing registered attempt to resolve the type if (0 == set.Count) { try { var registration = GetRegistration(typeof(TElement), name); value = (TElement)resolve(typeof(TElement), name, registration); } catch { yield break; } yield return(value); } }
public MeshCache(Device device, BufferPool pool, int initialSizeInVertices = 1 << 22) { Pool = pool; pool.TakeAtLeast(initialSizeInVertices, out vertices); TriangleBuffer = new StructuredBuffer <Vector3>(device, initialSizeInVertices, "Mesh Cache Vertex Buffer"); allocator = new Allocator(initialSizeInVertices, pool); pendingUploads = new QuickList <UploadRequest>(128, pool); requestedIds = new QuickList <ulong>(128, pool); previouslyAllocatedIds = new QuickSet <ulong, PrimitiveComparer <ulong> >(256, pool); }
public MeshCache(Device device, BufferPool pool, int initialSizeInVertices = 1 << 22) { Pool = pool; pool.Take(initialSizeInVertices, out vertices); TriangleBuffer = new StructuredBuffer <Vector3>(device, initialSizeInVertices, "Mesh Cache Vertex Buffer"); allocator = new Allocator(initialSizeInVertices); QuickList <UploadRequest, Buffer <UploadRequest> > .Create(pool.SpecializeFor <UploadRequest>(), 128, out pendingUploads); QuickList <ulong, Buffer <ulong> > .Create(pool.SpecializeFor <ulong>(), 128, out requestedIds); QuickSet <ulong, Buffer <ulong>, Buffer <int>, PrimitiveComparer <ulong> > .Create(pool.SpecializeFor <ulong>(), pool.SpecializeFor <int>(), 8, 3, out previouslyAllocatedIds); }
internal IEnumerable <TElement> ComplexArray <TElement>(Func <Type, IRegistration, object?> resolve, Type type) { object?value; var set = new QuickSet(); var key = new HashKey(typeof(TElement)); var typeKey = new HashKey(type); // Iterate over hierarchy for (UnityContainer?container = this; null != container; container = container._parent) { // Skip to parent if no data if (null == container._metadata) { continue; } // Hold on to registries Debug.Assert(null != container._registry); var registry = container._registry; // Get indexes and iterate over them var length = container._metadata.GetMeta(ref typeKey, out int[]? data); if (null != data) { for (var i = 1; i < length; i++) { var index = data[i]; var registration = (ExplicitRegistration)registry.Entries[index].Policies; if (null != registration.Name && set.Add(registration.Name)) { try { var itemKey = new HashKey(typeof(TElement), registration.Name); var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name, registration); value = resolve(typeof(TElement), item); } catch (ArgumentException ex) when(ex.InnerException is TypeLoadException) { continue; } #pragma warning disable CS8601 // Possible null reference assignment. yield return((TElement)value); #pragma warning restore CS8601 // Possible null reference assignment. } } } } }
/// <summary> /// Identifies the points on the surface of hull. /// </summary> /// <param name="points">List of points in the set.</param> /// <param name="outputTriangleIndices">List of indices into the input point set composing the triangulated surface of the convex hull. /// Each group of 3 indices represents a triangle on the surface of the hull.</param> /// <param name="outputSurfacePoints">Unique points on the surface of the convex hull.</param> public static void GetConvexHull(ref QuickList <Vector3> points, ref QuickList <int> outputTriangleIndices, IList <Vector3> outputSurfacePoints) { GetConvexHull(ref points, ref outputTriangleIndices); var alreadyContainedIndices = new QuickSet <int>(BufferPools <int> .Locking, BufferPools <int> .Locking); for (int i = outputTriangleIndices.Count - 1; i >= 0; i--) { int index = outputTriangleIndices[i]; if (alreadyContainedIndices.Add(index)) { outputSurfacePoints.Add(points[index]); } } alreadyContainedIndices.Dispose(); }
public static void TestSetResizing <TSpan, TPool>(TPool pool) where TSpan : ISpan <int> where TPool : IMemoryPool <int, TSpan> { Random random = new Random(5); QuickSet <int, TSpan, TSpan, PrimitiveComparer <int> > .Create(pool, pool, 2, 3, out var set); HashSet <int> controlSet = new HashSet <int>(); for (int iterationIndex = 0; iterationIndex < 100000; ++iterationIndex) { if (random.NextDouble() < 0.7) { set.Add(iterationIndex, pool, pool); controlSet.Add(iterationIndex); } if (random.NextDouble() < 0.2) { var indexToRemove = random.Next(set.Count); var toRemove = set[indexToRemove]; set.FastRemove(toRemove); controlSet.Remove(toRemove); } if (iterationIndex % 1000 == 0) { set.EnsureCapacity(set.Count * 3, pool, pool); } else if (iterationIndex % 7777 == 0) { set.Compact(pool, pool); } } Debug.Assert(set.Count == controlSet.Count); for (int i = 0; i < set.Count; ++i) { Debug.Assert(controlSet.Contains(set[i])); } foreach (var element in controlSet) { Debug.Assert(set.Contains(element)); } set.Dispose(pool, pool); }
internal IEnumerable <TElement> ComplexArray <TElement>(Func <Type, string, ImplicitRegistration, object> resolve, Type type) { TElement value; var set = new QuickSet <Type>(); int hashCode = type.GetHashCode(); // Iterate over hierarchy for (var container = this; null != container; container = container._parent) { // Skip to parent if no data if (null == container._metadata) { continue; } // Hold on to registries var registry = container._registry; // Get indexes and iterate over them var length = container._metadata.GetEntries(hashCode, type, out int[] data); for (var i = 1; i < length; i++) { var index = data[i]; var name = registry.Entries[index].Key.Name; if (null == name) { continue; } if (set.Add(registry.Entries[index].HashCode, registry.Entries[index].Key.Type)) { try { int hash = NamedType.GetHashCode(typeof(TElement), name); var registration = container.GetOrAdd(hash, typeof(TElement), name, registry.Entries[index].Value); value = (TElement)resolve(typeof(TElement), name, registration); } catch (ArgumentException ex) when(ex.InnerException is TypeLoadException) { continue; } yield return(value); } } } }
unsafe static void TestQuickInlining(BufferPool pool) { { var set = new QuickSet <double, PrimitiveComparer <double> >(4, pool); set.AddUnsafely(5); var item = set[0]; Console.WriteLine($"Managed Item: {item}"); var comparer = default(PrimitiveComparer <double>); var hash = comparer.Hash(ref item); Console.WriteLine($"Hash: {hash}"); } { var set = new QuickSet <int, PrimitiveComparer <int> >(4, pool); set.AddUnsafely(5); var item = set[0]; } }
public static void TestSetResizing() { Random random = new Random(5); UnsafeBufferPool <int> pool = new UnsafeBufferPool <int>(); QuickSet <int> set = new QuickSet <int>(pool, pool); HashSet <int> controlSet = new HashSet <int>(); for (int iterationIndex = 0; iterationIndex < 100000; ++iterationIndex) { if (random.NextDouble() < 0.7) { set.Add(iterationIndex); controlSet.Add(iterationIndex); } if (random.NextDouble() < 0.2) { var indexToRemove = random.Next(set.Count); var toRemove = set[indexToRemove]; set.FastRemove(toRemove); controlSet.Remove(toRemove); } if (iterationIndex % 1000 == 0) { set.EnsureCapacity(set.Count * 3); } else if (iterationIndex % 7777 == 0) { set.Compact(); } } Assert.IsTrue(set.Count == controlSet.Count); for (int i = 0; i < set.Count; ++i) { Assert.IsTrue(controlSet.Contains(set[i])); } foreach (var element in controlSet) { Assert.IsTrue(set.Contains(element)); } }
public static void RasterizeTriangle(ref Vector3 a, ref Vector3 b, ref Vector3 c, float cellSize, ref Vector3 gridOrigin, BufferPool pool, ref QuickSet <Cell, CellComparer> cells) { var gridA = a - gridOrigin; var gridB = b - gridOrigin; var gridC = c - gridOrigin; //Compute the bounding box of the triangle. var max = Vector3.Max(Vector3.Max(gridA, gridB), gridC); var min = Vector3.Min(Vector3.Min(gridA, gridB), gridC); var epsilon = new Vector3(1e-5f); min -= epsilon; max += epsilon; //Discretize the bounding box. //All indices are positive, so we can just truncate. int startX, endX, startY, endY, startZ, endZ; float inverseCellSize = 1f / cellSize; startX = (int)Math.Floor(min.X * inverseCellSize); endX = (int)Math.Floor(max.X * inverseCellSize); startY = (int)Math.Floor(min.Y * inverseCellSize); endY = (int)Math.Floor(max.Y * inverseCellSize); startZ = (int)Math.Floor(min.Z * inverseCellSize); endZ = (int)Math.Floor(max.Z * inverseCellSize); //Test the triangle against each cell. var halfExtents = new Vector3(cellSize * 0.5f); for (int i = startX; i <= endX; ++i) { for (int j = startY; j <= endY; ++j) { for (int k = startZ; k <= endZ; ++k) { var cellIndex = new Vector3(i, j, k); var cellOrigin = cellSize * cellIndex + halfExtents; var shiftedA = gridA - cellOrigin; var shiftedB = gridB - cellOrigin; var shiftedC = gridC - cellOrigin; if (BoxTriangleCollider.Intersecting(ref halfExtents, ref shiftedA, ref shiftedB, ref shiftedC)) { cells.Add(new Cell { X = i, Y = j, Z = k }, pool); } } } } }
internal IEnumerable <TElement> ResolveEnumerable <TElement>(Func <Type, string, ImplicitRegistration, object> resolve, Type typeDefinition, string name) { TElement value; var set = new QuickSet <Type>(); int hashCode = typeof(TElement).GetHashCode(); int hashGeneric = typeDefinition.GetHashCode(); // Iterate over hierarchy for (var container = this; null != container; container = container._parent) { // Skip to parent if no data if (null == container._metadata) { continue; } // Hold on to registries var registry = container._registry; // Get indexes for bound types and iterate over them var length = container._metadata.GetEntries <TElement>(hashCode, out int[] data); for (var i = 1; i < length; i++) { var index = data[i]; if (set.Add(registry.Entries[index].HashCode, registry.Entries[index].Key.Type)) { try { var registration = (ExplicitRegistration)registry.Entries[index].Value; value = (TElement)resolve(typeof(TElement), registry.Entries[index].Key.Name, registration); } catch (ArgumentException ex) when(ex.InnerException is TypeLoadException) { continue; } yield return(value); } } // Get indexes for unbound types and iterate over them length = container._metadata.GetEntries(hashGeneric, typeDefinition, out data); for (var i = 1; i < length; i++) { var index = data[i]; var key = registry.Entries[index].Key.Name; if (set.Add(registry.Entries[index].HashCode, registry.Entries[index].Key.Type)) { try { int hash = NamedType.GetHashCode(typeof(TElement), key); var registration = container.GetOrAdd(hash, typeof(TElement), key, registry.Entries[index].Value); value = (TElement)resolve(typeof(TElement), key, registration); } catch (MakeGenericTypeFailedException) { continue; } catch (InvalidOperationException ex) when(ex.InnerException is InvalidRegistrationException) { continue; } // TODO: Verify if required //catch (ArgumentException ex) when (ex.InnerException is TypeLoadException) //{ // continue; //} yield return(value); } } } // If nothing registered attempt to resolve the type if (0 == set.Count) { try { var registration = GetRegistration(typeof(TElement), name); value = (TElement)resolve(typeof(TElement), name, registration); } catch { yield break; } yield return(value); } }
internal IEnumerable <TElement> ResolveEnumerable <TElement>(Func <Type, IRegistration, object?> resolve, Type typeDefinition, string?name) { object?value; var set = new QuickSet(); var key = new HashKey(typeof(TElement)); var keyGeneric = new HashKey(typeDefinition); // Iterate over hierarchy for (UnityContainer?container = this; null != container; container = container._parent) { // Skip to parent if no data if (null == container._metadata) { continue; } // Hold on to registries Debug.Assert(null != container._registry); var registry = container._registry; // Get indexes for bound types and iterate over them var length = container._metadata.GetMeta(ref key, out int[]? data); if (null != data) { for (var i = 1; i < length; i++) { var index = data[i]; var registration = (ExplicitRegistration)registry.Entries[index].Policies; if (!set.Add(registration.Name)) { continue; } try { value = resolve(typeof(TElement), registration); } catch (ArgumentException ex) when(ex.InnerException is TypeLoadException) { continue; } #pragma warning disable CS8601 // Possible null reference assignment. yield return((TElement)value); #pragma warning restore CS8601 // Possible null reference assignment. } } // Get indexes for unbound types and iterate over them length = container._metadata.GetMeta(ref keyGeneric, out data); if (null != data) { for (var i = 1; i < length; i++) { var index = data[i]; var registration = (ExplicitRegistration)registry.Entries[index].Policies; if (set.Add(registration.Name)) { try { var itemKey = new HashKey(typeof(TElement), registration.Name); var item = container.GetOrAdd(ref itemKey, typeof(TElement), registration.Name, registration); value = resolve(typeof(TElement), item); } catch (MakeGenericTypeFailedException) { continue; } catch (InvalidRegistrationException) { continue; } #pragma warning disable CS8601 // Possible null reference assignment. yield return((TElement)value); #pragma warning restore CS8601 // Possible null reference assignment. } } } } // If nothing registered attempt to resolve the type if (0 == set.Count) { try { var registration = GetRegistration(typeof(TElement), name); value = resolve(typeof(TElement), registration); } catch { yield break; } #pragma warning disable CS8601 // Possible null reference assignment. yield return((TElement)value); #pragma warning restore CS8601 // Possible null reference assignment. } }
public static unsafe 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; } }
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); } }
public static void TestSetResizing() { Random random = new Random(5); UnsafeBufferPool<int> pool = new UnsafeBufferPool<int>(); QuickSet<int> set = new QuickSet<int>(pool, pool); HashSet<int> controlSet = new HashSet<int>(); for (int iterationIndex = 0; iterationIndex < 100000; ++iterationIndex) { if (random.NextDouble() < 0.7) { set.Add(iterationIndex); controlSet.Add(iterationIndex); } if (random.NextDouble() < 0.2) { var indexToRemove = random.Next(set.Count); var toRemove = set[indexToRemove]; set.FastRemove(toRemove); controlSet.Remove(toRemove); } if (iterationIndex % 1000 == 0) { set.EnsureCapacity(set.Count * 3); } else if (iterationIndex % 7777 == 0) { set.Compact(); } } Assert.IsTrue(set.Count == controlSet.Count); for (int i = 0; i < set.Count; ++i) { Assert.IsTrue(controlSet.Contains(set[i])); } foreach (var element in controlSet) { Assert.IsTrue(set.Contains(element)); } }
/// <summary> /// Identifies the points on the surface of hull. /// </summary> /// <param name="points">List of points in the set.</param> /// <param name="outputTriangleIndices">List of indices into the input point set composing the triangulated surface of the convex hull. /// Each group of 3 indices represents a triangle on the surface of the hull.</param> /// <param name="outputSurfacePoints">Unique points on the surface of the convex hull.</param> public static void GetConvexHull(ref QuickList<Vector3> points, ref QuickList<int> outputTriangleIndices, IList<Vector3> outputSurfacePoints) { GetConvexHull(ref points, ref outputTriangleIndices); var alreadyContainedIndices = new QuickSet<int>(BufferPools<int>.Locking, BufferPools<int>.Locking); for (int i = outputTriangleIndices.Count - 1; i >= 0; i--) { int index = outputTriangleIndices[i]; if (alreadyContainedIndices.Add(index)) { outputSurfacePoints.Add(points[index]); } } alreadyContainedIndices.Dispose(); }