ShaderVariantCollector.cs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.IO;
  6. using UnityEngine;
  7. using UnityEditor;
  8. using UnityEditor.SceneManagement;
  9. using Debug = UnityEngine.Debug;
  10. namespace YooAsset.Editor
  11. {
  12. public static class ShaderVariantCollector
  13. {
  14. private const float WaitMilliseconds = 1000f;
  15. private static string _saveFilePath;
  16. private static bool _isStarted = false;
  17. private static readonly Stopwatch _elapsedTime = new Stopwatch();
  18. private static Action _completedCallback;
  19. private static void EditorUpdate()
  20. {
  21. // 注意:一定要延迟保存才会起效
  22. if (_isStarted && _elapsedTime.ElapsedMilliseconds > WaitMilliseconds)
  23. {
  24. _isStarted = false;
  25. _elapsedTime.Stop();
  26. EditorApplication.update -= EditorUpdate;
  27. // 保存结果
  28. ShaderVariantCollectionHelper.SaveCurrentShaderVariantCollection(_saveFilePath);
  29. // 创建说明文件
  30. CreateReadme();
  31. Debug.Log($"搜集SVC完毕!");
  32. _completedCallback?.Invoke();
  33. }
  34. }
  35. /// <summary>
  36. /// 开始收集
  37. /// </summary>
  38. public static void Run(string saveFilePath, Action completedCallback)
  39. {
  40. if (_isStarted)
  41. return;
  42. if (Path.HasExtension(saveFilePath) == false)
  43. saveFilePath = $"{saveFilePath}.shadervariants";
  44. if (Path.GetExtension(saveFilePath) != ".shadervariants")
  45. throw new System.Exception("Shader variant file extension is invalid.");
  46. // 注意:先删除再保存,否则ShaderVariantCollection内容将无法及时刷新
  47. AssetDatabase.DeleteAsset(ShaderVariantCollectorSettingData.Setting.SavePath);
  48. EditorTools.CreateFileDirectory(saveFilePath);
  49. _saveFilePath = saveFilePath;
  50. _completedCallback = completedCallback;
  51. // 聚焦到游戏窗口
  52. EditorTools.FocusUnityGameWindow();
  53. // 清空旧数据
  54. ShaderVariantCollectionHelper.ClearCurrentShaderVariantCollection();
  55. // 创建临时测试场景
  56. CreateTemperScene();
  57. // 收集着色器变种
  58. var materials = GetAllMaterials();
  59. CollectVariants(materials);
  60. EditorApplication.update += EditorUpdate;
  61. _isStarted = true;
  62. _elapsedTime.Reset();
  63. _elapsedTime.Start();
  64. }
  65. private static void CreateTemperScene()
  66. {
  67. // 创建临时场景
  68. EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
  69. }
  70. private static List<Material> GetAllMaterials()
  71. {
  72. int progressValue = 0;
  73. List<string> allAssets = new List<string>(1000);
  74. // 获取所有打包的资源
  75. List<CollectAssetInfo> allCollectInfos = AssetBundleCollectorSettingData.Setting.GetAllCollectAssets(EBuildMode.DryRunBuild);
  76. List<string> collectAssets = allCollectInfos.Select(t => t.AssetPath).ToList();
  77. foreach (var assetPath in collectAssets)
  78. {
  79. string[] depends = AssetDatabase.GetDependencies(assetPath, true);
  80. foreach (var depend in depends)
  81. {
  82. if (allAssets.Contains(depend) == false)
  83. allAssets.Add(depend);
  84. }
  85. EditorTools.DisplayProgressBar("获取所有打包资源", ++progressValue, collectAssets.Count);
  86. }
  87. EditorTools.ClearProgressBar();
  88. // 搜集所有材质球
  89. progressValue = 0;
  90. var shaderDic = new Dictionary<Shader, List<Material>>(100);
  91. foreach (var assetPath in allAssets)
  92. {
  93. System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
  94. if (assetType == typeof(UnityEngine.Material))
  95. {
  96. var material = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
  97. var shader = material.shader;
  98. if (shader == null)
  99. continue;
  100. if (shaderDic.ContainsKey(shader) == false)
  101. {
  102. shaderDic.Add(shader, new List<Material>());
  103. }
  104. if (shaderDic[shader].Contains(material) == false)
  105. {
  106. shaderDic[shader].Add(material);
  107. }
  108. }
  109. EditorTools.DisplayProgressBar("搜集所有材质球", ++progressValue, allAssets.Count);
  110. }
  111. EditorTools.ClearProgressBar();
  112. // 返回结果
  113. var materials = new List<Material>(1000);
  114. foreach (var valuePair in shaderDic)
  115. {
  116. materials.AddRange(valuePair.Value);
  117. }
  118. return materials;
  119. }
  120. private static void CollectVariants(List<Material> materials)
  121. {
  122. Camera camera = Camera.main;
  123. if (camera == null)
  124. throw new System.Exception("Not found main camera.");
  125. // 设置主相机
  126. float aspect = camera.aspect;
  127. int totalMaterials = materials.Count;
  128. float height = Mathf.Sqrt(totalMaterials / aspect) + 1;
  129. float width = Mathf.Sqrt(totalMaterials / aspect) * aspect + 1;
  130. float halfHeight = Mathf.CeilToInt(height / 2f);
  131. float halfWidth = Mathf.CeilToInt(width / 2f);
  132. camera.orthographic = true;
  133. camera.orthographicSize = halfHeight;
  134. camera.transform.position = new Vector3(0f, 0f, -10f);
  135. // 创建测试球体
  136. int xMax = (int)(width - 1);
  137. int x = 0, y = 0;
  138. int progressValue = 0;
  139. for (int i = 0; i < materials.Count; i++)
  140. {
  141. var material = materials[i];
  142. var position = new Vector3(x - halfWidth + 1f, y - halfHeight + 1f, 0f);
  143. CreateSphere(material, position, i);
  144. if (x == xMax)
  145. {
  146. x = 0;
  147. y++;
  148. }
  149. else
  150. {
  151. x++;
  152. }
  153. EditorTools.DisplayProgressBar("测试所有材质球", ++progressValue, materials.Count);
  154. }
  155. EditorTools.ClearProgressBar();
  156. }
  157. private static void CreateSphere(Material material, Vector3 position, int index)
  158. {
  159. var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
  160. go.GetComponent<Renderer>().material = material;
  161. go.transform.position = position;
  162. go.name = $"Sphere_{index}|{material.name}";
  163. }
  164. private static void CreateReadme()
  165. {
  166. AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
  167. ShaderVariantCollection svc = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(_saveFilePath);
  168. if (svc != null)
  169. {
  170. var wrapper = ShaderVariantCollectionReadme.Extract(svc);
  171. string jsonContents = JsonUtility.ToJson(wrapper, true);
  172. string savePath = _saveFilePath.Replace(".shadervariants", "Manifest.json");
  173. File.WriteAllText(savePath, jsonContents);
  174. }
  175. AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
  176. }
  177. }
  178. }