A game about forced loneliness, made by TACStudios
at master 264 lines 10 kB view raw
1using System; 2using System.Diagnostics; 3using System.Threading; 4using AOT; 5using Unity.Burst; 6using Unity.Collections.LowLevel.Unsafe; 7using Unity.Mathematics; 8 9namespace Unity.Collections 10{ 11 unsafe internal struct ArrayOfArrays<T> : IDisposable where T : unmanaged 12 { 13 AllocatorManager.AllocatorHandle m_backingAllocatorHandle; 14 int m_lengthInElements; 15 int m_capacityInElements; 16 int m_log2BlockSizeInElements; 17 int m_blocks; 18 IntPtr* m_block; 19 20 int BlockSizeInElements => 1 << m_log2BlockSizeInElements; 21 int BlockSizeInBytes => BlockSizeInElements * sizeof(T); 22 int BlockMask => BlockSizeInElements - 1; 23 24 public int Length => m_lengthInElements; 25 public int Capacity => m_capacityInElements; 26 27 public ArrayOfArrays(int capacityInElements, AllocatorManager.AllocatorHandle backingAllocatorHandle, int log2BlockSizeInElements = 12) 28 { 29 this = default; 30 m_backingAllocatorHandle = backingAllocatorHandle; 31 m_lengthInElements = 0; 32 m_capacityInElements = capacityInElements; 33 m_log2BlockSizeInElements = log2BlockSizeInElements; 34 m_blocks = (capacityInElements + BlockMask) >> m_log2BlockSizeInElements; 35 m_block = (IntPtr*)Memory.Unmanaged.Allocate(sizeof(IntPtr) * m_blocks, 16, m_backingAllocatorHandle); 36 UnsafeUtility.MemSet(m_block, 0, sizeof(IntPtr) * m_blocks); 37 } 38 39 public void LockfreeAdd(T t) 40 { 41 var elementIndex = Interlocked.Increment(ref m_lengthInElements) - 1; 42 var blockIndex = BlockIndexOfElement(elementIndex); 43 CheckBlockIndex(blockIndex); 44 if(m_block[blockIndex] == IntPtr.Zero) 45 { 46 void* pointer = Memory.Unmanaged.Allocate(BlockSizeInBytes, 16, m_backingAllocatorHandle); // $$$! 47 var lastBlock = math.min(m_blocks, blockIndex + 4); // don't overgrow too fast, simply to avoid a $$$ free 48 for(; blockIndex < lastBlock; ++blockIndex) 49 if(IntPtr.Zero == Interlocked.CompareExchange(ref m_block[blockIndex], (IntPtr)pointer, IntPtr.Zero)) 50 break; // install the new block, into *any* empty slot available, to avoid wasting the time we spent on malloc 51 if(blockIndex == lastBlock) 52 Memory.Unmanaged.Free(pointer, m_backingAllocatorHandle); // $$$, only if absolutely necessary 53 } 54 this[elementIndex] = t; 55 } 56 57 public ref T this[int elementIndex] 58 { 59 get 60 { 61 CheckElementIndex(elementIndex); 62 var blockIndex = BlockIndexOfElement(elementIndex); 63 CheckBlockIndex(blockIndex); 64 CheckBlockIsNotNull(blockIndex); 65 IntPtr blockIntPtr = m_block[blockIndex]; 66 var elementIndexInBlock = elementIndex & BlockMask; 67 T* blockPointer = (T*)blockIntPtr; 68 return ref blockPointer[elementIndexInBlock]; 69 } 70 } 71 72 public void Rewind() 73 { 74 m_lengthInElements = 0; 75 } 76 77 public void Clear() 78 { 79 Rewind(); 80 for(var i = 0; i < m_blocks; ++i) 81 if(m_block[i] != IntPtr.Zero) 82 { 83 Memory.Unmanaged.Free((void*)m_block[i], m_backingAllocatorHandle); 84 m_block[i] = IntPtr.Zero; 85 } 86 } 87 88 public void Dispose() 89 { 90 Clear(); 91 Memory.Unmanaged.Free(m_block, m_backingAllocatorHandle); 92 } 93 94 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 95 void CheckElementIndex(int elementIndex) 96 { 97 if (elementIndex >= m_lengthInElements) 98 throw new ArgumentException($"Element index {elementIndex} must be less than length in elements {m_lengthInElements}."); 99 } 100 101 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 102 void CheckBlockIndex(int blockIndex) 103 { 104 if (blockIndex >= m_blocks) 105 throw new ArgumentException($"Block index {blockIndex} must be less than number of blocks {m_blocks}."); 106 } 107 108 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 109 void CheckBlockIsNotNull(int blockIndex) 110 { 111 if(m_block[blockIndex] == IntPtr.Zero) 112 throw new ArgumentException($"Block index {blockIndex} is a null pointer."); 113 } 114 115 public void RemoveAtSwapBack(int elementIndex) 116 { 117 this[elementIndex] = this[Length-1]; 118 --m_lengthInElements; 119 } 120 121 int BlockIndexOfElement(int elementIndex) 122 { 123 return elementIndex >> m_log2BlockSizeInElements; 124 } 125 126 public void TrimExcess() 127 { 128 for(var blockIndex = BlockIndexOfElement(m_lengthInElements + BlockMask); blockIndex < m_blocks; ++blockIndex) 129 { 130 CheckBlockIndex(blockIndex); 131 if(m_block[blockIndex] != IntPtr.Zero) 132 { 133 var blockIntPtr = m_block[blockIndex]; 134 void* blockPointer = (void*)blockIntPtr; 135 Memory.Unmanaged.Free(blockPointer, m_backingAllocatorHandle); 136 m_block[blockIndex] = IntPtr.Zero; 137 } 138 } 139 } 140 } 141 142 [BurstCompile] 143 internal struct AutoFreeAllocator : AllocatorManager.IAllocator 144 { 145 ArrayOfArrays<IntPtr> m_allocated; 146 ArrayOfArrays<IntPtr> m_tofree; 147 AllocatorManager.AllocatorHandle m_handle; 148 AllocatorManager.AllocatorHandle m_backingAllocatorHandle; 149 150 unsafe public void Update() 151 { 152 for(var i = m_tofree.Length; i --> 0;) 153 for(var j = m_allocated.Length; j --> 0;) 154 if(m_allocated[j] == m_tofree[i]) 155 { 156 Memory.Unmanaged.Free((void*)m_tofree[i], m_backingAllocatorHandle); 157 m_allocated.RemoveAtSwapBack(j); 158 break; 159 } 160 m_tofree.Rewind(); 161 m_allocated.TrimExcess(); 162 } 163 164 unsafe public void Initialize(AllocatorManager.AllocatorHandle backingAllocatorHandle) 165 { 166 m_allocated = new ArrayOfArrays<IntPtr>(1024 * 1024, backingAllocatorHandle); 167 m_tofree = new ArrayOfArrays<IntPtr>(128 * 1024, backingAllocatorHandle); 168 m_backingAllocatorHandle = backingAllocatorHandle; 169 } 170 171 unsafe public void FreeAll() 172 { 173 Update(); 174 m_handle.Rewind(); 175 for(var i = 0; i < m_allocated.Length; ++i) 176 Memory.Unmanaged.Free((void*) m_allocated[i], m_backingAllocatorHandle); 177 m_allocated.Rewind(); 178 } 179 180 /// <summary> 181 /// Dispose the allocator. This must be called to free the memory blocks that were allocated from the system. 182 /// </summary> 183 public void Dispose() 184 { 185 FreeAll(); 186 m_tofree.Dispose(); 187 m_allocated.Dispose(); 188 } 189 190 /// <summary> 191 /// The allocator function. It can allocate, deallocate, or reallocate. 192 /// </summary> 193 public AllocatorManager.TryFunction Function => Try; 194 195 /// <summary> 196 /// Invoke the allocator function. 197 /// </summary> 198 /// <param name="block">The block to allocate, deallocate, or reallocate. See <see cref="AllocatorManager.Try"/></param> 199 /// <returns>0 if successful. Otherwise, returns the error code from the allocator function.</returns> 200 public int Try(ref AllocatorManager.Block block) 201 { 202 unsafe 203 { 204 if (block.Range.Pointer == IntPtr.Zero) 205 { 206 if (block.Bytes == 0) 207 { 208 return 0; 209 } 210 211 var ptr = (byte*)Memory.Unmanaged.Allocate(block.Bytes, block.Alignment, m_backingAllocatorHandle); 212 block.Range.Pointer = (IntPtr)ptr; 213 block.AllocatedItems = block.Range.Items; 214 215 m_allocated.LockfreeAdd(block.Range.Pointer); 216 217 return 0; 218 } 219 220 if (block.Range.Items == 0) 221 { 222 m_tofree.LockfreeAdd(block.Range.Pointer); 223 224 block.Range.Pointer = IntPtr.Zero; 225 block.AllocatedItems = 0; 226 227 return 0; 228 } 229 230 return -1; 231 } 232 } 233 234 [BurstCompile] 235 [MonoPInvokeCallback(typeof(AllocatorManager.TryFunction))] 236 internal static int Try(IntPtr state, ref AllocatorManager.Block block) 237 { 238 unsafe { return ((AutoFreeAllocator*)state)->Try(ref block); } 239 } 240 241 /// <summary> 242 /// This allocator. 243 /// </summary> 244 /// <value>This allocator.</value> 245 public AllocatorManager.AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } } 246 247 /// <summary> 248 /// Cast the Allocator index into Allocator 249 /// </summary> 250 public Allocator ToAllocator { get { return m_handle.ToAllocator; } } 251 252 /// <summary> 253 /// Check whether an allocator is a custom allocator 254 /// </summary> 255 public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } } 256 257 /// <summary> 258 /// Check whether this allocator will automatically dispose allocations. 259 /// </summary> 260 /// <remarks>Allocations made by Auto free allocator are automatically disposed.</remarks> 261 /// <value>Always true</value> 262 public bool IsAutoDispose { get { return true; } } 263 } 264}