void Initialize<U>(int initialCapacity, ref U allocator, int disposeSentinelStackDepth) where U : unmanaged, AllocatorManager.IAllocator
        {
            var totalSize = UnsafeUtility.SizeOf<T>() * (long)initialCapacity;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            CheckAllocator((Allocator)allocator.Handle.Value);
            CheckInitialCapacity(initialCapacity);
            CollectionHelper.CheckIsUnmanaged<T>();
            CheckTotalSize(initialCapacity, totalSize);

            DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, disposeSentinelStackDepth, (Allocator)allocator.Handle.Value);
            if (s_staticSafetyId.Data == 0)
            {
                CreateStaticSafetyId();
            }
            AtomicSafetyHandle.SetStaticSafetyId(ref m_Safety, s_staticSafetyId.Data);

            m_SafetyIndexHint = (allocator.Handle).AddSafetyHandle(m_Safety);
            if(m_SafetyIndexHint != AllocatorManager.AllocatorHandle.InvalidChildSafetyHandleIndex)
                DisposeSentinel.Clear(ref m_DisposeSentinel);
#endif
            m_ListData = UnsafeList.Create(UnsafeUtility.SizeOf<T>(), UnsafeUtility.AlignOf<T>(), initialCapacity, ref allocator);
            m_DeprecatedAllocator = (Allocator)allocator.Handle.Value;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
#endif
        }
Beispiel #2
0
        private NativeRoutines(int initialCapacity, Allocator allocator, int disposeSentinelStackDepth)
        {
            var totalSize = (long)sizeof(T) * initialCapacity;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            // Native allocation is only valid for Temp, Job and Persistent.
            if (allocator <= Allocator.None)
            {
                throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
            }
            if (totalSize > int.MaxValue)
            {
                throw new ArgumentOutOfRangeException($"Capacity has exceeded {int.MaxValue.ToString()} bytes");
            }

            DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, disposeSentinelStackDepth, allocator);
#endif
            m_ListData = UnsafeList.Create(UnsafeUtility.SizeOf <T>(), UnsafeUtility.AlignOf <T>(), initialCapacity, allocator);

            m_AllocatorLabel = allocator;

#if UNITY_2019_3_OR_NEWER && ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
#endif
        }
Beispiel #3
0
        public NativePriorityQueue(int initialCapacity, Allocator allocator)
        {
            var totalSize = UnsafeUtility.SizeOf <Node>() * (long)initialCapacity;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (allocator <= Allocator.None)
            {
                throw new ArgumentException("Allocator must be Temp, TempJob, or Persistent", "allocator");
            }
            if (initialCapacity < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(initialCapacity), $"{nameof(initialCapacity)} must be >= 0");
            }
            if (!UnsafeUtility.IsBlittable <T>())
            {
                throw new ArgumentException($"{typeof(T)} used in {nameof(NativePriorityQueue<T>)} must be blittable");
            }

            if (totalSize > int.MaxValue)
            {
                throw new ArgumentOutOfRangeException(nameof(initialCapacity), $"Capacity * sizeof(T) cannot exceed {int.MaxValue} bytes");
            }

            DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
            AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
#endif

            int nodeSize  = UnsafeUtility.SizeOf <Node>();
            int nodeAlign = UnsafeUtility.AlignOf <Node>();
            _nodes = UnsafeList.Create(nodeSize, nodeAlign, initialCapacity, allocator);
            _nodes->Add <Node>(default);
        }
        /// <summary>
        /// Safely disposes of this container and deallocates its memory when the jobs that use it have completed.
        /// </summary>
        /// <remarks>You can call this function dispose of the container immediately after scheduling the job. Pass
        /// the [JobHandle](https://docs.unity3d.com/ScriptReference/Unity.Jobs.JobHandle.html) returned by
        /// the [Job.Schedule](https://docs.unity3d.com/ScriptReference/Unity.Jobs.IJobExtensions.Schedule.html)
        /// method using the `jobHandle` parameter so the job scheduler can dispose the container after all jobs
        /// using it have run.</remarks>
        /// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
        /// <returns>A new job handle containing the prior handles as well as the handle for the job that deletes
        /// the container.</returns>
        public JobHandle Dispose(JobHandle inputDeps)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            // [DeallocateOnJobCompletion] is not supported, but we want the deallocation
            // to happen in a thread. DisposeSentinel needs to be cleared on main thread.
            // AtomicSafetyHandle can be destroyed after the job was scheduled (Job scheduling
            // will check that no jobs are writing to the container).
            DisposeSentinel.Clear(ref m_DisposeSentinel);

            var jobHandle = new NativeListDisposeJob {
                Data = new NativeListDispose {
                    m_ListData = m_ListData, m_Safety = m_Safety
                }
            }.Schedule(inputDeps);

            AtomicSafetyHandle.Release(m_Safety);
#else
            var jobHandle = new NativeListDisposeJob {
                Data = new NativeListDispose {
                    m_ListData = m_ListData
                }
            }.Schedule(inputDeps);
#endif
            m_ListData = null;

            return(jobHandle);
        }
Beispiel #5
0
 private static void CheckAllocated(UnsafeList *listData)
 {
     if (listData == null || !listData->IsCreated)
     {
         throw new Exception($"Expected {nameof(listData)} to be allocated.");
     }
 }
Beispiel #6
0
 private static void CheckNull(UnsafeList *listData)
 {
     if (listData == null)
     {
         throw new Exception($"Expected {nameof(listData)} to not be null.");
     }
 }
Beispiel #7
0
        HashedVertices(int vertexCapacity, int chainedIndicesCapacity, Allocator allocator, int disposeSentinelStackDepth)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            // Native allocation is only valid for Temp, Job and Persistent.
            CheckAllocator(allocator);
            CheckArgPositive(chainedIndicesCapacity);

            DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, disposeSentinelStackDepth, allocator);
#if UNITY_2020_1_OR_NEWER
            if (s_staticSafetyId.Data == 0)
            {
                CreateStaticSafetyId();
            }
            AtomicSafetyHandle.SetStaticSafetyId(ref m_Safety, s_staticSafetyId.Data);
#endif
#endif
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
#endif
            m_AllocatorLabel = allocator;
            var hashTableMemSize = (ushort)(kHashTableSize + 1) * UnsafeUtility.SizeOf <ushort>();
            m_HashTable = UnsafeUtility.Malloc(hashTableMemSize, UnsafeUtility.AlignOf <ushort>(), m_AllocatorLabel);
            UnsafeUtility.MemClear(m_HashTable, hashTableMemSize);

            m_Vertices       = UnsafeList.Create(UnsafeUtility.SizeOf <float3>(), UnsafeUtility.AlignOf <float3>(), vertexCapacity, allocator);
            m_ChainedIndices = UnsafeList.Create(UnsafeUtility.SizeOf <ushort>(), UnsafeUtility.AlignOf <ushort>(), chainedIndicesCapacity, allocator);

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
#endif
        }
Beispiel #8
0
        NativeList(int initialCapacity, Allocator allocator, int disposeSentinelStackDepth)
        {
            var totalSize = UnsafeUtility.SizeOf <T>() * (long)initialCapacity;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
            // Native allocation is only valid for Temp, Job and Persistent.
            if (allocator <= Allocator.None)
            {
                throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
            }
            if (initialCapacity < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(initialCapacity), "Capacity must be >= 0");
            }

            //CollectionHelper.CheckIsUnmanaged<T>();

            // Make sure we cannot allocate more than int.MaxValue (2,147,483,647 bytes)
            // because the underlying UnsafeUtility.Malloc is expecting a int.
            // TODO: change UnsafeUtility.Malloc to accept a UIntPtr length instead to match C++ API
            if (totalSize > int.MaxValue)
            {
                throw new ArgumentOutOfRangeException(nameof(initialCapacity), $"Capacity * sizeof(T) cannot exceed {int.MaxValue} bytes");
            }

            DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, disposeSentinelStackDepth, allocator);
#endif
            m_ListData            = UnsafeList.Create(UnsafeUtility.SizeOf <T>(), UnsafeUtility.AlignOf <T>(), initialCapacity, allocator);
            m_DeprecatedAllocator = allocator;

#if UNITY_2019_3_OR_NEWER && ENABLE_UNITY_COLLECTIONS_CHECKS
            AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
#endif
        }
Beispiel #9
0
 private static void CheckNotAllocated(UnsafeList *listData)
 {
     if (listData != null && listData->IsCreated)
     {
         throw new Exception($"Expected {nameof(listData)} to not be allocated.");
     }
 }
        public static void CopyTo <T>(UnsafeList *list, void *destination, int destinationIndex) where T : unmanaged
        {
            if (destination == null)
            {
                throw new ArgumentNullException(nameof(destination));
            }

            if (destinationIndex < 0)
            {
                throw new ArgumentOutOfRangeException(ThrowHelper.ArgumentOutOfRange_Index);
            }

            UDebug.Assert(list != null);
            UDebug.Assert(list->_items.Ptr != null);
            UDebug.Assert(typeof(T).TypeHandle.Value == list->_typeHandle);

            int numToCopy = list->_count;

            if (numToCopy == 0)
            {
                return;
            }

            UnsafeBuffer.CopyTo <T>(list->_items, 0, destination, destinationIndex, numToCopy);
        }
Beispiel #11
0
        public JobHandle Dispose(JobHandle inputDeps)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            DisposeSentinel.Clear(ref m_DisposeSentinel);

            var jobHandle = new NativePriorityQueueDisposeJob {
                Data = new NativePriorityQueueDispose
                {
                    m_ListData = _nodes,
                    m_Safety   = m_Safety
                }
            }.Schedule(inputDeps);

            AtomicSafetyHandle.Release(m_Safety);
#else
            var jobHandle = new NativePriorityQueueDisposeJob
            {
                Data = new NativePriorityQueueDispose {
                    m_ListData = _nodes
                }
            }.Schedule(inputDeps);
#endif
            _nodes = null;

            return(jobHandle);
        }
                public ConcurrentConnectionQueue(NativeList <int> queue)
                {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    m_Safety = NativeListUnsafeUtility.GetAtomicSafetyHandle(ref queue);
                    AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
#endif
                    m_ConnectionEventHeadTail = (UnsafeList *)NativeListUnsafeUtility.GetInternalListDataPtrUnchecked(ref queue);
                }
        public static Enumerator <T> GetEnumerator <T>(UnsafeList *list) where T : unmanaged
        {
            UDebug.Assert(list != null);
            UDebug.Assert(list->_items.Ptr != null);
            UDebug.Assert(typeof(T).TypeHandle.Value == list->_typeHandle);

            return(new Enumerator <T>(list->_items, 0, list->_count));
        }
Beispiel #14
0
 private unsafe static void CheckPtrInitialized(UnsafeList *ptr, int index)
 {
     if (ptr == null ||
         !ptr->IsCreated)
     {
         throw new IndexOutOfRangeException($"Index {index} has not been initialized.");
     }
 }
Beispiel #15
0
        /// <summary>
        /// Disposes of this container and deallocates its memory immediately.
        /// </summary>
        public void Dispose()
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
#endif
            UnsafeList.Destroy(m_ListData);
            m_ListData = null;
        }
        public static int LastIndexOf <T>(UnsafeList *list, T item) where T : unmanaged, IEquatable <T>
        {
            UDebug.Assert(list != null);
            UDebug.Assert(list->_items.Ptr != null);
            UDebug.Assert(typeof(T).TypeHandle.Value == list->_typeHandle);

            return(UnsafeBuffer.LastIndexOf(list->_items, item, list->_count - 1, list->_count));
        }
Beispiel #17
0
        public static void SetCapacity(UnsafeList *list, int capacity)
        {
            Assert.Check(list != null);

            if (list->_items.Dynamic == false)
            {
                throw new InvalidOperationException(LIST_FIXED_CANT_CHANGE_CAPACITY);
            }

            // no change in capacity
            if (capacity == list->_items.Length)
            {
                return;
            }

            // tried to set to zero or negative, so free items
            if (capacity <= 0)
            {
                // have to make sure to set count to 0
                list->_count = 0;

                // and clear memory for items
                if (list->_items.Ptr != null)
                {
                    UnsafeBuffer.Free(&list->_items);
                }

                return;
            }

            // allocate new items
            UnsafeBuffer newItems = default;

            UnsafeBuffer.InitDynamic(&newItems, capacity, list->_items.Stride);

            // if have anything in list, copy it
            if (list->_count > 0)
            {
                // also make sure that count is
                // not larger than the new capacity
                if (list->_count > capacity)
                {
                    list->_count = capacity;
                }

                // copy over elements
                UnsafeBuffer.Copy(list->_items, 0, newItems, 0, list->_count);
            }

            // if an existing buffer was here, free it
            if (list->_items.Ptr != null)
            {
                UnsafeBuffer.Free(&list->_items);
            }

            // assign new buffer
            list->_items = newItems;
        }
 public BlocksOfSize(int dummy)
 {
     m_Blocks = (UnsafeList *)Memory.Unmanaged.Allocate(
         UnsafeUtility.SizeOf <UnsafeList>(),
         UnsafeUtility.AlignOf <UnsafeList>(),
         Allocator.Persistent);
     UnsafeUtility.MemClear(m_Blocks, UnsafeUtility.SizeOf <UnsafeList>());
     m_Blocks->Allocator = Allocator.Persistent;
 }
Beispiel #19
0
        // Add but make the assumption we're not growing any list
        public unsafe static ushort Add(ushort *hashTable, UnsafeList *chainedIndices, UnsafeList *vertices, float3 vertex)
        {
            var centerIndex = new int3((int)(vertex.x / HashedVertices.kCellSize), (int)(vertex.y / HashedVertices.kCellSize), (int)(vertex.z / HashedVertices.kCellSize));
            var offsets     = stackalloc int3[]
            {
                new int3(-1, -1, -1), new int3(-1, -1, 0), new int3(-1, -1, +1),
                new int3(-1, 0, -1), new int3(-1, 0, 0), new int3(-1, 0, +1),
                new int3(-1, +1, -1), new int3(-1, +1, 0), new int3(-1, +1, +1),

                new int3(0, -1, -1), new int3(0, -1, 0), new int3(0, -1, +1),
                new int3(0, 0, -1), new int3(0, 0, 0), new int3(0, 0, +1),
                new int3(0, +1, -1), new int3(0, +1, 0), new int3(0, +1, +1),

                new int3(+1, -1, -1), new int3(+1, -1, 0), new int3(+1, -1, +1),
                new int3(+1, 0, -1), new int3(+1, 0, 0), new int3(+1, 0, +1),
                new int3(+1, +1, -1), new int3(+1, +1, 0), new int3(+1, +1, +1)
            };

            float3 *verticesPtr = (float3 *)vertices->Ptr;

            ushort closestVertexIndex = ushort.MaxValue;

            for (int i = 0; i < 3 * 3 * 3; i++)
            {
                var index      = centerIndex + offsets[i];
                var chainIndex = ((int)hashTable[GetHash(index)]) - 1;
                {
                    float closestDistance = CSGConstants.kSqrVertexEqualEpsilon;
                    while (chainIndex != -1)
                    {
                        var nextChainIndex = ((int)((ushort *)chainedIndices->Ptr)[chainIndex]) - 1;
                        var sqrDistance    = math.lengthsq(verticesPtr[chainIndex] - vertex);
                        if (sqrDistance < closestDistance)
                        {
                            closestVertexIndex = (ushort)chainIndex;
                            closestDistance    = sqrDistance;
                        }
                        chainIndex = nextChainIndex;
                    }
                }
            }
            if (closestVertexIndex != ushort.MaxValue)
            {
                return(closestVertexIndex);
            }

            // Add Unique vertex
            {
                var hashCode       = GetHash(centerIndex);
                var prevChainIndex = hashTable[hashCode];
                var newChainIndex  = chainedIndices->Length;
                vertices->Add(vertex);
                chainedIndices->Add((ushort)prevChainIndex);
                hashTable[(int)hashCode] = (ushort)(newChainIndex + 1);
                return((ushort)newChainIndex);
            }
        }
Beispiel #20
0
        public void AddComponents(UnsafeList *sortedEntityBatchList, ref ComponentTypes types)
        {
            Assert.IsFalse(types.ChunkComponentCount > 0);

            // Reverse order so that batch indices do not change while iterating.
            for (int i = sortedEntityBatchList->Length - 1; i >= 0; i--)
            {
                AddComponents(((EntityBatchInChunk *)sortedEntityBatchList->Ptr)[i], types);
            }
        }
Beispiel #21
0
        public void RemoveComponent(UnsafeList *sortedEntityBatchList, ComponentType type)
        {
            Assert.IsFalse(type.IsChunkComponent);

            // Reverse order so that batch indices do not change while iterating.
            for (int i = sortedEntityBatchList->Length - 1; i >= 0; i--)
            {
                RemoveComponent(((EntityBatchInChunk *)sortedEntityBatchList->Ptr)[i], type);
            }
        }
        public void Dispose()
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
#endif
            UnsafeList.Destroy(m_Vertices);
            m_Vertices = null;
            UnsafeList.Destroy(m_ChainedIndices);
            m_ChainedIndices = null;
            UnsafeUtility.Free(m_HashTable, m_AllocatorLabel);
        }
        public void Dispose()
        {
            UnsafeList.Destroy(elements);
            elements = null;
            UnsafeList.Destroy(lookup);
            lookup = null;
            UnsafeList.Destroy(nodes);
            nodes = null;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            DisposeSentinel.Dispose(ref safetyHandle, ref disposeSentinel);
#endif
        }
Beispiel #24
0
        /// <summary>
        /// Destroys list.
        /// </summary>
        public static void Destroy(UnsafeList *listData)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            if (listData == null)
            {
                throw new Exception("UnsafeList has yet to be created or has been destroyed!");
            }
#endif
            var allocator = listData->Allocator;
            listData->Dispose();
            UnsafeUtility.Free(listData, allocator);
        }
        public static bool RemoveUnordered <T>(UnsafeList *list, T item) where T : unmanaged, IEquatable <T>
        {
            int index = IndexOf(list, item);

            if (index < 0)
            {
                return(false);
            }

            RemoveAtUnordered(list, index);
            return(true);
        }
        HashedVertices(int vertexCapacity, int chainedIndicesCapacity, Allocator allocator = Allocator.Persistent)
        {
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
#endif
            m_AllocatorLabel = allocator;
            var hashTableMemSize = (ushort)(kHashTableSize + 1) * UnsafeUtility.SizeOf <ushort>();
            m_HashTable = UnsafeUtility.Malloc(hashTableMemSize, UnsafeUtility.AlignOf <ushort>(), m_AllocatorLabel);
            UnsafeUtility.MemClear(m_HashTable, hashTableMemSize);

            m_Vertices       = UnsafeList.Create(UnsafeUtility.SizeOf <float3>(), UnsafeUtility.AlignOf <float3>(), vertexCapacity, allocator);
            m_ChainedIndices = UnsafeList.Create(UnsafeUtility.SizeOf <ushort>(), UnsafeUtility.AlignOf <ushort>(), chainedIndicesCapacity, allocator);
        }
            public void Query(NativeOctree <T> tree, AABB bounds, NativeList <OctElement <T> > results)
            {
                this.tree   = tree;
                this.bounds = bounds;
                count       = 0;

                // Get pointer to inner list data for faster writing
                fastResults = (UnsafeList *)NativeListUnsafeUtility.GetInternalListDataPtrUnchecked(ref results);

                RecursiveRangeQuery(tree.bounds, false, 1, 1);

                fastResults->Length = count;
            }
Beispiel #28
0
        public static bool Remove <T>(UnsafeList *list, T item) where T : unmanaged, IEquatable <T>
        {
            Assert.Check(list != null);

            int index = IndexOf <T>(list, item);

            if (index < 0)
            {
                return(false);
            }

            RemoveAt(list, index);
            return(true);
        }
Beispiel #29
0
        public static T *GetPtr <T>(UnsafeList *list, int index) where T : unmanaged
        {
            Assert.Check(list != null);

            // cast to uint trick, which eliminates < 0 check
            if ((uint)index >= (uint)list->_count)
            {
                throw new IndexOutOfRangeException();
            }

            var items = list->_items;

            return((T *)UnsafeBuffer.Element(items.Ptr, index, items.Stride));
        }
Beispiel #30
0
        public unsafe static void ReplaceIfExists(ushort *hashTable, UnsafeList *chainedIndices, UnsafeList *vertices, float3 vertex)
        {
            var centerIndex = new int3((int)(vertex.x / HashedVertices.kCellSize), (int)(vertex.y / HashedVertices.kCellSize), (int)(vertex.z / HashedVertices.kCellSize));
            var offsets     = stackalloc int3[]
            {
                new int3(-1, -1, -1), new int3(-1, -1, 0), new int3(-1, -1, +1),
                new int3(-1, 0, -1), new int3(-1, 0, 0), new int3(-1, 0, +1),
                new int3(-1, +1, -1), new int3(-1, +1, 0), new int3(-1, +1, +1),

                new int3(0, -1, -1), new int3(0, -1, 0), new int3(0, -1, +1),
                new int3(0, 0, -1), new int3(0, 0, 0), new int3(0, 0, +1),
                new int3(0, +1, -1), new int3(0, +1, 0), new int3(0, +1, +1),

                new int3(+1, -1, -1), new int3(+1, -1, 0), new int3(+1, -1, +1),
                new int3(+1, 0, -1), new int3(+1, 0, 0), new int3(+1, 0, +1),
                new int3(+1, +1, -1), new int3(+1, +1, 0), new int3(+1, +1, +1)
            };

            float3 *verticesPtr = (float3 *)vertices->Ptr;

            ushort closestVertexIndex = ushort.MaxValue;

            for (int i = 0; i < 3 * 3 * 3; i++)
            {
                var index      = centerIndex + offsets[i];
                var chainIndex = ((int)hashTable[GetHash(index)]) - 1;
                {
                    float closestDistance = CSGConstants.kSqrVertexEqualEpsilon;
                    while (chainIndex != -1)
                    {
                        var nextChainIndex = ((int)((ushort *)chainedIndices->Ptr)[chainIndex]) - 1;
                        var sqrDistance    = math.lengthsq(verticesPtr[chainIndex] - vertex);
                        if (sqrDistance < closestDistance)
                        {
                            closestVertexIndex = (ushort)chainIndex;
                            closestDistance    = sqrDistance;
                        }
                        chainIndex = nextChainIndex;
                    }
                }
            }
            if (closestVertexIndex == ushort.MaxValue)
            {
                return;
            }

            verticesPtr[closestVertexIndex] = vertex;
        }