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
}
}