설명 없음
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AllocatorCustomTests.cs 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. #region allocator-custom-example
  2. using System;
  3. using AOT;
  4. using System.Collections.Generic;
  5. using System.Threading.Tasks;
  6. using NUnit.Framework;
  7. using Unity.Collections;
  8. using Unity.Collections.LowLevel.Unsafe;
  9. using Unity.Burst;
  10. using System.Threading;
  11. // This is the example code used in
  12. // Packages/com.unity.collections/Documentation~/allocator/allocator-custom.md
  13. // Example custom allocator. The allocator is able to allocate memory from Allocator.Persistant,
  14. // if successful, initialize the allocated memory with a user configured value and increment an
  15. // allocation count. The allocator is able to deallocate the memory, if successful, decrement
  16. // the allocation count.
  17. // A custom allocator must implement AllocatorManager.IAllocator interface
  18. [BurstCompile(CompileSynchronously = true)]
  19. internal struct ExampleCustomAllocator : AllocatorManager.IAllocator
  20. {
  21. // A custom allocator must contain AllocatorManager.AllocatorHandle
  22. AllocatorManager.AllocatorHandle m_handle;
  23. // Implement the Function property required by IAllocator interface
  24. public AllocatorManager.TryFunction Function => AllocatorFunction;
  25. // Implement the Handle property required by IAllocator interface
  26. public AllocatorManager.AllocatorHandle Handle { get { return m_handle; } set { m_handle = value; } }
  27. // Implement the ToAllocator property required by IAllocator interface
  28. public Allocator ToAllocator { get { return m_handle.ToAllocator; } }
  29. // Implement the IsCustomAllocator property required by IAllocator interface
  30. public bool IsCustomAllocator { get { return m_handle.IsCustomAllocator; } }
  31. // Implement the IsAutoDispose property required by IAllocator interface
  32. // Allocations made by this example allocator are not automatically disposed.
  33. // This implementation can be skipped because the default implementation of
  34. // this property is false.
  35. public bool IsAutoDispose { get { return false; } }
  36. // Implement the Dispose method required by IDisposable interface because
  37. // AllocatorManager.IAllocator implements IDisposable
  38. public void Dispose()
  39. {
  40. // Make sure no memory leaks
  41. Assert.AreEqual(0, m_allocationCount);
  42. m_handle.Dispose();
  43. }
  44. #region allocator-custom-try
  45. // Value to initialize the allocated memory
  46. byte m_initialValue;
  47. // Allocation count
  48. int m_allocationCount;
  49. // Implement the Try method required by IAllocator interface
  50. public unsafe int Try(ref AllocatorManager.Block block)
  51. {
  52. // Error status
  53. int error = 0;
  54. // Allocate
  55. if (block.Range.Pointer == IntPtr.Zero)
  56. {
  57. // Allocate memory from Allocator.Persistant and restore the original allocator
  58. AllocatorManager.AllocatorHandle tempAllocator = block.Range.Allocator;
  59. block.Range.Allocator = Allocator.Persistent;
  60. error = AllocatorManager.Try(ref block);
  61. block.Range.Allocator = tempAllocator;
  62. // return if error occurs
  63. if (error != 0)
  64. return error;
  65. // if allocation succeeds, intialize the memory with the initial value and increment the allocation count
  66. if (block.Range.Pointer != IntPtr.Zero)
  67. {
  68. UnsafeUtility.MemSet((void*)block.Range.Pointer, m_initialValue, block.Bytes);
  69. Interlocked.Increment(ref m_allocationCount);
  70. }
  71. return 0;
  72. }
  73. // Deallocate
  74. else
  75. {
  76. // Deallocate memory from Allocator.Persistant and restore the original allocator
  77. AllocatorManager.AllocatorHandle tempAllocator = block.Range.Allocator;
  78. block.Range.Allocator = Allocator.Persistent;
  79. error = AllocatorManager.Try(ref block);
  80. block.Range.Allocator = tempAllocator;
  81. // return if error occurs
  82. if (error != 0)
  83. return error;
  84. // if deallocation succeeds, decrement the allocation count
  85. if (block.Range.Pointer == IntPtr.Zero)
  86. {
  87. Interlocked.Decrement(ref m_allocationCount);
  88. }
  89. return 0;
  90. }
  91. }
  92. #endregion // allocator-custom-try
  93. #region allocator-custom-allocator-function
  94. // Implement the allocator function of delegate AllocatorManager.TryFunction that is
  95. // required when register the allocator on the global allocator table
  96. [BurstCompile(CompileSynchronously = true)]
  97. [MonoPInvokeCallback(typeof(AllocatorManager.TryFunction))]
  98. public static unsafe int AllocatorFunction(IntPtr customAllocatorPtr, ref AllocatorManager.Block block)
  99. {
  100. return ((ExampleCustomAllocator*)customAllocatorPtr)->Try(ref block);
  101. }
  102. #endregion // allocator-custom-allocator-function
  103. // Property to get the initial value
  104. public byte InitialValue => m_initialValue;
  105. // Property to get the allocation count
  106. public int AllocationCount => m_allocationCount;
  107. // Initialize the allocator
  108. public void Initialize(byte initialValue)
  109. {
  110. m_initialValue = initialValue;
  111. m_allocationCount = 0;
  112. }
  113. }
  114. #endregion // allocator-custom-example
  115. #region allocator-custom-user-struct
  116. // Example user structure that contains the custom allocator
  117. internal struct ExampleCustomAllocatorStruct
  118. {
  119. // Use AllocatorHelper to help creating the example custom alloctor
  120. AllocatorHelper<ExampleCustomAllocator> customAllocatorHelper;
  121. // Custom allocator property for accessibility
  122. public ref ExampleCustomAllocator customAllocator => ref customAllocatorHelper.Allocator;
  123. // Create the example custom allocator
  124. void CreateCustomAllocator(AllocatorManager.AllocatorHandle backgroundAllocator, byte initialValue)
  125. {
  126. // Allocate the custom allocator from backgroundAllocator and register the allocator
  127. customAllocatorHelper = new AllocatorHelper<ExampleCustomAllocator>(backgroundAllocator);
  128. // Set the initial value to initialize the memory
  129. customAllocator.Initialize(initialValue);
  130. }
  131. #region allocator-custom-dispose
  132. // Dispose the custom allocator
  133. void DisposeCustomAllocator()
  134. {
  135. // Dispose the custom allocator
  136. customAllocator.Dispose();
  137. // Unregister the custom allocator and dispose it
  138. customAllocatorHelper.Dispose();
  139. }
  140. #endregion // allocator-custom-dispose
  141. // Constructor of user structure
  142. public ExampleCustomAllocatorStruct(byte initialValue)
  143. {
  144. this = default;
  145. CreateCustomAllocator(Allocator.Persistent, initialValue);
  146. }
  147. // Dispose the user structure
  148. public void Dispose()
  149. {
  150. DisposeCustomAllocator();
  151. }
  152. #region allocator-custom-use
  153. // Sample code to use the custom allocator to allocate containers
  154. public void UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
  155. {
  156. // Use custom allocator to allocate a native array and check initial value.
  157. nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref customAllocator, NativeArrayOptions.UninitializedMemory);
  158. Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
  159. nativeArray[0] = 0xFE;
  160. // Use custom allocator to allocate a native list and check initial value.
  161. nativeList = new NativeList<int>(customAllocator.Handle);
  162. for (int i = 0; i < 50; i++)
  163. {
  164. nativeList.Add(i);
  165. }
  166. unsafe
  167. {
  168. // Use custom allocator to allocate a byte buffer.
  169. byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
  170. Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);
  171. // Free the byte buffer.
  172. AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
  173. }
  174. }
  175. #endregion // allocator-custom-use
  176. // Get allocation count from the custom allocator
  177. public int AllocationCount => customAllocator.AllocationCount;
  178. public void UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
  179. {
  180. // Use custom allocator to allocate a native array and check initial value.
  181. nativeArray = CollectionHelper.CreateNativeArray<int>(100, customAllocator.ToAllocator, NativeArrayOptions.UninitializedMemory);
  182. Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
  183. nativeArray[0] = 0xFE;
  184. // Use custom allocator to allocate a native list and check initial value.
  185. nativeList = new NativeList<int>(customAllocator.Handle);
  186. for (int i = 0; i < 50; i++)
  187. {
  188. nativeList.Add(i);
  189. }
  190. unsafe
  191. {
  192. // Use custom allocator to allocate a byte buffer.
  193. byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
  194. Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);
  195. // Free the byte buffer.
  196. AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
  197. }
  198. }
  199. }
  200. internal class ExampleCustomAllocatorStructUsage
  201. {
  202. // Initial value for the custom allocator.
  203. const int IntialValue = 0xAB;
  204. // Test code.
  205. [Test]
  206. public void UseCustomAllocator_Works()
  207. {
  208. ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);
  209. // Allocate native array and native list from the custom allocator
  210. exampleStruct.UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList);
  211. // Able to access the native array and native list
  212. Assert.AreEqual(nativeArray[0], 0xFE);
  213. Assert.AreEqual(nativeList[10], 10);
  214. // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
  215. CollectionHelper.Dispose(nativeArray) ;
  216. // Dispose the native list
  217. nativeList.Dispose();
  218. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  219. // Object disposed exception throws because nativeArray is already disposed
  220. Assert.Throws<ObjectDisposedException>(() =>
  221. {
  222. nativeArray[0] = 0xEF;
  223. });
  224. // Object disposed exception throws because nativeList is already disposed
  225. Assert.Throws<ObjectDisposedException>(() =>
  226. {
  227. nativeList[10] = 0x10;
  228. });
  229. #endif
  230. // Check allocation count after dispose the native array and native list
  231. Assert.AreEqual(0, exampleStruct.AllocationCount);
  232. // Dispose the user structure
  233. exampleStruct.Dispose();
  234. }
  235. [Test]
  236. public void UseCustomAllocatorHandle_Works()
  237. {
  238. ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);
  239. // Allocate native array and native list from the custom allocator handle
  240. exampleStruct.UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList);
  241. // Able to access the native array and native list
  242. Assert.AreEqual(nativeArray[0], 0xFE);
  243. Assert.AreEqual(nativeList[10], 10);
  244. // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
  245. CollectionHelper.Dispose(nativeArray);
  246. // Dispose the native list
  247. nativeList.Dispose();
  248. #if ENABLE_UNITY_COLLECTIONS_CHECKS
  249. // Object disposed exception throws because nativeArray is already disposed
  250. Assert.Throws<ObjectDisposedException>(() =>
  251. {
  252. nativeArray[0] = 0xEF;
  253. });
  254. // Object disposed exception throws because nativeList is already disposed
  255. Assert.Throws<ObjectDisposedException>(() =>
  256. {
  257. nativeList[10] = 0x10;
  258. });
  259. #endif
  260. // Check allocation count after dispose the native array and native list
  261. Assert.AreEqual(0, exampleStruct.AllocationCount);
  262. // Dispose the user structure
  263. exampleStruct.Dispose();
  264. }
  265. [Test]
  266. public void CustomAllocatorHandle_MultiThreadWorks()
  267. {
  268. ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);
  269. var taskList = new List<Task>();
  270. // create 128 native array with another threads
  271. for (var i = 0; i < 128; i++)
  272. {
  273. var task = Task.Run(() =>
  274. {
  275. var nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref exampleStruct.customAllocator,
  276. NativeArrayOptions.UninitializedMemory);
  277. CollectionHelper.Dispose(nativeArray);
  278. });
  279. taskList.Add(task);
  280. }
  281. Task.WaitAll(taskList.ToArray());
  282. exampleStruct.Dispose();
  283. }
  284. }
  285. #endregion // allocator-custom-user-struct