Нет описания
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

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