using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Rendering
{
///
/// Flags describing usage of an asset by its dependents, when that asset might have serialized shader property names.
///
[Flags]
enum SerializedShaderPropertyUsage : byte
{
///
/// Asset's usage is unknown.
///
Unknown = 0,
///
/// Asset contains no serialized shader properties.
///
NoShaderProperties = 1,
///
/// Asset is used by objects that have materials which have been upgraded.
///
UsedByUpgraded = 2,
///
/// Asset is used by objects that have materials which were not upgraded.
///
UsedByNonUpgraded = 4,
///
/// Asset is used by objects that have materials which may have been upgraded, but there is no unambiguous upgrade path.
///
UsedByAmbiguouslyUpgraded = 8,
}
///
/// Class containing utility methods for upgrading assets affected by render pipeline migration.
///
static class UpgradeUtility
{
///
/// Stable, unique identifier for some asset.
///
internal struct UID
{
public string Value;
public static implicit operator string(UID uid) => uid.Value;
public static implicit operator UID(string id) => new UID { Value = id };
}
internal interface IMaterial
{
UID ID { get; }
string ShaderName { get; }
}
internal struct MaterialProxy : IMaterial
{
public MaterialProxy(Material material, UnityObject[] allAssetsAtPath)
{
m_ID = $"{allAssetsAtPath}{Array.IndexOf(allAssetsAtPath, material)}";
m_Material = material;
}
UID m_ID;
Material m_Material;
public UID ID => m_ID;
public string ShaderName => m_Material.shader.name;
public static implicit operator Material(MaterialProxy proxy) => proxy.m_Material;
public static implicit operator MaterialProxy(Material material) =>
new MaterialProxy(material, AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(material)));
public override string ToString() => m_Material.ToString();
}
///
/// Create A table of new shader names and all known upgrade paths to them in the target pipeline.
///
/// The set of from which to build the table.
/// A table of new shader names and all known upgrade paths to them in the target pipeline.
public static Dictionary> GetAllUpgradePathsToShaders(
IEnumerable upgraders
)
{
var upgradePathBuilder = new Dictionary>();
foreach (var upgrader in upgraders)
{
// skip over upgraders that do not rename shaders or have not been initialized
if (upgrader.NewShaderPath == null)
continue;
if (!upgradePathBuilder.TryGetValue(upgrader.NewShaderPath, out var allPaths))
upgradePathBuilder[upgrader.NewShaderPath] = allPaths = new List();
allPaths.Add(upgrader);
}
return upgradePathBuilder.ToDictionary(kv => kv.Key, kv => kv.Value as IReadOnlyList);
}
///
/// Gets the new name for a serialized shader property, which needs to be applied to a material that has been upgraded.
///
///
/// Some assets serialize shader property names, in order to apply modifications to a material at run-time.
/// Use this method's return value to determine whether the serialized property name can be safely substituted,
/// based on a material the host object intends to apply it to.
///
/// A shader property name serialized on a host object.
///
/// The target material to which some shader property modification will be applied.
/// It is presumed to have already been upgraded.
///
/// What type of property is.
///
/// A table of new shader names and all known upgrade paths to them in the target pipeline.
/// (See also .)
///
///
/// Optional table of materials known to have gone through a specific upgrade path.
///
///
/// The new name for .
/// Its value is only guaranteed to be unambiguous if the method returns
/// .
///
///
/// Usage flags indicating how relates to .
///
public static SerializedShaderPropertyUsage GetNewPropertyName(
string shaderPropertyName,
IMaterial material,
MaterialUpgrader.MaterialPropertyType materialPropertyType,
IReadOnlyDictionary> allUpgradePathsToNewShaders,
IReadOnlyDictionary upgradePathsUsedByMaterials,
out string newPropertyName
)
{
var result = SerializedShaderPropertyUsage.Unknown;
// we want to find out if we should rename the property
newPropertyName = shaderPropertyName;
// first check if we already know how this material was upgraded
if (upgradePathsUsedByMaterials != null && upgradePathsUsedByMaterials.TryGetValue(material.ID, out var upgrader))
{
result |= SerializedShaderPropertyUsage.UsedByUpgraded;
var propertyRenameTable = upgrader.GetPropertyRenameMap(materialPropertyType);
propertyRenameTable.TryGetValue(shaderPropertyName, out newPropertyName);
}
// otherwise, try to guess whether it might have been upgraded
if (newPropertyName == shaderPropertyName)
{
// get possible known upgrade paths material might have taken
allUpgradePathsToNewShaders.TryGetValue(material.ShaderName, out var possibleUpgraders);
// if there are none, then assume this material was not upgraded
if ((possibleUpgraders?.Count ?? 0) == 0)
{
result |= SerializedShaderPropertyUsage.UsedByNonUpgraded;
}
// otherwise, see if there are any possible upgrade paths
else
{
// narrow possible upgraders to those which specify a rename for the bound property
var matchingUpgraders = possibleUpgraders.Where(
u => u.GetPropertyRenameMap(materialPropertyType).ContainsKey(shaderPropertyName)
).ToList();
// if there are any, assume the material has been upgraded
if (matchingUpgraders.Any())
{
result |= SerializedShaderPropertyUsage.UsedByUpgraded;
// if there are many possible upgrade paths to take, mark the upgrade as ambiguous
newPropertyName = matchingUpgraders[0].GetPropertyRenameMap(materialPropertyType)[shaderPropertyName];
var name = newPropertyName; // cannot use out param inside lambda
if (matchingUpgraders.Any(u => u.GetPropertyRenameMap(materialPropertyType)[shaderPropertyName] != name))
result |= SerializedShaderPropertyUsage.UsedByAmbiguouslyUpgraded;
}
else
{
var alreadyUpgraded = possibleUpgraders.Any(u => u.GetPropertyRenameMap(materialPropertyType).Values.Contains(shaderPropertyName));
if (alreadyUpgraded)
{
result |= SerializedShaderPropertyUsage.UsedByUpgraded;
}
}
}
}
return result;
}
}
}