A game about forced loneliness, made by TACStudios
1// Example code used in trunk, unity/Documentation/ApiDocs/Unity.Collections.LowLevel.Unsafe/NativeContainerAttribute.mem.xml
2
3using System.Diagnostics;
4using System;
5using Unity.Collections.LowLevel.Unsafe;
6using Unity.Collections;
7using Unity.Burst;
8
9// Marks our struct as a NativeContainer.
10// If ENABLE_UNITY_COLLECTIONS_CHECKS is enabled,
11// it is required that m_Safety with exactly this name.
12[NativeContainer]
13// The [NativeContainerSupportsMinMaxWriteRestriction] enables
14// a common jobification pattern where an IJobParallelFor is split into ranges
15// And the job is only allowed to access the index range being Executed by that worker thread.
16// Effectively limiting access of the array to the specific index passed into the Execute(int index) method
17// This attribute requires m_MinIndex & m_MaxIndex to exist.
18// and the container is expected to perform out of bounds checks against it.
19// m_MinIndex & m_MaxIndex will be set by the job scheduler before Execute is called on the worker thread.
20[NativeContainerSupportsMinMaxWriteRestriction]
21// It is recommended to always implement a Debugger proxy
22// to visualize the contents of the array in VisualStudio and other tools.
23[DebuggerDisplay("Length = {Length}")]
24[DebuggerTypeProxy(typeof(NativeCustomArrayDebugView<>))]
25internal unsafe struct NativeCustomArray<T> : IDisposable where T : unmanaged
26{
27 internal void* m_Buffer;
28 internal int m_Length;
29
30#if ENABLE_UNITY_COLLECTIONS_CHECKS
31 internal int m_MinIndex;
32 internal int m_MaxIndex;
33 internal AtomicSafetyHandle m_Safety;
34 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeCustomArray<T>>();
35#endif
36
37 internal Allocator m_AllocatorLabel;
38
39 public NativeCustomArray(int length, Allocator allocator)
40 {
41 int totalSize = UnsafeUtility.SizeOf<T>() * length;
42
43#if ENABLE_UNITY_COLLECTIONS_CHECKS
44 // Native allocation is only valid for Temp, TempJob, Persistent or registered custom allocator
45 if (allocator <= Allocator.None)
46 throw new ArgumentException("Allocator must be Temp, TempJob, Persistent or registered custom allcoator", "allocator");
47 if (length < 0)
48 throw new ArgumentOutOfRangeException("length", "Length must be >= 0");
49 if (!UnsafeUtility.IsBlittable<T>())
50 throw new ArgumentException(string.Format("{0} used in NativeCustomArray<{0}> must be blittable", typeof(T)));
51#endif
52
53 m_Buffer = AllocatorManager.Allocate(allocator, totalSize, UnsafeUtility.AlignOf<T>());
54 UnsafeUtility.MemClear(m_Buffer, totalSize);
55
56 m_Length = length;
57 m_AllocatorLabel = allocator;
58
59#if ENABLE_UNITY_COLLECTIONS_CHECKS
60 m_MinIndex = 0;
61 m_MaxIndex = length - 1;
62 m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
63 CollectionHelper.SetStaticSafetyId<NativeCustomArray<T>>(ref m_Safety, ref s_staticSafetyId.Data);
64#endif
65 }
66
67 public int Length { get { return m_Length; } }
68
69 public unsafe T this[int index]
70 {
71 get
72 {
73#if ENABLE_UNITY_COLLECTIONS_CHECKS
74 // If the container is currently not allowed to read from the buffer
75 // then this will throw an exception.
76 // This handles all cases, from already disposed containers
77 // to safe multithreaded access.
78 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
79
80 // Perform out of range checks based on
81 // the NativeContainerSupportsMinMaxWriteRestriction policy
82 if (index < m_MinIndex || index > m_MaxIndex)
83 FailOutOfRangeError(index);
84#endif
85 // Read the element from the allocated native memory
86 return UnsafeUtility.ReadArrayElement<T>(m_Buffer, index);
87 }
88
89 set
90 {
91#if ENABLE_UNITY_COLLECTIONS_CHECKS
92 // If the container is currently not allowed to write to the buffer
93 // then this will throw an exception.
94 // This handles all cases, from already disposed containers
95 // to safe multithreaded access.
96 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
97
98 // Perform out of range checks based on
99 // the NativeContainerSupportsMinMaxWriteRestriction policy
100 if (index < m_MinIndex || index > m_MaxIndex)
101 FailOutOfRangeError(index);
102#endif
103 // Writes value to the allocated native memory
104 UnsafeUtility.WriteArrayElement(m_Buffer, index, value);
105 }
106 }
107
108 public T[] ToArray()
109 {
110#if ENABLE_UNITY_COLLECTIONS_CHECKS
111 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
112#endif
113
114 var array = new T[Length];
115 for (var i = 0; i < Length; i++)
116 array[i] = UnsafeUtility.ReadArrayElement<T>(m_Buffer, i);
117 return array;
118 }
119
120 public bool IsCreated
121 {
122 get { return m_Buffer != null; }
123 }
124
125 public void Dispose()
126 {
127#if ENABLE_UNITY_COLLECTIONS_CHECKS
128 CollectionHelper.DisposeSafetyHandle(ref m_Safety);
129#endif
130
131 AllocatorManager.Free(m_AllocatorLabel, m_Buffer);
132 m_Buffer = null;
133 m_Length = 0;
134 }
135
136#if ENABLE_UNITY_COLLECTIONS_CHECKS
137 private void FailOutOfRangeError(int index)
138 {
139 if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1))
140 throw new IndexOutOfRangeException(string.Format(
141 "Index {0} is out of restricted IJobParallelFor range [{1}...{2}] in ReadWriteBuffer.\n" +
142 "ReadWriteBuffers are restricted to only read & write the element at the job index. " +
143 "You can use double buffering strategies to avoid race conditions due to " +
144 "reading & writing in parallel to the same elements from a job.",
145 index, m_MinIndex, m_MaxIndex));
146
147 throw new IndexOutOfRangeException(string.Format("Index {0} is out of range of '{1}' Length.", index, Length));
148 }
149
150#endif
151}
152
153// Visualizes the custom array in the C# debugger
154internal sealed class NativeCustomArrayDebugView<T> where T : unmanaged
155{
156 private NativeCustomArray<T> m_Array;
157
158 public NativeCustomArrayDebugView(NativeCustomArray<T> array)
159 {
160 m_Array = array;
161 }
162
163 public T[] Items
164 {
165 get { return m_Array.ToArray(); }
166 }
167}