using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering
{
/// <summary>
/// Class to handle the Material Upgraders and fetching of them
/// </summary>
internal class MaterialUpgraderRegistry
{
private static Lazy<MaterialUpgraderRegistry> m_Instance = new(() => new MaterialUpgraderRegistry());
/// <summary>
/// Singleton instance of the registry.
/// </summary>
public static MaterialUpgraderRegistry instance => m_Instance.Value;
Dictionary<Type, List<MaterialUpgrader>> m_UpgradersSupportedByPipeline = new();
/// <summary>
/// Returns a list of <see cref="MaterialUpgrader"/> with a given asset type
/// </summary>
/// <param name="renderPipelineAssetType">A valid RP asset type</param>
/// <returns>Returns a list of <see cref="MaterialUpgrader"/></returns>
public List<MaterialUpgrader> GetMaterialUpgradersForPipeline(Type renderPipelineAssetType)
{
List<MaterialUpgrader> materialUpgraders = GetOrCreateMaterialUpgradersForPipeline(renderPipelineAssetType);
materialUpgraders.AddRange(GetOrCreateMaterialUpgradersForPipeline(typeof(RenderPipelineAsset)));
materialUpgraders.Sort(CompareUpgradersByOldShaderAndPriority);
return materialUpgraders;
}
private int CompareUpgradersByOldShaderAndPriority(MaterialUpgrader a, MaterialUpgrader b)
{
string nameA = a?.OldShaderPath ?? string.Empty;
string nameB = b?.OldShaderPath ?? string.Empty;
int nameComparison = string.Compare(nameA, nameB, StringComparison.Ordinal);
if (nameComparison != 0)
return nameComparison;
// If names are the same, compare by priority
return b.priority.CompareTo(a.priority);
}
MaterialUpgraderRegistry()
{
var providerTypes = TypeCache.GetTypesDerivedFrom<IMaterialUpgradersProvider>();
foreach (var providerType in providerTypes)
{
if (providerType.IsAbstract)
continue;
var provider = Activator.CreateInstance(providerType) as IMaterialUpgradersProvider;
var upgraders = provider.GetUpgraders();
if (upgraders == null)
continue;
var pipelineTypes = GetSupportedPipelines(providerType);
if (pipelineTypes.Length == 0)
pipelineTypes = new Type[] { typeof(RenderPipelineAsset) }; // Default to all pipelines if none specified
foreach (var pipelineType in pipelineTypes)
{
var upgradersForPipeline = GetOrCreateMaterialUpgradersForPipeline(pipelineType);
foreach(var upgrader in upgraders)
{
upgradersForPipeline.Add(upgrader);
}
}
}
}
private static Type[] GetSupportedPipelines(Type upgraderType)
{
var attr = upgraderType.GetCustomAttribute<SupportedOnRenderPipelineAttribute>();
if (attr == null)
throw new InvalidOperationException($"Missing {nameof(SupportedOnRenderPipelineAttribute)} on {upgraderType}");
return attr.renderPipelineTypes;
}
private List<MaterialUpgrader> GetOrCreateMaterialUpgradersForPipeline(Type renderPipelineAssetType)
{
if (!typeof(RenderPipelineAsset).IsAssignableFrom(renderPipelineAssetType))
throw new ArgumentException($"Type '{renderPipelineAssetType.FullName}' must inherit from RenderPipelineAsset.", nameof(renderPipelineAssetType));
if (!m_UpgradersSupportedByPipeline.TryGetValue(renderPipelineAssetType, out var upgradersForPipeline))
{
upgradersForPipeline = new List<MaterialUpgrader>();
m_UpgradersSupportedByPipeline[renderPipelineAssetType] = upgradersForPipeline;
}
return upgradersForPipeline;
}
}
}