using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; namespace UnityEngine.Rendering.HighDefinition { internal class HDShadowRequestDatabase { #region ASCII Diagram // Diagram of HDShadowRequestDatabase and its relation to HDLightRenderDatabase: // // =================================================== // key: +---+ || // array index (* is value): | * | || // +---+ || // || // --+ || // index reference: | || // +--> || // || // || // =================================================== // // +----------------------------------------------------+ // | | // | free index: 0-----+ +-------+ | +---+ // | | | | | | | // +---+---+---+---+---+---+---+---+ v | v v | v // sparse handles: |-1 |-1 |-1 |-1 |-1 | 5 |-1 |-1 | +---+---+---+---+---+---+---+---+ // +---+---+---+---+---+---+---+---+ shadow requests:| 1-->2-->4 | 6<--3 | | 7 | 8 ---> // lightEntities: |-1 |-1 | 1 |-1 |-1 | 0 |-1 |-1 | | | | | +-----------^ | | // +---+---+---+---+---+---+---+---+ | | | | | | | | | // | | request 0 | * | * | * | * | * | * | * | * | // | | request 1 | * | * | * | * | * | * | * | * | // +---+ | request 2 | * | * | * | * | * | * | * | * | // +---|---------------+ request 3 | * | * | * | * | * | * | * | * | // | | request 4 | * | * | * | * | * | * | * | * | // | | request 5 | * | * | * | * | * | * | * | * | // v v +---+---+---+---+---+---+---+---+ // +---+---+---+---+---+---+---+---+ ^ // packed handles: | 5 |-1 |-1 |-1 |-1 |-1 |-1 |-1 | | // +---+---+---+---+---+---+---+---+ | // | | // | | // +------------------------------------------------------------------------+ // // // Explanation: // A lightEntity in the HDLightRenderDatabase may or may not allocate a shadow from the HDShadowRequestDatabase. // If it does, it maintains references in both its sparse array for quick lookup by entity, // and in its packed (aka dense) array for quick lookup while looping through packed lightEntity data. // // As for the shadow requests themselves, they are allocated via an in-place free list, // where a linked list of free indices is maintained within the free indices themselves. // Additionally, each shadow index is actually six shadow indices, // as lights can have up to six shadow requests depending on light type. // // When light render data is normalized in the future (as arrays of arrays), // the shadow database will be removed. In its place will be a subset of the normalized light arrays, // each of which will contain shadow data that is index-aligned with the other light data. #endregion private static HDShadowRequestDatabase s_Instance; // TODO: Pad each set of HDShadowRequests to eliminate cache line sharing between sets, as a prerequisite for parallel shadow request update work. private int m_HDShadowRequestFreeListIndex; private int m_HDShadowRequestCount; private int m_HDShadowRequestCapacity; private NativeList m_HDShadowRequestStorage = new NativeList(Allocator.Persistent); private NativeList m_HDShadowRequestIndicesStorage = new NativeList(Allocator.Persistent); private NativeList m_FrustumPlanesStorage = new NativeList(Allocator.Persistent); private NativeList m_CachedViewPositionsStorage = new NativeList(Allocator.Persistent); private bool m_HDShadowRequestsCreated = true; public NativeList hdShadowRequestStorage => m_HDShadowRequestStorage; public NativeList hdShadowRequestIndicesStorage => m_HDShadowRequestIndicesStorage; public NativeList frustumPlanesStorage => m_FrustumPlanesStorage; public NativeList cachedViewPositionsStorage => m_CachedViewPositionsStorage; static public HDShadowRequestDatabase instance { get { if (s_Instance == null) s_Instance = new HDShadowRequestDatabase(); return s_Instance; } } public bool IsCreated => m_HDShadowRequestsCreated; public void EnsureNativeListsAreCreated() { if (!m_HDShadowRequestsCreated) { m_HDShadowRequestsCreated = true; m_HDShadowRequestStorage = new NativeList(Allocator.Persistent); m_HDShadowRequestIndicesStorage = new NativeList(Allocator.Persistent); m_FrustumPlanesStorage = new NativeList(Allocator.Persistent); m_CachedViewPositionsStorage = new NativeList(Allocator.Persistent); } } public unsafe HDShadowRequestSetHandle AllocateHDShadowRequests() { EnsureNativeListsAreCreated(); int oldFreeIndex = m_HDShadowRequestFreeListIndex; int oldCapacity = m_HDShadowRequestCapacity; if (oldFreeIndex >= oldCapacity) { int newCapacity = oldCapacity * 2; newCapacity = newCapacity < 1 ? 1 : newCapacity; m_HDShadowRequestStorage.Length = newCapacity * HDShadowRequest.maxLightShadowRequestsCount; m_HDShadowRequestIndicesStorage.Length = newCapacity * HDShadowRequest.maxLightShadowRequestsCount; m_FrustumPlanesStorage.Length = newCapacity * HDShadowRequest.maxLightShadowRequestsCount * HDShadowRequest.frustumPlanesCount; m_CachedViewPositionsStorage.Length = newCapacity * HDShadowRequest.maxLightShadowRequestsCount; ref UnsafeList requestIndicesExpanded = ref UnsafeUtility.AsRef>(m_HDShadowRequestIndicesStorage.GetUnsafeList()); for (int i = oldCapacity; i < newCapacity; i++) { requestIndicesExpanded[i * HDShadowRequest.maxLightShadowRequestsCount] = i + 1; } m_HDShadowRequestCapacity = newCapacity; } ref UnsafeList requests = ref UnsafeUtility.AsRef>(m_HDShadowRequestStorage.GetUnsafeList()); int sizeOfRequests = UnsafeUtility.SizeOf(); int requestsByteCount = sizeOfRequests * HDShadowRequest.maxLightShadowRequestsCount; UnsafeUtility.MemClear(requests.Ptr + oldFreeIndex * HDShadowRequest.maxLightShadowRequestsCount, requestsByteCount); for (int i = oldFreeIndex * HDShadowRequest.maxLightShadowRequestsCount; i < oldFreeIndex * HDShadowRequest.maxLightShadowRequestsCount + HDShadowRequest.maxLightShadowRequestsCount; i++) { requests.ElementAt(i).InitDefault(); } ref UnsafeList requestIndices = ref UnsafeUtility.AsRef>(m_HDShadowRequestIndicesStorage.GetUnsafeList()); m_HDShadowRequestFreeListIndex = requestIndices[oldFreeIndex * HDShadowRequest.maxLightShadowRequestsCount]; int sizeOfIndices = sizeof(int); int indicesByteCount = sizeOfIndices * HDShadowRequest.maxLightShadowRequestsCount; UnsafeUtility.MemClear(requestIndices.Ptr + oldFreeIndex * HDShadowRequest.maxLightShadowRequestsCount, indicesByteCount); ref UnsafeList planes = ref UnsafeUtility.AsRef>(m_FrustumPlanesStorage.GetUnsafeList()); int sizeOfPlane = UnsafeUtility.SizeOf(); int planesByteCount = sizeOfPlane * HDShadowRequest.frustumPlanesCount * HDShadowRequest.maxLightShadowRequestsCount; UnsafeUtility.MemClear(planes.Ptr + oldFreeIndex * HDShadowRequest.frustumPlanesCount * HDShadowRequest.maxLightShadowRequestsCount, planesByteCount); ref UnsafeList cachedViewPositions = ref UnsafeUtility.AsRef>(m_CachedViewPositionsStorage.GetUnsafeList()); int sizeOfViewPositions = UnsafeUtility.SizeOf(); int viewPositionsByteCount = sizeOfViewPositions * HDShadowRequest.maxLightShadowRequestsCount; UnsafeUtility.MemClear(cachedViewPositions.Ptr + oldFreeIndex * HDShadowRequest.maxLightShadowRequestsCount, viewPositionsByteCount); ++m_HDShadowRequestCount; return new HDShadowRequestSetHandle { relativeDataOffset = oldFreeIndex }; } public unsafe void FreeHDShadowRequests(ref HDShadowRequestSetHandle shadowHandle) { if (!shadowHandle.valid) return; ref UnsafeList requestIndices = ref UnsafeUtility.AsRef>(m_HDShadowRequestIndicesStorage.GetUnsafeList()); requestIndices[shadowHandle.storageIndexForCachedViewPositions] = m_HDShadowRequestFreeListIndex; m_HDShadowRequestFreeListIndex = shadowHandle.relativeDataOffset; --m_HDShadowRequestCount; if (m_HDShadowRequestCount == 0) { DeleteArrays(); } shadowHandle = new HDShadowRequestSetHandle(){relativeDataOffset = HDShadowRequestSetHandle.InvalidIndex}; } public void DeleteArrays() { m_HDShadowRequestFreeListIndex = 0; m_HDShadowRequestCapacity = 0; m_HDShadowRequestCapacity = 0; if (m_HDShadowRequestsCreated) { m_HDShadowRequestStorage.Dispose(); m_HDShadowRequestStorage = default; m_HDShadowRequestIndicesStorage.Dispose(); m_HDShadowRequestIndicesStorage = default; m_CachedViewPositionsStorage.Dispose(); m_CachedViewPositionsStorage = default; m_FrustumPlanesStorage.Dispose(); m_FrustumPlanesStorage = default; } m_HDShadowRequestsCreated = false; } } }