A game about forced loneliness, made by TACStudios
at master 192 lines 7.5 kB view raw
1using System; 2using System.Collections.Generic; 3using System.Runtime.CompilerServices; 4using Unity.Mathematics; 5 6namespace UnityEngine.Rendering 7{ 8 /// <summary> 9 /// ContextContainer is a Dictionary like storage where the key is a generic parameter and the value is of the same type. 10 /// </summary> 11 public class ContextContainer : IDisposable 12 { 13 Item[] m_Items = new Item[64]; 14 List<uint> m_ActiveItemIndices = new(); 15 16 /// <summary> 17 /// Retrives a T of class <c>ContextContainerItem</c> if it was previously created without it being disposed. 18 /// </summary> 19 /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam> 20 /// <returns>The value created previously using <![CDATA[Create<T>]]> .</returns> 21 /// <exception cref="InvalidOperationException">This is thown if the value isn't previously created.</exception> 22 [MethodImpl(MethodImplOptions.AggressiveInlining)] 23 public T Get<T>() 24 where T : ContextItem, new() 25 { 26 var typeId = TypeId<T>.value; 27 if (!Contains(typeId)) 28 { 29 throw new InvalidOperationException($"Type {typeof(T).FullName} has not been created yet."); 30 } 31 32 return (T) m_Items[typeId].storage; 33 34 } 35 36 /// <summary> 37 /// Creates the value of type T. 38 /// </summary> 39 /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam> 40 /// <returns>The value of type T created inside the <c>ContextContainer</c>.</returns> 41 /// <exception cref="InvalidOperationException">Thown if you try to create the value of type T agian after it is already created.</exception> 42 [MethodImpl(MethodImplOptions.AggressiveInlining)] 43#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 44 public T Create<T>([CallerLineNumber] int lineNumber = 0, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "") 45#else 46 public T Create<T>() 47#endif 48 where T : ContextItem, new() 49 { 50 var typeId = TypeId<T>.value; 51 if (Contains(typeId)) 52 { 53#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 54 throw new InvalidOperationException($"Type {typeof(T).FullName} has already been created. It was previously created in member {m_Items[typeId].memberName} at line {m_Items[typeId].lineNumber} in {m_Items[typeId].filePath}."); 55#else 56 throw new InvalidOperationException($"Type {typeof(T).FullName} has already been created."); 57#endif 58 } 59 60#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 61 return CreateAndGetData<T>(typeId, lineNumber, memberName, filePath); 62#else 63 return CreateAndGetData<T>(typeId); 64#endif 65 } 66 67 /// <summary> 68 /// Creates the value of type T if the value is not previously created otherwise try to get the value of type T. 69 /// </summary> 70 /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam> 71 /// <returns>Returns the value of type T which is created or retrived.</returns> 72 [MethodImpl(MethodImplOptions.AggressiveInlining)] 73#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 74 public T GetOrCreate<T>([CallerLineNumber] int lineNumber = 0, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "") 75#else 76 public T GetOrCreate<T>() 77#endif 78 where T : ContextItem, new() 79 { 80 var typeId = TypeId<T>.value; 81 if (Contains(typeId)) 82 { 83 return (T) m_Items[typeId].storage; 84 } 85 86 87#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 88 return CreateAndGetData<T>(typeId, lineNumber, memberName, filePath); 89#else 90 return CreateAndGetData<T>(typeId); 91#endif 92 } 93 94 /// <summary> 95 /// Check if the value of type T has previously been created. 96 /// </summary> 97 /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam> 98 /// <returns>Returns true if the value exists and false otherwise.</returns> 99 [MethodImpl(MethodImplOptions.AggressiveInlining)] 100 public bool Contains<T>() 101 where T : ContextItem, new() 102 { 103 var typeId = TypeId<T>.value; 104 return Contains(typeId); 105 } 106 107 [MethodImpl(MethodImplOptions.AggressiveInlining)] 108 bool Contains(uint typeId) => typeId < m_Items.Length && m_Items[typeId].isSet; 109 110#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 111 T CreateAndGetData<T>(uint typeId, int lineNumber, string memberName, string filePath) 112#else 113 T CreateAndGetData<T>(uint typeId) 114#endif 115 where T : ContextItem, new() 116 { 117 if (m_Items.Length <= typeId) 118 { 119 var items = new Item[math.max(math.ceilpow2(s_TypeCount), m_Items.Length * 2)]; 120 for (var i = 0; i < m_Items.Length; i++) 121 { 122 items[i] = m_Items[i]; 123 } 124 m_Items = items; 125 } 126 127 m_ActiveItemIndices.Add(typeId); 128 ref var item = ref m_Items[typeId]; 129 item.storage ??= new T(); 130 item.isSet = true; 131#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 132 item.lineNumber = lineNumber; 133 item.memberName = memberName; 134 item.filePath = filePath; 135#endif 136 137 return (T)item.storage; 138 } 139 140 /// <summary> 141 /// Call Dispose to remove the created values. 142 /// </summary> 143 public void Dispose() 144 { 145 foreach (var index in m_ActiveItemIndices) 146 { 147 ref var item = ref m_Items[index]; 148 item.storage.Reset(); 149 item.isSet = false; 150 } 151 152 m_ActiveItemIndices.Clear(); 153 } 154 155 static uint s_TypeCount; 156 157 static class TypeId<T> 158 { 159 public static uint value = s_TypeCount++; 160 } 161 162 struct Item 163 { 164 public ContextItem storage; 165 public bool isSet; 166#if CONTEXT_CONTAINER_ALLOCATOR_DEBUG 167 public int lineNumber; 168 public string memberName; 169 public string filePath; 170#endif 171 } 172 173 } 174 175 /// <summary> 176 /// This is needed to add the data to <c>ContextContainer</c> and will control how the data are removed when calling Dispose on the <c>ContextContainer</c>. 177 /// </summary> 178 public abstract class ContextItem 179 { 180 /// <summary> 181 /// Resets the object so it can be used as a new instance next time it is created. 182 /// To avoid memory allocations and generating garbage, the system reuses objects. 183 /// This function should clear the object so it can be reused without leaking any 184 /// information (e.g. pointers to objects that will no longer be valid to access). 185 /// So it is important the implementation carefully clears all relevant members. 186 /// Note that this is different from a Dispose or Destructor as the object in not 187 /// freed but reset. This can be useful when havin large sub-allocated objects like 188 /// arrays or lists which can be cleared and reused without re-allocating. 189 /// </summary> 190 public abstract void Reset(); 191 } 192}