A game about forced loneliness, made by TACStudios
at master 674 lines 32 kB view raw
1using System; 2using System.Diagnostics; 3using System.Runtime.InteropServices; 4using Unity.Collections.LowLevel.Unsafe; 5using Unity.Burst; 6using Unity.Burst.CompilerServices; 7using Unity.Jobs; 8using Unity.Jobs.LowLevel.Unsafe; 9using Unity.Mathematics; 10using System.Reflection; 11using System.Runtime.CompilerServices; 12 13namespace Unity.Collections 14{ 15 /// <summary> 16 /// For scheduling release of unmanaged resources. 17 /// </summary> 18 public interface INativeDisposable : IDisposable 19 { 20 /// <summary> 21 /// Creates and schedules a job that will release all resources (memory and safety handles) of this collection. 22 /// </summary> 23 /// <param name="inputDeps">A job handle which the newly scheduled job will depend upon.</param> 24 /// <returns>The handle of a new job that will release all resources (memory and safety handles) of this collection.</returns> 25 JobHandle Dispose(JobHandle inputDeps); 26 } 27 28 /// <summary> 29 /// Provides helper methods for collections. 30 /// </summary> 31 [GenerateTestsForBurstCompatibility] 32 public static class CollectionHelper 33 { 34 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 35 internal static void CheckAllocator(AllocatorManager.AllocatorHandle allocator) 36 { 37 if (!ShouldDeallocate(allocator)) 38 throw new ArgumentException($"Allocator {allocator} must not be None or Invalid"); 39 } 40 41 /// <summary> 42 /// The size in bytes of the current platform's L1 cache lines. 43 /// </summary> 44 /// <value>The size in bytes of the current platform's L1 cache lines.</value> 45 public const int CacheLineSize = JobsUtility.CacheLineSize; 46 47 [StructLayout(LayoutKind.Explicit)] 48 internal struct LongDoubleUnion 49 { 50 [FieldOffset(0)] 51 internal long longValue; 52 53 [FieldOffset(0)] 54 internal double doubleValue; 55 } 56 57 /// <summary> 58 /// Returns the binary logarithm of the `value`, but the result is rounded down to the nearest integer. 59 /// </summary> 60 /// <param name="value">The value.</param> 61 /// <returns>The binary logarithm of the `value`, but the result is rounded down to the nearest integer.</returns> 62 public static int Log2Floor(int value) 63 { 64 return 31 - math.lzcnt((uint)value); 65 } 66 67 /// <summary> 68 /// Returns the binary logarithm of the `value`, but the result is rounded up to the nearest integer. 69 /// </summary> 70 /// <param name="value">The value.</param> 71 /// <returns>The binary logarithm of the `value`, but the result is rounded up to the nearest integer.</returns> 72 public static int Log2Ceil(int value) 73 { 74 return 32 - math.lzcnt((uint)value - 1); 75 } 76 77 /// <summary> 78 /// Returns an allocation size in bytes that factors in alignment. 79 /// </summary> 80 /// <example><code> 81 /// // 55 aligned to 16 is 64. 82 /// int size = CollectionHelper.Align(55, 16); 83 /// </code></example> 84 /// <param name="size">The size to align.</param> 85 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param> 86 /// <returns>The smallest integer that is greater than or equal to `size` and is a multiple of `alignmentPowerOfTwo`.</returns> 87 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception> 88 public static int Align(int size, int alignmentPowerOfTwo) 89 { 90 if (alignmentPowerOfTwo == 0) 91 return size; 92 93 CheckIntPositivePowerOfTwo(alignmentPowerOfTwo); 94 95 return (size + alignmentPowerOfTwo - 1) & ~(alignmentPowerOfTwo - 1); 96 } 97 98 /// <summary> 99 /// Returns an allocation size in bytes that factors in alignment. 100 /// </summary> 101 /// <example><code> 102 /// // 55 aligned to 16 is 64. 103 /// ulong size = CollectionHelper.Align(55, 16); 104 /// </code></example> 105 /// <param name="size">The size to align.</param> 106 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param> 107 /// <returns>The smallest integer that is greater than or equal to `size` and is a multiple of `alignmentPowerOfTwo`.</returns> 108 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception> 109 public static ulong Align(ulong size, ulong alignmentPowerOfTwo) 110 { 111 if (alignmentPowerOfTwo == 0) 112 return size; 113 114 CheckUlongPositivePowerOfTwo(alignmentPowerOfTwo); 115 116 return (size + alignmentPowerOfTwo - 1) & ~(alignmentPowerOfTwo - 1); 117 } 118 119 /// <summary> 120 /// Returns true if the address represented by the pointer has a given alignment. 121 /// </summary> 122 /// <param name="p">The pointer.</param> 123 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param> 124 /// <returns>True if the address is a multiple of `alignmentPowerOfTwo`.</returns> 125 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception> 126 public static unsafe bool IsAligned(void* p, int alignmentPowerOfTwo) 127 { 128 CheckIntPositivePowerOfTwo(alignmentPowerOfTwo); 129 return ((ulong)p & ((ulong)alignmentPowerOfTwo - 1)) == 0; 130 } 131 132 /// <summary> 133 /// Returns true if an offset has a given alignment. 134 /// </summary> 135 /// <param name="offset">An offset</param> 136 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param> 137 /// <returns>True if the offset is a multiple of `alignmentPowerOfTwo`.</returns> 138 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception> 139 public static bool IsAligned(ulong offset, int alignmentPowerOfTwo) 140 { 141 CheckIntPositivePowerOfTwo(alignmentPowerOfTwo); 142 return (offset & ((ulong)alignmentPowerOfTwo - 1)) == 0; 143 } 144 145 /// <summary> 146 /// Returns true if a positive value is a non-zero power of two. 147 /// </summary> 148 /// <remarks>Result is invalid if `value &lt; 0`.</remarks> 149 /// <param name="value">A positive value.</param> 150 /// <returns>True if the value is a non-zero, positive power of two.</returns> 151 public static bool IsPowerOfTwo(int value) 152 { 153 return (value & (value - 1)) == 0; 154 } 155 156 /// <summary> 157 /// Returns a (non-cryptographic) hash of a memory block. 158 /// </summary> 159 /// <remarks>The hash function used is [djb2](http://web.archive.org/web/20190508211657/http://www.cse.yorku.ca/~oz/hash.html).</remarks> 160 /// <param name="ptr">A buffer.</param> 161 /// <param name="bytes">The number of bytes to hash.</param> 162 /// <returns>A hash of the bytes.</returns> 163 public static unsafe uint Hash(void* ptr, int bytes) 164 { 165 // djb2 - Dan Bernstein hash function 166 // http://web.archive.org/web/20190508211657/http://www.cse.yorku.ca/~oz/hash.html 167 byte* str = (byte*)ptr; 168 ulong hash = 5381; 169 while (bytes > 0) 170 { 171 ulong c = str[--bytes]; 172 hash = ((hash << 5) + hash) + c; 173 } 174 return (uint)hash; 175 } 176 177 [ExcludeFromBurstCompatTesting("Used only for debugging, and uses managed strings")] 178 internal static void WriteLayout(Type type) 179 { 180 Console.WriteLine($" Offset | Bytes | Name Layout: {0}", type.Name); 181 var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 182 foreach (var field in fields) 183 { 184 Console.WriteLine(" {0, 6} | {1, 6} | {2}" 185 , Marshal.OffsetOf(type, field.Name) 186 , Marshal.SizeOf(field.FieldType) 187 , field.Name 188 ); 189 } 190 } 191 192 internal static bool ShouldDeallocate(AllocatorManager.AllocatorHandle allocator) 193 { 194 // Allocator.Invalid == container is not initialized. 195 // Allocator.None == container is initialized, but container doesn't own data. 196 return allocator.ToAllocator > Allocator.None; 197 } 198 199 /// <summary> 200 /// Tell Burst that an integer can be assumed to map to an always positive value. 201 /// </summary> 202 /// <param name="value">The integer that is always positive.</param> 203 /// <returns>Returns `x`, but allows the compiler to assume it is always positive.</returns> 204 [return: AssumeRange(0, int.MaxValue)] 205 [MethodImpl(MethodImplOptions.AggressiveInlining)] 206 internal static int AssumePositive(int value) 207 { 208 return value; 209 } 210 211 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 212 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", GenericTypeArguments = new[] { typeof(NativeArray<int>) })] 213 internal static void CheckIsUnmanaged<T>() 214 { 215 if (!UnsafeUtility.IsUnmanaged<T>()) 216 { 217 throw new ArgumentException($"{typeof(T)} used in native collection is not blittable or not primitive"); 218 } 219 } 220 221#if ENABLE_UNITY_COLLECTIONS_CHECKS 222 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", GenericTypeArguments = new[] { typeof(NativeArray<int>) })] 223 internal static void InitNativeContainer<T>(AtomicSafetyHandle handle) 224 { 225 if (UnsafeUtility.IsNativeContainerType<T>()) 226 AtomicSafetyHandle.SetNestedContainer(handle, true); 227 } 228#endif 229 230 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 231 internal static void CheckIntPositivePowerOfTwo(int value) 232 { 233 var valid = (value > 0) && ((value & (value - 1)) == 0); 234 if (!valid) 235 { 236 throw new ArgumentException($"Alignment requested: {value} is not a non-zero, positive power of two."); 237 } 238 } 239 240 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 241 internal static void CheckUlongPositivePowerOfTwo(ulong value) 242 { 243 var valid = (value > 0) && ((value & (value - 1)) == 0); 244 if (!valid) 245 { 246 throw new ArgumentException($"Alignment requested: {value} is not a non-zero, positive power of two."); 247 } 248 } 249 250 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 251 [MethodImpl(MethodImplOptions.AggressiveInlining)] 252 internal static void CheckIndexInRange(int index, int length) 253 { 254 // This checks both < 0 and >= Length with one comparison 255 if ((uint)index >= (uint)length) 256 throw new IndexOutOfRangeException($"Index {index} is out of range in container of '{length}' Length."); 257 } 258 259 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 260 internal static void CheckCapacityInRange(int capacity, int length) 261 { 262 if (capacity < 0) 263 throw new ArgumentOutOfRangeException($"Capacity {capacity} must be positive."); 264 265 if (capacity < length) 266 throw new ArgumentOutOfRangeException($"Capacity {capacity} is out of range in container of '{length}' Length."); 267 } 268 269 /// <summary> 270 /// Create a NativeArray, using a provided allocator that implements IAllocator. 271 /// </summary> 272 /// <typeparam name="T">The type of the elements.</typeparam> 273 /// <typeparam name="U">The type of allocator.</typeparam> 274 /// <param name="length">The number of elements to allocate.</param> 275 /// <param name="allocator">The allocator to use.</param> 276 /// <param name="options">Options for allocation, such as whether to clear the memory.</param> 277 /// <returns>Returns the NativeArray that was created.</returns> 278 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(AllocatorManager.AllocatorHandle) })] 279 public static NativeArray<T> CreateNativeArray<T, U>(int length, ref U allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory) 280 where T : unmanaged 281 where U : unmanaged, AllocatorManager.IAllocator 282 { 283 NativeArray<T> nativeArray; 284 if (!allocator.IsCustomAllocator) 285 { 286 nativeArray = new NativeArray<T>(length, allocator.ToAllocator, options); 287 } 288 else 289 { 290 nativeArray = new NativeArray<T>(); 291 nativeArray.Initialize(length, ref allocator, options); 292 } 293 return nativeArray; 294 } 295 296 /// <summary> 297 /// Create a NativeArray, using a provided AllocatorHandle. 298 /// </summary> 299 /// <typeparam name="T">The type of the elements.</typeparam> 300 /// <param name="length">The number of elements to allocate.</param> 301 /// <param name="allocator">The AllocatorHandle to use.</param> 302 /// <param name="options">Options for allocation, such as whether to clear the memory.</param> 303 /// <returns>Returns the NativeArray that was created.</returns> 304 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 305 public static NativeArray<T> CreateNativeArray<T>(int length, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory) 306 where T : unmanaged 307 { 308 NativeArray<T> nativeArray; 309 if(!AllocatorManager.IsCustomAllocator(allocator)) 310 { 311 nativeArray = new NativeArray<T>(length, allocator.ToAllocator, options); 312 } 313 else 314 { 315 nativeArray = new NativeArray<T>(); 316 nativeArray.Initialize(length, allocator, options); 317 } 318 return nativeArray; 319 } 320 321 /// <summary> 322 /// Create a NativeArray from another NativeArray, using a provided AllocatorHandle. 323 /// </summary> 324 /// <typeparam name="T">The type of the elements.</typeparam> 325 /// <param name="array">The NativeArray to make a copy of.</param> 326 /// <param name="allocator">The AllocatorHandle to use.</param> 327 /// <returns>Returns the NativeArray that was created.</returns> 328 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 329 public static NativeArray<T> CreateNativeArray<T>(NativeArray<T> array, AllocatorManager.AllocatorHandle allocator) 330 where T : unmanaged 331 { 332 NativeArray<T> nativeArray; 333 if (!AllocatorManager.IsCustomAllocator(allocator)) 334 { 335 nativeArray = new NativeArray<T>(array, allocator.ToAllocator); 336 } 337 else 338 { 339 nativeArray = new NativeArray<T>(); 340 nativeArray.Initialize(array.Length, allocator); 341 nativeArray.CopyFrom(array); 342 } 343 return nativeArray; 344 } 345 346 /// <summary> 347 /// Create a NativeArray from a managed array, using a provided AllocatorHandle. 348 /// </summary> 349 /// <typeparam name="T">The type of the elements.</typeparam> 350 /// <param name="array">The managed array to make a copy of.</param> 351 /// <param name="allocator">The AllocatorHandle to use.</param> 352 /// <returns>Returns the NativeArray that was created.</returns> 353 [ExcludeFromBurstCompatTesting("Managed array")] 354 public static NativeArray<T> CreateNativeArray<T>(T[] array, AllocatorManager.AllocatorHandle allocator) 355 where T : unmanaged 356 { 357 NativeArray<T> nativeArray; 358 if (!AllocatorManager.IsCustomAllocator(allocator)) 359 { 360 nativeArray = new NativeArray<T>(array, allocator.ToAllocator); 361 } 362 else 363 { 364 nativeArray = new NativeArray<T>(); 365 nativeArray.Initialize(array.Length, allocator); 366 nativeArray.CopyFrom(array); 367 } 368 return nativeArray; 369 } 370 371 /// <summary> 372 /// Create a NativeArray from a managed array, using a provided Allocator. 373 /// </summary> 374 /// <typeparam name="T">The type of the elements.</typeparam> 375 /// <typeparam name="U">The type of allocator.</typeparam> 376 /// <param name="array">The managed array to make a copy of.</param> 377 /// <param name="allocator">The Allocator to use.</param> 378 /// <returns>Returns the NativeArray that was created.</returns> 379 [ExcludeFromBurstCompatTesting("Managed array")] 380 public static NativeArray<T> CreateNativeArray<T, U>(T[] array, ref U allocator) 381 where T : unmanaged 382 where U : unmanaged, AllocatorManager.IAllocator 383 { 384 NativeArray<T> nativeArray; 385 if (!allocator.IsCustomAllocator) 386 { 387 nativeArray = new NativeArray<T>(array, allocator.ToAllocator); 388 } 389 else 390 { 391 nativeArray = new NativeArray<T>(); 392 nativeArray.Initialize(array.Length, ref allocator); 393 nativeArray.CopyFrom(array); 394 } 395 return nativeArray; 396 } 397 398 /// <summary> 399 /// Dispose a NativeArray from an AllocatorHandle where it is allocated. 400 /// </summary> 401 /// <typeparam name="T">The type of the elements.</typeparam> 402 /// <param name="nativeArray">The NativeArray to make a copy of.</param> 403 /// <param name="allocator">The AllocatorHandle used to allocate the NativeArray.</param> 404 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 405 public static void DisposeNativeArray<T>(NativeArray<T> nativeArray, AllocatorManager.AllocatorHandle allocator) 406 where T : unmanaged 407 { 408 nativeArray.DisposeCheckAllocator(); 409 } 410 411 /// <summary> 412 /// Dispose a NativeArray from an AllocatorHandle where it is allocated. 413 /// </summary> 414 /// <typeparam name="T">The type of the elements.</typeparam> 415 /// <param name="nativeArray">The NativeArray to be disposed.</param> 416 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 417 public static void Dispose<T>(NativeArray<T> nativeArray) 418 where T : unmanaged 419 { 420 nativeArray.DisposeCheckAllocator(); 421 } 422 423 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 424 static void CheckConvertArguments<T>(int length) where T : unmanaged 425 { 426 if (length < 0) 427 throw new ArgumentOutOfRangeException(nameof(length), "Length must be >= 0"); 428 429 if (!UnsafeUtility.IsUnmanaged<T>()) 430 { 431 throw new InvalidOperationException( 432 $"{typeof(T)} used in NativeArray<{typeof(T)}> must be unmanaged (contain no managed types)."); 433 } 434 } 435 436#if ENABLE_UNITY_COLLECTIONS_CHECKS 437 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] 438 static void InitNestedNativeContainer<T>(AtomicSafetyHandle handle) 439 where T : unmanaged 440 { 441 if (UnsafeUtility.IsNativeContainerType<T>()) 442 { 443 AtomicSafetyHandle.SetNestedContainer(handle, true); 444 } 445 } 446#endif 447 448 /// <summary> 449 /// Convert existing data into a NativeArray. 450 /// </summary> 451 /// <typeparam name="T">The type of the elements.</typeparam> 452 /// <param name="dataPointer">Pointer to the data to be converted.</param> 453 /// <param name="length">The count of elements.</param> 454 /// <param name="allocator">The Allocator to use.</param> 455 /// <param name="setTempMemoryHandle">Use temporary memory atomic safety handle.</param> 456 /// <returns>Returns the NativeArray that was created.</returns> 457 /// <remarks>The caller is still the owner of the data.</remarks> 458 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 459 public static unsafe NativeArray<T> ConvertExistingDataToNativeArray<T>(void* dataPointer, int length, AllocatorManager.AllocatorHandle allocator, bool setTempMemoryHandle = false) 460 where T : unmanaged 461 { 462#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG 463 CheckConvertArguments<T>(length); 464#endif 465 NativeArray<T> nativeArray = default; 466 467 nativeArray.m_Buffer = dataPointer; 468 nativeArray.m_Length = length; 469 if (!allocator.IsCustomAllocator) 470 { 471 nativeArray.m_AllocatorLabel = allocator.ToAllocator; 472 } 473 else 474 { 475 nativeArray.m_AllocatorLabel = Allocator.None; 476 } 477 478#if ENABLE_UNITY_COLLECTIONS_CHECKS 479 nativeArray.m_MinIndex = 0; 480 nativeArray.m_MaxIndex = length - 1; 481 if (setTempMemoryHandle) 482 { 483 NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.GetTempMemoryHandle()); 484 } 485#endif 486 return nativeArray; 487 } 488 489 /// <summary> 490 /// Convert NativeList into a NativeArray. 491 /// </summary> 492 /// <typeparam name="T">The type of the elements.</typeparam> 493 /// <param name="nativeList">NativeList to be converted.</param> 494 /// <param name="length">The count of elements.</param> 495 /// <param name="allocator">The Allocator to use.</param> 496 /// <returns>Returns the NativeArray that was created.</returns> 497 /// <remarks>There is a caveat if users would like to transfer memory ownership from the NativeList to the converted NativeArray. 498 /// NativeList implementation includes two memory allocations, one holds its header, another holds the list data. 499 /// After convertion, the converted NativeArray holds the list data and dispose the array only free the list data. 500 /// Users need to manually free the list header to avoid memory leaks, for example after convertion call, 501 /// AllocatorManager.Free(allocator, nativeList.m_ListData); </remarks> 502 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] 503 public static unsafe NativeArray<T> ConvertExistingNativeListToNativeArray<T>(ref NativeList<T> nativeList, int length, AllocatorManager.AllocatorHandle allocator) 504 where T : unmanaged 505 { 506 NativeArray<T> nativeArray = ConvertExistingDataToNativeArray<T>(nativeList.GetUnsafePtr(), length, allocator); 507 508#if ENABLE_UNITY_COLLECTIONS_CHECKS 509 var safetyHandle = NativeListUnsafeUtility.GetAtomicSafetyHandle(ref nativeList); 510 NativeArrayUnsafeUtility.SetAtomicSafetyHandle<T>(ref nativeArray, safetyHandle); 511 InitNestedNativeContainer<T>(nativeArray.m_Safety); 512#endif 513 return nativeArray; 514 } 515 516#if ENABLE_UNITY_COLLECTIONS_CHECKS 517 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)] 518 internal static AtomicSafetyHandle GetNativeArraySafetyHandle<T>(ref NativeArray<T> nativeArray) 519 where T : unmanaged 520 { 521 return nativeArray.m_Safety; 522 } 523#endif 524 525 /// <summary> 526 /// Create a NativeParallelMultiHashMap from a managed array, using a provided Allocator. 527 /// </summary> 528 /// <typeparam name="TKey">The type of the keys.</typeparam> 529 /// <typeparam name="TValue">The type of the values.</typeparam> 530 /// <typeparam name="U">The type of allocator.</typeparam> 531 /// <param name="length">The desired capacity of the NativeParallelMultiHashMap.</param> 532 /// <param name="allocator">The Allocator to use.</param> 533 /// <returns>Returns the NativeParallelMultiHashMap that was created.</returns> 534 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int), typeof(AllocatorManager.AllocatorHandle) })] 535 public static NativeParallelMultiHashMap<TKey, TValue> CreateNativeParallelMultiHashMap<TKey, TValue, U>(int length, ref U allocator) 536 where TKey : unmanaged, IEquatable<TKey> 537 where TValue : unmanaged 538 where U : unmanaged, AllocatorManager.IAllocator 539 { 540 var container = new NativeParallelMultiHashMap<TKey, TValue>(); 541 container.Initialize(length, ref allocator); 542 return container; 543 } 544 545 /// <summary> 546 /// Empty job type used for Burst compilation testing 547 /// </summary> 548 [BurstCompile] 549 public struct DummyJob : IJob 550 { 551 /// <summary> 552 /// Empty job execute function used for Burst compilation testing 553 /// </summary> 554 public void Execute() 555 { 556 } 557 } 558 559 /// <summary> 560 /// Checks that reflection data was properly registered for a job. 561 /// </summary> 562 /// <remarks>This should be called before instantiating JobsUtility.JobScheduleParameters in order to report to the user if they need to take action.</remarks> 563 /// <param name="reflectionData">The reflection data pointer.</param> 564 /// <typeparam name="T">Job type</typeparam> 565 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", 566 GenericTypeArguments = new[] { typeof(DummyJob) })] 567 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 568 public static void CheckReflectionDataCorrect<T>(IntPtr reflectionData) 569 { 570#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG 571 bool burstCompiled = true; 572 CheckReflectionDataCorrectInternal<T>(reflectionData, ref burstCompiled); 573 if (burstCompiled && reflectionData == IntPtr.Zero) 574 throw new InvalidOperationException("Reflection data was not set up by an Initialize() call. For generic job types, please include [assembly: RegisterGenericJobType(typeof(MyJob<MyJobSpecialization>))] in your source file."); 575#endif 576 } 577 578#if ENABLE_UNITY_COLLECTIONS_CHECKS 579 /// <summary> 580 /// Creates a new AtomicSafetyHandle that is valid until [[CollectionHelper.DisposeSafetyHandle]] is called. 581 /// </summary> 582 /// <param name="allocator">The AllocatorHandle to use.</param> 583 /// <returns>Safety handle.</returns> 584 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")] 585 public static AtomicSafetyHandle CreateSafetyHandle(AllocatorManager.AllocatorHandle allocator) 586 { 587 if (allocator.IsCustomAllocator) 588 { 589 return AtomicSafetyHandle.Create(); 590 } 591 592 return (allocator.ToAllocator == Allocator.Temp) ? AtomicSafetyHandle.GetTempMemoryHandle() : AtomicSafetyHandle.Create(); 593 } 594 595 /// <summary> 596 /// Disposes a previously created AtomicSafetyHandle. 597 /// </summary> 598 /// <param name="handle">Safety handle.</param> 599 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")] 600 public static void DisposeSafetyHandle(ref AtomicSafetyHandle handle) 601 { 602 AtomicSafetyHandle.CheckDeallocateAndThrow(handle); 603 // If the safety handle is for a temp allocation, create a new safety handle for this instance which can be marked as invalid 604 // Setting it to new AtomicSafetyHandle is not enough since the handle needs a valid node pointer in order to give the correct errors 605 if (AtomicSafetyHandle.IsTempMemoryHandle(handle)) 606 { 607 int staticSafetyId = handle.staticSafetyId; 608 handle = AtomicSafetyHandle.Create(); 609 handle.staticSafetyId = staticSafetyId; 610 } 611 AtomicSafetyHandle.Release(handle); 612 } 613 614 static unsafe void CreateStaticSafetyIdInternal(ref int id, in FixedString512Bytes name) 615 { 616 id = AtomicSafetyHandle.NewStaticSafetyId(name.GetUnsafePtr(), name.Length); 617 } 618 619 [BurstDiscard] 620 static void CreateStaticSafetyIdInternal<T>(ref int id) 621 { 622 CreateStaticSafetyIdInternal(ref id, typeof(T).ToString()); 623 } 624 625 /// <summary> 626 /// Assigns the provided static safety ID to an [[AtomicSafetyHandle]]. The ID's owner type name and any custom error messages are used by the job debugger when reporting errors involving the target handle. 627 /// </summary> 628 /// <remarks>This is preferable to AtomicSafetyHandle.NewStaticSafetyId as it is compatible with burst.</remarks> 629 /// <typeparam name="T">Type of container safety handle refers to.</typeparam> 630 /// <param name="handle">Safety handle.</param> 631 /// <param name="sharedStaticId">The static safety ID to associate with the provided handle. This ID must have been allocated with ::ref::NewStaticSafetyId.</param> 632 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", GenericTypeArguments = new[] { typeof(NativeArray<int>) })] 633 public static void SetStaticSafetyId<T>(ref AtomicSafetyHandle handle, ref int sharedStaticId) 634 { 635 if (sharedStaticId == 0) 636 { 637 // This will eventually either work with burst supporting a subset of typeof() 638 // or something similar to Burst.BurstRuntime.GetTypeName() will be implemented 639 // JIRA issue DOTS-5685 640 641 CreateStaticSafetyIdInternal<T>(ref sharedStaticId); 642 } 643 644 AtomicSafetyHandle.SetStaticSafetyId(ref handle, sharedStaticId); 645 } 646 647 /// <summary> 648 /// Assigns the provided static safety ID to an [[AtomicSafetyHandle]]. The ID's owner type name and any custom error messages are used by the job debugger when reporting errors involving the target handle. 649 /// </summary> 650 /// <remarks>This is preferable to AtomicSafetyHandle.NewStaticSafetyId as it is compatible with burst.</remarks> 651 /// <param name="handle">Safety handle.</param> 652 /// <param name="sharedStaticId">The static safety ID to associate with the provided handle. This ID must have been allocated with ::ref::NewStaticSafetyId.</param> 653 /// <param name="name">The name of the resource type.</param> 654 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")] 655 public static unsafe void SetStaticSafetyId(ref AtomicSafetyHandle handle, ref int sharedStaticId, FixedString512Bytes name) 656 { 657 if (sharedStaticId == 0) 658 { 659 CreateStaticSafetyIdInternal(ref sharedStaticId, name); 660 } 661 662 AtomicSafetyHandle.SetStaticSafetyId(ref handle, sharedStaticId); 663 } 664#endif 665 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 666 [BurstDiscard] 667 static void CheckReflectionDataCorrectInternal<T>(IntPtr reflectionData, ref bool burstCompiled) 668 { 669 if (reflectionData == IntPtr.Zero) 670 throw new InvalidOperationException($"Reflection data was not set up by an Initialize() call. For generic job types, please include [assembly: RegisterGenericJobType(typeof({typeof(T)}))] in your source file."); 671 burstCompiled = false; 672 } 673 } 674}