using System; using System.Collections.Generic; using System.Reflection; #if UNITY_EDITOR using System.Linq; using UnityEditor; using ShaderKeywordFilter = UnityEditor.ShaderKeywordFilter; #endif namespace UnityEngine.Rendering.Universal { /// /// Class ScriptableRendererData contains resources for a ScriptableRenderer. /// /// public abstract class ScriptableRendererData : ScriptableObject { internal bool isInvalidated { get; set; } /// /// Class contains references to shader resources used by Rendering Debugger. /// [Serializable, ReloadGroup] public sealed class DebugShaderResources { /// /// Debug shader used to output interpolated vertex attributes. /// [Reload("Shaders/Debug/DebugReplacement.shader")] public Shader debugReplacementPS; } /// /// Container for shader resources used by Rendering Debugger. /// public DebugShaderResources debugShaders; /// /// Creates the instance of the ScriptableRenderer. /// /// The instance of ScriptableRenderer protected abstract ScriptableRenderer Create(); [SerializeField] internal List m_RendererFeatures = new List(10); [SerializeField] internal List m_RendererFeatureMap = new List(10); #if UNITY_EDITOR [ShaderKeywordFilter.SelectOrRemove(true, keywordNames: ShaderKeywordStrings.RenderPassEnabled)] #endif [SerializeField] bool m_UseNativeRenderPass = false; /// /// List of additional render pass features for this renderer. /// public List rendererFeatures { get => m_RendererFeatures; } /// /// Use SetDirty when changing seeings in the ScriptableRendererData. /// It will rebuild the render passes with the new data. /// public new void SetDirty() { isInvalidated = true; } internal ScriptableRenderer InternalCreateRenderer() { isInvalidated = false; return Create(); } protected virtual void OnValidate() { SetDirty(); #if UNITY_EDITOR if (m_RendererFeatures.Contains(null)) ValidateRendererFeatures(); #endif } protected virtual void OnEnable() { SetDirty(); } public bool useNativeRenderPass { get => m_UseNativeRenderPass; set { SetDirty(); m_UseNativeRenderPass = value; } } /// /// Returns true if contains renderer feature with specified type. /// /// Renderer Feature type. /// internal bool TryGetRendererFeature(out T rendererFeature) where T : ScriptableRendererFeature { foreach (var target in rendererFeatures) { if (target.GetType() == typeof(T)) { rendererFeature = target as T; return true; } } rendererFeature = null; return false; } #if UNITY_EDITOR internal virtual Material GetDefaultMaterial(DefaultMaterialType materialType) { return null; } internal virtual Shader GetDefaultShader() { return null; } internal bool ValidateRendererFeatures() { // Get all Subassets var subassets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(this)); var linkedIds = new List(); var loadedAssets = new Dictionary(); var mapValid = m_RendererFeatureMap != null && m_RendererFeatureMap?.Count == m_RendererFeatures?.Count; var debugOutput = $"{name}\nValid Sub-assets:\n"; // Collect valid, compiled sub-assets foreach (var asset in subassets) { if (asset == null || asset.GetType().BaseType != typeof(ScriptableRendererFeature)) continue; AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var guid, out long localId); loadedAssets.Add(localId, asset); debugOutput += $"-{asset.name}\n--localId={localId}\n"; } // Collect assets that are connected to the list for (var i = 0; i < m_RendererFeatures?.Count; i++) { if (!m_RendererFeatures[i]) continue; if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(m_RendererFeatures[i], out var guid, out long localId)) { linkedIds.Add(localId); } } var mapDebug = mapValid ? "Linking" : "Map missing, will attempt to re-map"; debugOutput += $"Feature List Status({mapDebug}):\n"; // Try fix missing references for (var i = 0; i < m_RendererFeatures?.Count; i++) { if (m_RendererFeatures[i] == null) { if (mapValid && m_RendererFeatureMap[i] != 0) { var localId = m_RendererFeatureMap[i]; loadedAssets.TryGetValue(localId, out var asset); m_RendererFeatures[i] = (ScriptableRendererFeature)asset; } else { m_RendererFeatures[i] = (ScriptableRendererFeature)GetUnusedAsset(ref linkedIds, ref loadedAssets); } } debugOutput += m_RendererFeatures[i] != null ? $"-{i}:Linked\n" : $"-{i}:Missing\n"; } UpdateMap(); if (!m_RendererFeatures.Contains(null)) return true; Debug.LogError($"{name} is missing RendererFeatures\nThis could be due to missing scripts or compile error.", this); return false; } internal bool DuplicateFeatureCheck(Type type) { var isSingleFeature = type.GetCustomAttribute(typeof(DisallowMultipleRendererFeature)); return isSingleFeature != null && m_RendererFeatures.Select(renderFeature => renderFeature.GetType()).Any(t => t == type); } private static object GetUnusedAsset(ref List usedIds, ref Dictionary assets) { foreach (var asset in assets) { var alreadyLinked = usedIds.Any(used => asset.Key == used); if (alreadyLinked) continue; usedIds.Add(asset.Key); return asset.Value; } return null; } private void UpdateMap() { if (m_RendererFeatureMap.Count != m_RendererFeatures.Count) { m_RendererFeatureMap.Clear(); m_RendererFeatureMap.AddRange(new long[m_RendererFeatures.Count]); } for (int i = 0; i < rendererFeatures.Count; i++) { if (m_RendererFeatures[i] == null) continue; if (!AssetDatabase.TryGetGUIDAndLocalFileIdentifier(m_RendererFeatures[i], out var guid, out long localId)) continue; m_RendererFeatureMap[i] = localId; } } #endif } }