using System; using System.Runtime.CompilerServices; namespace XCEngine.Rendering.Universal { internal sealed class ScriptableRendererDataCollection { private ScriptableRendererData[] m_rendererDataList; public ScriptableRendererDataCollection() { m_rendererDataList = Array.Empty(); } public ScriptableRendererData GetDefaultRendererData( ref ScriptableRendererData[] configuredRendererDataList, ref int defaultRendererIndex, Func createDefaultRendererData) { return GetRendererData( ref configuredRendererDataList, ref defaultRendererIndex, ResolveDefaultRendererIndex( ref configuredRendererDataList, ref defaultRendererIndex, createDefaultRendererData), createDefaultRendererData); } public ScriptableRendererData GetRendererData( ref ScriptableRendererData[] configuredRendererDataList, ref int defaultRendererIndex, int rendererIndex, Func createDefaultRendererData) { int resolvedRendererIndex = ResolveRendererIndexWithFallback( ref configuredRendererDataList, ref defaultRendererIndex, rendererIndex, createDefaultRendererData); if (resolvedRendererIndex < 0) { return null; } return m_rendererDataList[resolvedRendererIndex]; } public int ResolveDefaultRendererIndex( ref ScriptableRendererData[] configuredRendererDataList, ref int defaultRendererIndex, Func createDefaultRendererData) { ScriptableRendererData[] rendererDataList = ResolveConfiguredRendererDataList( ref configuredRendererDataList, createDefaultRendererData); if (rendererDataList.Length == 0) { return -1; } if (defaultRendererIndex < 0 || defaultRendererIndex >= rendererDataList.Length) { defaultRendererIndex = 0; } if (rendererDataList[defaultRendererIndex] != null) { return defaultRendererIndex; } int firstValidRendererIndex = FindFirstValidRendererIndex(rendererDataList); if (firstValidRendererIndex >= 0) { defaultRendererIndex = firstValidRendererIndex; return defaultRendererIndex; } rendererDataList[defaultRendererIndex] = createDefaultRendererData != null ? createDefaultRendererData() : null; return rendererDataList[defaultRendererIndex] != null ? defaultRendererIndex : -1; } public int ResolveRendererIndexWithFallback( ref ScriptableRendererData[] configuredRendererDataList, ref int defaultRendererIndex, int rendererIndex, Func createDefaultRendererData) { ScriptableRendererData[] rendererDataList = ResolveConfiguredRendererDataList( ref configuredRendererDataList, createDefaultRendererData); if (rendererDataList.Length == 0) { return -1; } if (rendererIndex < 0 || rendererIndex >= rendererDataList.Length) { return ResolveDefaultRendererIndex( ref configuredRendererDataList, ref defaultRendererIndex, createDefaultRendererData); } if (rendererDataList[rendererIndex] == null) { return ResolveDefaultRendererIndex( ref configuredRendererDataList, ref defaultRendererIndex, createDefaultRendererData); } return rendererIndex; } public void Synchronize( ref ScriptableRendererData[] configuredRendererDataList, ref int defaultRendererIndex, ref int collectionHash, ref bool collectionHashResolved, Func createDefaultRendererData, Action onChanged) { ScriptableRendererData[] rendererDataList = ResolveConfiguredRendererDataList( ref configuredRendererDataList, createDefaultRendererData); int resolvedDefaultRendererIndex = ResolveDefaultRendererIndex( ref configuredRendererDataList, ref defaultRendererIndex, createDefaultRendererData); int resolvedHash = ComputeCollectionHash( rendererDataList, resolvedDefaultRendererIndex); if (!collectionHashResolved) { collectionHash = resolvedHash; collectionHashResolved = true; return; } if (resolvedHash == collectionHash) { return; } collectionHash = resolvedHash; onChanged?.Invoke(); } public void ReleaseRuntimeResources( ref ScriptableRendererData[] configuredRendererDataList, ref int defaultRendererIndex, Func createDefaultRendererData) { ScriptableRendererData[] rendererDataList = ResolveConfiguredRendererDataList( ref configuredRendererDataList, createDefaultRendererData); for (int i = 0; i < rendererDataList.Length; ++i) { ScriptableRendererData rendererData = rendererDataList[i]; if (rendererData == null || WasRendererDataReleasedEarlier( rendererDataList, i)) { continue; } rendererData.ReleaseRuntimeResourcesInstance(); } } private ScriptableRendererData[] ResolveConfiguredRendererDataList( ref ScriptableRendererData[] configuredRendererDataList, Func createDefaultRendererData) { if (configuredRendererDataList == null || configuredRendererDataList.Length == 0) { ScriptableRendererData defaultRendererData = createDefaultRendererData != null ? createDefaultRendererData() : null; configuredRendererDataList = defaultRendererData != null ? new ScriptableRendererData[] { defaultRendererData } : Array.Empty(); } m_rendererDataList = configuredRendererDataList; return m_rendererDataList; } private static int ComputeCollectionHash( ScriptableRendererData[] rendererDataList, int defaultRendererIndex) { unchecked { int hash = 17; hash = (hash * 31) + defaultRendererIndex; if (rendererDataList == null) { return hash; } hash = (hash * 31) + rendererDataList.Length; for (int i = 0; i < rendererDataList.Length; ++i) { ScriptableRendererData rendererData = rendererDataList[i]; if (rendererData == null) { hash = (hash * 31) + 1; continue; } hash = (hash * 31) + RuntimeHelpers.GetHashCode(rendererData); hash = (hash * 31) + rendererData.GetRuntimeStateVersionInstance(); } return hash; } } private static int FindFirstValidRendererIndex( ScriptableRendererData[] rendererDataList) { for (int i = 0; i < rendererDataList.Length; ++i) { if (rendererDataList[i] != null) { return i; } } return -1; } private static bool WasRendererDataReleasedEarlier( ScriptableRendererData[] rendererDataList, int rendererDataIndex) { ScriptableRendererData rendererData = rendererDataList[rendererDataIndex]; for (int i = 0; i < rendererDataIndex; ++i) { if (object.ReferenceEquals( rendererDataList[i], rendererData)) { return true; } } return false; } } }