No Description
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.

ContextContainer.cs 7.3KB

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