168 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			168 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | // Example code used in trunk, unity/Documentation/ApiDocs/Unity.Collections.LowLevel.Unsafe/NativeContainerAttribute.mem.xml | ||
|  | 
 | ||
|  | using System.Diagnostics; | ||
|  | using System; | ||
|  | using Unity.Collections.LowLevel.Unsafe; | ||
|  | using Unity.Collections; | ||
|  | using Unity.Burst; | ||
|  | 
 | ||
|  | // Marks our struct as a NativeContainer. | ||
|  | // If ENABLE_UNITY_COLLECTIONS_CHECKS is enabled, | ||
|  | // it is required that m_Safety with exactly this name. | ||
|  | [NativeContainer] | ||
|  | // The [NativeContainerSupportsMinMaxWriteRestriction] enables | ||
|  | // a common jobification pattern where an IJobParallelFor is split into ranges | ||
|  | // And the job is only allowed to access the index range being Executed by that worker thread. | ||
|  | // Effectively limiting access of the array to the specific index passed into the Execute(int index) method | ||
|  | // This attribute requires m_MinIndex & m_MaxIndex to exist. | ||
|  | // and the container is expected to perform out of bounds checks against it. | ||
|  | // m_MinIndex & m_MaxIndex will be set by the job scheduler before Execute is called on the worker thread. | ||
|  | [NativeContainerSupportsMinMaxWriteRestriction] | ||
|  | // It is recommended to always implement a Debugger proxy | ||
|  | // to visualize the contents of the array in VisualStudio and other tools. | ||
|  | [DebuggerDisplay("Length = {Length}")] | ||
|  | [DebuggerTypeProxy(typeof(NativeCustomArrayDebugView<>))] | ||
|  | internal unsafe struct NativeCustomArray<T> : IDisposable where T : unmanaged | ||
|  | { | ||
|  |     internal void* m_Buffer; | ||
|  |     internal int m_Length; | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |     internal int m_MinIndex; | ||
|  |     internal int m_MaxIndex; | ||
|  |     internal AtomicSafetyHandle m_Safety; | ||
|  |     internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeCustomArray<T>>(); | ||
|  | #endif | ||
|  | 
 | ||
|  |     internal Allocator m_AllocatorLabel; | ||
|  | 
 | ||
|  |     public NativeCustomArray(int length, Allocator allocator) | ||
|  |     { | ||
|  |         int totalSize = UnsafeUtility.SizeOf<T>() * length; | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |         // Native allocation is only valid for Temp, TempJob, Persistent or registered custom allocator | ||
|  |         if (allocator <= Allocator.None) | ||
|  |             throw new ArgumentException("Allocator must be Temp, TempJob, Persistent or registered custom allcoator", "allocator"); | ||
|  |         if (length < 0) | ||
|  |             throw new ArgumentOutOfRangeException("length", "Length must be >= 0"); | ||
|  |         if (!UnsafeUtility.IsBlittable<T>()) | ||
|  |             throw new ArgumentException(string.Format("{0} used in NativeCustomArray<{0}> must be blittable", typeof(T))); | ||
|  | #endif | ||
|  | 
 | ||
|  |         m_Buffer = AllocatorManager.Allocate(allocator, totalSize, UnsafeUtility.AlignOf<T>()); | ||
|  |         UnsafeUtility.MemClear(m_Buffer, totalSize); | ||
|  | 
 | ||
|  |         m_Length = length; | ||
|  |         m_AllocatorLabel = allocator; | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |         m_MinIndex = 0; | ||
|  |         m_MaxIndex = length - 1; | ||
|  |         m_Safety = CollectionHelper.CreateSafetyHandle(allocator); | ||
|  |         CollectionHelper.SetStaticSafetyId<NativeCustomArray<T>>(ref m_Safety, ref s_staticSafetyId.Data); | ||
|  | #endif | ||
|  |     } | ||
|  | 
 | ||
|  |     public int Length { get { return m_Length; } } | ||
|  | 
 | ||
|  |     public unsafe T this[int index] | ||
|  |     { | ||
|  |         get | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             // If the container is currently not allowed to read from the buffer | ||
|  |             // then this will throw an exception. | ||
|  |             // This handles all cases, from already disposed containers | ||
|  |             // to safe multithreaded access. | ||
|  |             AtomicSafetyHandle.CheckReadAndThrow(m_Safety); | ||
|  | 
 | ||
|  |             // Perform out of range checks based on | ||
|  |             // the NativeContainerSupportsMinMaxWriteRestriction policy | ||
|  |             if (index < m_MinIndex || index > m_MaxIndex) | ||
|  |                 FailOutOfRangeError(index); | ||
|  | #endif | ||
|  |             // Read the element from the allocated native memory | ||
|  |             return UnsafeUtility.ReadArrayElement<T>(m_Buffer, index); | ||
|  |         } | ||
|  | 
 | ||
|  |         set | ||
|  |         { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |             // If the container is currently not allowed to write to the buffer | ||
|  |             // then this will throw an exception. | ||
|  |             // This handles all cases, from already disposed containers | ||
|  |             // to safe multithreaded access. | ||
|  |             AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); | ||
|  | 
 | ||
|  |             // Perform out of range checks based on | ||
|  |             // the NativeContainerSupportsMinMaxWriteRestriction policy | ||
|  |             if (index < m_MinIndex || index > m_MaxIndex) | ||
|  |                 FailOutOfRangeError(index); | ||
|  | #endif | ||
|  |             // Writes value to the allocated native memory | ||
|  |             UnsafeUtility.WriteArrayElement(m_Buffer, index, value); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     public T[] ToArray() | ||
|  |     { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |         AtomicSafetyHandle.CheckReadAndThrow(m_Safety); | ||
|  | #endif | ||
|  | 
 | ||
|  |         var array = new T[Length]; | ||
|  |         for (var i = 0; i < Length; i++) | ||
|  |             array[i] = UnsafeUtility.ReadArrayElement<T>(m_Buffer, i); | ||
|  |         return array; | ||
|  |     } | ||
|  | 
 | ||
|  |     public bool IsCreated | ||
|  |     { | ||
|  |         get { return m_Buffer != null; } | ||
|  |     } | ||
|  | 
 | ||
|  |     public void Dispose() | ||
|  |     { | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |         CollectionHelper.DisposeSafetyHandle(ref m_Safety); | ||
|  | #endif | ||
|  | 
 | ||
|  |         AllocatorManager.Free(m_AllocatorLabel, m_Buffer); | ||
|  |         m_Buffer = null; | ||
|  |         m_Length = 0; | ||
|  |     } | ||
|  | 
 | ||
|  | #if ENABLE_UNITY_COLLECTIONS_CHECKS | ||
|  |     private void FailOutOfRangeError(int index) | ||
|  |     { | ||
|  |         if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1)) | ||
|  |             throw new IndexOutOfRangeException(string.Format( | ||
|  |                 "Index {0} is out of restricted IJobParallelFor range [{1}...{2}] in ReadWriteBuffer.\n" + | ||
|  |                 "ReadWriteBuffers are restricted to only read & write the element at the job index. " + | ||
|  |                 "You can use double buffering strategies to avoid race conditions due to " + | ||
|  |                 "reading & writing in parallel to the same elements from a job.", | ||
|  |                 index, m_MinIndex, m_MaxIndex)); | ||
|  | 
 | ||
|  |         throw new IndexOutOfRangeException(string.Format("Index {0} is out of range of '{1}' Length.", index, Length)); | ||
|  |     } | ||
|  | 
 | ||
|  | #endif | ||
|  | } | ||
|  | 
 | ||
|  | // Visualizes the custom array in the C# debugger | ||
|  | internal sealed class NativeCustomArrayDebugView<T> where T : unmanaged | ||
|  | { | ||
|  |     private NativeCustomArray<T> m_Array; | ||
|  | 
 | ||
|  |     public NativeCustomArrayDebugView(NativeCustomArray<T> array) | ||
|  |     { | ||
|  |         m_Array = array; | ||
|  |     } | ||
|  | 
 | ||
|  |     public T[] Items | ||
|  |     { | ||
|  |         get { return m_Array.ToArray(); } | ||
|  |     } | ||
|  | } |