UpgradeUtility.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEngine;
  5. using UnityObject = UnityEngine.Object;
  6. namespace UnityEditor.Rendering
  7. {
  8. /// <summary>
  9. /// Flags describing usage of an asset by its dependents, when that asset might have serialized shader property names.
  10. /// </summary>
  11. [Flags]
  12. enum SerializedShaderPropertyUsage : byte
  13. {
  14. /// <summary>
  15. /// Asset's usage is unknown.
  16. /// </summary>
  17. Unknown = 0,
  18. /// <summary>
  19. /// Asset contains no serialized shader properties.
  20. /// </summary>
  21. NoShaderProperties = 1,
  22. /// <summary>
  23. /// Asset is used by objects that have materials which have been upgraded.
  24. /// </summary>
  25. UsedByUpgraded = 2,
  26. /// <summary>
  27. /// Asset is used by objects that have materials which were not upgraded.
  28. /// </summary>
  29. UsedByNonUpgraded = 4,
  30. /// <summary>
  31. /// Asset is used by objects that have materials which may have been upgraded, but there is no unambiguous upgrade path.
  32. /// </summary>
  33. UsedByAmbiguouslyUpgraded = 8,
  34. }
  35. /// <summary>
  36. /// Class containing utility methods for upgrading assets affected by render pipeline migration.
  37. /// </summary>
  38. static class UpgradeUtility
  39. {
  40. /// <summary>
  41. /// Stable, unique identifier for some asset.
  42. /// </summary>
  43. internal struct UID
  44. {
  45. public string Value;
  46. public static implicit operator string(UID uid) => uid.Value;
  47. public static implicit operator UID(string id) => new UID { Value = id };
  48. }
  49. internal interface IMaterial
  50. {
  51. UID ID { get; }
  52. string ShaderName { get; }
  53. }
  54. internal struct MaterialProxy : IMaterial
  55. {
  56. public MaterialProxy(Material material, UnityObject[] allAssetsAtPath)
  57. {
  58. m_ID = $"{allAssetsAtPath}{Array.IndexOf(allAssetsAtPath, material)}";
  59. m_Material = material;
  60. }
  61. UID m_ID;
  62. Material m_Material;
  63. public UID ID => m_ID;
  64. public string ShaderName => m_Material.shader.name;
  65. public static implicit operator Material(MaterialProxy proxy) => proxy.m_Material;
  66. public static implicit operator MaterialProxy(Material material) =>
  67. new MaterialProxy(material, AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(material)));
  68. public override string ToString() => m_Material.ToString();
  69. }
  70. /// <summary>
  71. /// Create A table of new shader names and all known upgrade paths to them in the target pipeline.
  72. /// </summary>
  73. /// <param name="upgraders">The set of <see cref="MaterialUpgrader"/> from which to build the table.</param>
  74. /// <returns>A table of new shader names and all known upgrade paths to them in the target pipeline.</returns>
  75. public static Dictionary<string, IReadOnlyList<MaterialUpgrader>> GetAllUpgradePathsToShaders(
  76. IEnumerable<MaterialUpgrader> upgraders
  77. )
  78. {
  79. var upgradePathBuilder = new Dictionary<string, List<MaterialUpgrader>>();
  80. foreach (var upgrader in upgraders)
  81. {
  82. // skip over upgraders that do not rename shaders or have not been initialized
  83. if (upgrader.NewShaderPath == null)
  84. continue;
  85. if (!upgradePathBuilder.TryGetValue(upgrader.NewShaderPath, out var allPaths))
  86. upgradePathBuilder[upgrader.NewShaderPath] = allPaths = new List<MaterialUpgrader>();
  87. allPaths.Add(upgrader);
  88. }
  89. return upgradePathBuilder.ToDictionary(kv => kv.Key, kv => kv.Value as IReadOnlyList<MaterialUpgrader>);
  90. }
  91. /// <summary>
  92. /// Gets the new name for a serialized shader property, which needs to be applied to a material that has been upgraded.
  93. /// </summary>
  94. /// <remarks>
  95. /// Some assets serialize shader property names, in order to apply modifications to a material at run-time.
  96. /// Use this method's return value to determine whether the serialized property name can be safely substituted,
  97. /// based on a material the host object intends to apply it to.
  98. /// </remarks>
  99. /// <param name="shaderPropertyName">A shader property name serialized on a host object.</param>
  100. /// <param name="material">
  101. /// The target material to which some shader property modification will be applied.
  102. /// It is presumed to have already been upgraded.
  103. /// </param>
  104. /// <param name="materialPropertyType">What type of property <paramref name="shaderPropertyName"/> is.</param>
  105. /// <param name="allUpgradePathsToNewShaders">
  106. /// A table of new shader names and all known upgrade paths to them in the target pipeline.
  107. /// (See also <seealso cref="UpgradeUtility.GetAllUpgradePathsToShaders"/>.)
  108. /// </param>
  109. /// <param name="upgradePathsUsedByMaterials">
  110. /// Optional table of materials known to have gone through a specific upgrade path.
  111. /// </param>
  112. /// <param name="newPropertyName">
  113. /// The new name for <paramref name="shaderPropertyName"/>.
  114. /// Its value is only guaranteed to be unambiguous if the method returns
  115. /// <see cref="SerializedShaderPropertyUsage.UsedByUpgraded"/>.
  116. /// </param>
  117. /// <returns>
  118. /// Usage flags indicating how <paramref name="shaderPropertyName"/> relates to <paramref name="material"/>.
  119. /// </returns>
  120. public static SerializedShaderPropertyUsage GetNewPropertyName(
  121. string shaderPropertyName,
  122. IMaterial material,
  123. MaterialUpgrader.MaterialPropertyType materialPropertyType,
  124. IReadOnlyDictionary<string, IReadOnlyList<MaterialUpgrader>> allUpgradePathsToNewShaders,
  125. IReadOnlyDictionary<UID, MaterialUpgrader> upgradePathsUsedByMaterials,
  126. out string newPropertyName
  127. )
  128. {
  129. var result = SerializedShaderPropertyUsage.Unknown;
  130. // we want to find out if we should rename the property
  131. newPropertyName = shaderPropertyName;
  132. // first check if we already know how this material was upgraded
  133. if (upgradePathsUsedByMaterials != null && upgradePathsUsedByMaterials.TryGetValue(material.ID, out var upgrader))
  134. {
  135. result |= SerializedShaderPropertyUsage.UsedByUpgraded;
  136. var propertyRenameTable = upgrader.GetPropertyRenameMap(materialPropertyType);
  137. propertyRenameTable.TryGetValue(shaderPropertyName, out newPropertyName);
  138. }
  139. // otherwise, try to guess whether it might have been upgraded
  140. if (newPropertyName == shaderPropertyName)
  141. {
  142. // get possible known upgrade paths material might have taken
  143. allUpgradePathsToNewShaders.TryGetValue(material.ShaderName, out var possibleUpgraders);
  144. // if there are none, then assume this material was not upgraded
  145. if ((possibleUpgraders?.Count ?? 0) == 0)
  146. {
  147. result |= SerializedShaderPropertyUsage.UsedByNonUpgraded;
  148. }
  149. // otherwise, see if there are any possible upgrade paths
  150. else
  151. {
  152. // narrow possible upgraders to those which specify a rename for the bound property
  153. var matchingUpgraders = possibleUpgraders.Where(
  154. u => u.GetPropertyRenameMap(materialPropertyType).ContainsKey(shaderPropertyName)
  155. ).ToList();
  156. // if there are any, assume the material has been upgraded
  157. if (matchingUpgraders.Any())
  158. {
  159. result |= SerializedShaderPropertyUsage.UsedByUpgraded;
  160. // if there are many possible upgrade paths to take, mark the upgrade as ambiguous
  161. newPropertyName = matchingUpgraders[0].GetPropertyRenameMap(materialPropertyType)[shaderPropertyName];
  162. var name = newPropertyName; // cannot use out param inside lambda
  163. if (matchingUpgraders.Any(u => u.GetPropertyRenameMap(materialPropertyType)[shaderPropertyName] != name))
  164. result |= SerializedShaderPropertyUsage.UsedByAmbiguouslyUpgraded;
  165. }
  166. else
  167. {
  168. var alreadyUpgraded = possibleUpgraders.Any(u => u.GetPropertyRenameMap(materialPropertyType).Values.Contains(shaderPropertyName));
  169. if (alreadyUpgraded)
  170. {
  171. result |= SerializedShaderPropertyUsage.UsedByUpgraded;
  172. }
  173. }
  174. }
  175. }
  176. return result;
  177. }
  178. }
  179. }