123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityObject = UnityEngine.Object;
- namespace UnityEditor.Rendering
- {
- /// <summary>
- /// Flags describing usage of an asset by its dependents, when that asset might have serialized shader property names.
- /// </summary>
- [Flags]
- enum SerializedShaderPropertyUsage : byte
- {
- /// <summary>
- /// Asset's usage is unknown.
- /// </summary>
- Unknown = 0,
- /// <summary>
- /// Asset contains no serialized shader properties.
- /// </summary>
- NoShaderProperties = 1,
- /// <summary>
- /// Asset is used by objects that have materials which have been upgraded.
- /// </summary>
- UsedByUpgraded = 2,
- /// <summary>
- /// Asset is used by objects that have materials which were not upgraded.
- /// </summary>
- UsedByNonUpgraded = 4,
- /// <summary>
- /// Asset is used by objects that have materials which may have been upgraded, but there is no unambiguous upgrade path.
- /// </summary>
- UsedByAmbiguouslyUpgraded = 8,
- }
- /// <summary>
- /// Class containing utility methods for upgrading assets affected by render pipeline migration.
- /// </summary>
- static class UpgradeUtility
- {
- /// <summary>
- /// Stable, unique identifier for some asset.
- /// </summary>
- 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();
- }
- /// <summary>
- /// Create A table of new shader names and all known upgrade paths to them in the target pipeline.
- /// </summary>
- /// <param name="upgraders">The set of <see cref="MaterialUpgrader"/> from which to build the table.</param>
- /// <returns>A table of new shader names and all known upgrade paths to them in the target pipeline.</returns>
- public static Dictionary<string, IReadOnlyList<MaterialUpgrader>> GetAllUpgradePathsToShaders(
- IEnumerable<MaterialUpgrader> upgraders
- )
- {
- var upgradePathBuilder = new Dictionary<string, List<MaterialUpgrader>>();
- 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<MaterialUpgrader>();
- allPaths.Add(upgrader);
- }
- return upgradePathBuilder.ToDictionary(kv => kv.Key, kv => kv.Value as IReadOnlyList<MaterialUpgrader>);
- }
- /// <summary>
- /// Gets the new name for a serialized shader property, which needs to be applied to a material that has been upgraded.
- /// </summary>
- /// <remarks>
- /// 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.
- /// </remarks>
- /// <param name="shaderPropertyName">A shader property name serialized on a host object.</param>
- /// <param name="material">
- /// The target material to which some shader property modification will be applied.
- /// It is presumed to have already been upgraded.
- /// </param>
- /// <param name="materialPropertyType">What type of property <paramref name="shaderPropertyName"/> is.</param>
- /// <param name="allUpgradePathsToNewShaders">
- /// A table of new shader names and all known upgrade paths to them in the target pipeline.
- /// (See also <seealso cref="UpgradeUtility.GetAllUpgradePathsToShaders"/>.)
- /// </param>
- /// <param name="upgradePathsUsedByMaterials">
- /// Optional table of materials known to have gone through a specific upgrade path.
- /// </param>
- /// <param name="newPropertyName">
- /// The new name for <paramref name="shaderPropertyName"/>.
- /// Its value is only guaranteed to be unambiguous if the method returns
- /// <see cref="SerializedShaderPropertyUsage.UsedByUpgraded"/>.
- /// </param>
- /// <returns>
- /// Usage flags indicating how <paramref name="shaderPropertyName"/> relates to <paramref name="material"/>.
- /// </returns>
- public static SerializedShaderPropertyUsage GetNewPropertyName(
- string shaderPropertyName,
- IMaterial material,
- MaterialUpgrader.MaterialPropertyType materialPropertyType,
- IReadOnlyDictionary<string, IReadOnlyList<MaterialUpgrader>> allUpgradePathsToNewShaders,
- IReadOnlyDictionary<UID, MaterialUpgrader> 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;
- }
- }
- }
|