123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- using System;
- using System.Collections.Generic;
- using System.Runtime.CompilerServices;
- using Unity.Mathematics;
-
- namespace UnityEngine.Rendering
- {
- /// <summary>
- /// ContextContainer is a Dictionary like storage where the key is a generic parameter and the value is of the same type.
- /// </summary>
- public class ContextContainer : IDisposable
- {
- Item[] m_Items = new Item[64];
- List<uint> m_ActiveItemIndices = new();
-
- /// <summary>
- /// Retrives a T of class <c>ContextContainerItem</c> if it was previously created without it being disposed.
- /// </summary>
- /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam>
- /// <returns>The value created previously using <![CDATA[Create<T>]]> .</returns>
- /// <exception cref="InvalidOperationException">This is thown if the value isn't previously created.</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public T Get<T>()
- where T : ContextItem, new()
- {
- var typeId = TypeId<T>.value;
- if (!Contains(typeId))
- {
- throw new InvalidOperationException($"Type {typeof(T).FullName} has not been created yet.");
- }
-
- return (T) m_Items[typeId].storage;
-
- }
-
- /// <summary>
- /// Creates the value of type T.
- /// </summary>
- /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam>
- /// <returns>The value of type T created inside the <c>ContextContainer</c>.</returns>
- /// <exception cref="InvalidOperationException">Thown if you try to create the value of type T agian after it is already created.</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- public T Create<T>([CallerLineNumber] int lineNumber = 0, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "")
- #else
- public T Create<T>()
- #endif
- where T : ContextItem, new()
- {
- var typeId = TypeId<T>.value;
- if (Contains(typeId))
- {
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- 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}.");
- #else
- throw new InvalidOperationException($"Type {typeof(T).FullName} has already been created.");
- #endif
- }
-
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- return CreateAndGetData<T>(typeId, lineNumber, memberName, filePath);
- #else
- return CreateAndGetData<T>(typeId);
- #endif
- }
-
- /// <summary>
- /// Creates the value of type T if the value is not previously created otherwise try to get the value of type T.
- /// </summary>
- /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam>
- /// <returns>Returns the value of type T which is created or retrived.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- public T GetOrCreate<T>([CallerLineNumber] int lineNumber = 0, [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "")
- #else
- public T GetOrCreate<T>()
- #endif
- where T : ContextItem, new()
- {
- var typeId = TypeId<T>.value;
- if (Contains(typeId))
- {
- return (T) m_Items[typeId].storage;
- }
-
-
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- return CreateAndGetData<T>(typeId, lineNumber, memberName, filePath);
- #else
- return CreateAndGetData<T>(typeId);
- #endif
- }
-
- /// <summary>
- /// Check if the value of type T has previously been created.
- /// </summary>
- /// <typeparam name="T">Is the class which you are trying to fetch. T has to inherit from <c>ContextContainerItem</c></typeparam>
- /// <returns>Returns true if the value exists and false otherwise.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool Contains<T>()
- where T : ContextItem, new()
- {
- var typeId = TypeId<T>.value;
- return Contains(typeId);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- bool Contains(uint typeId) => typeId < m_Items.Length && m_Items[typeId].isSet;
-
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- T CreateAndGetData<T>(uint typeId, int lineNumber, string memberName, string filePath)
- #else
- T CreateAndGetData<T>(uint typeId)
- #endif
- where T : ContextItem, new()
- {
- if (m_Items.Length <= typeId)
- {
- var items = new Item[math.max(math.ceilpow2(s_TypeCount), m_Items.Length * 2)];
- for (var i = 0; i < m_Items.Length; i++)
- {
- items[i] = m_Items[i];
- }
- m_Items = items;
- }
-
- m_ActiveItemIndices.Add(typeId);
- ref var item = ref m_Items[typeId];
- item.storage ??= new T();
- item.isSet = true;
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- item.lineNumber = lineNumber;
- item.memberName = memberName;
- item.filePath = filePath;
- #endif
-
- return (T)item.storage;
- }
-
- /// <summary>
- /// Call Dispose to remove the created values.
- /// </summary>
- public void Dispose()
- {
- foreach (var index in m_ActiveItemIndices)
- {
- ref var item = ref m_Items[index];
- item.storage.Reset();
- item.isSet = false;
- }
-
- m_ActiveItemIndices.Clear();
- }
-
- static uint s_TypeCount;
-
- static class TypeId<T>
- {
- public static uint value = s_TypeCount++;
- }
-
- struct Item
- {
- public ContextItem storage;
- public bool isSet;
- #if CONTEXT_CONTAINER_ALLOCATOR_DEBUG
- public int lineNumber;
- public string memberName;
- public string filePath;
- #endif
- }
-
- }
-
- /// <summary>
- /// 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>.
- /// </summary>
- public abstract class ContextItem
- {
- /// <summary>
- /// Resets the object so it can be used as a new instance next time it is created.
- /// To avoid memory allocations and generating garbage, the system reuses objects.
- /// This function should clear the object so it can be reused without leaking any
- /// information (e.g. pointers to objects that will no longer be valid to access).
- /// So it is important the implementation carefully clears all relevant members.
- /// Note that this is different from a Dispose or Destructor as the object in not
- /// freed but reset. This can be useful when havin large sub-allocated objects like
- /// arrays or lists which can be cleared and reused without re-allocating.
- /// </summary>
- public abstract void Reset();
- }
- }
|