ShaderVariantCollector.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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 enum ESteps
  15. {
  16. None,
  17. Prepare,
  18. CollectAllMaterial,
  19. CollectVariants,
  20. CollectSleeping,
  21. WaitingDone,
  22. }
  23. private const float WaitMilliseconds = 1000f;
  24. private const float SleepMilliseconds = 100f;
  25. private static string _savePath;
  26. private static string _packageName;
  27. private static int _processMaxNum;
  28. private static Action _completedCallback;
  29. private static ESteps _steps = ESteps.None;
  30. private static Stopwatch _elapsedTime;
  31. private static List<string> _allMaterials;
  32. private static List<GameObject> _allSpheres = new List<GameObject>(1000);
  33. /// <summary>
  34. /// 开始收集
  35. /// </summary>
  36. public static void Run(string savePath, string packageName, int processMaxNum, Action completedCallback)
  37. {
  38. if (_steps != ESteps.None)
  39. return;
  40. if (Path.HasExtension(savePath) == false)
  41. savePath = $"{savePath}.shadervariants";
  42. if (Path.GetExtension(savePath) != ".shadervariants")
  43. throw new System.Exception("Shader variant file extension is invalid.");
  44. if (string.IsNullOrEmpty(packageName))
  45. throw new System.Exception("Package name is null or empty !");
  46. // 注意:先删除再保存,否则ShaderVariantCollection内容将无法及时刷新
  47. AssetDatabase.DeleteAsset(savePath);
  48. EditorTools.CreateFileDirectory(savePath);
  49. _savePath = savePath;
  50. _packageName = packageName;
  51. _processMaxNum = processMaxNum;
  52. _completedCallback = completedCallback;
  53. // 聚焦到游戏窗口
  54. EditorTools.FocusUnityGameWindow();
  55. // 创建临时测试场景
  56. CreateTempScene();
  57. _steps = ESteps.Prepare;
  58. EditorApplication.update += EditorUpdate;
  59. }
  60. private static void EditorUpdate()
  61. {
  62. if (_steps == ESteps.None)
  63. return;
  64. if (_steps == ESteps.Prepare)
  65. {
  66. ShaderVariantCollectionHelper.ClearCurrentShaderVariantCollection();
  67. _steps = ESteps.CollectAllMaterial;
  68. return; //等待一帧
  69. }
  70. if (_steps == ESteps.CollectAllMaterial)
  71. {
  72. _allMaterials = GetAllMaterials();
  73. _steps = ESteps.CollectVariants;
  74. return; //等待一帧
  75. }
  76. if (_steps == ESteps.CollectVariants)
  77. {
  78. int count = Mathf.Min(_processMaxNum, _allMaterials.Count);
  79. List<string> range = _allMaterials.GetRange(0, count);
  80. _allMaterials.RemoveRange(0, count);
  81. CollectVariants(range);
  82. if (_allMaterials.Count > 0)
  83. {
  84. _elapsedTime = Stopwatch.StartNew();
  85. _steps = ESteps.CollectSleeping;
  86. }
  87. else
  88. {
  89. _elapsedTime = Stopwatch.StartNew();
  90. _steps = ESteps.WaitingDone;
  91. }
  92. }
  93. if (_steps == ESteps.CollectSleeping)
  94. {
  95. if (_elapsedTime.ElapsedMilliseconds > SleepMilliseconds)
  96. {
  97. DestroyAllSpheres();
  98. _elapsedTime.Stop();
  99. _steps = ESteps.CollectVariants;
  100. }
  101. }
  102. if (_steps == ESteps.WaitingDone)
  103. {
  104. // 注意:一定要延迟保存才会起效
  105. if (_elapsedTime.ElapsedMilliseconds > WaitMilliseconds)
  106. {
  107. _elapsedTime.Stop();
  108. _steps = ESteps.None;
  109. // 保存结果并创建清单
  110. ShaderVariantCollectionHelper.SaveCurrentShaderVariantCollection(_savePath);
  111. CreateManifest();
  112. Debug.Log($"搜集SVC完毕!");
  113. EditorApplication.update -= EditorUpdate;
  114. _completedCallback?.Invoke();
  115. }
  116. }
  117. }
  118. private static void CreateTempScene()
  119. {
  120. EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
  121. }
  122. private static List<string> GetAllMaterials()
  123. {
  124. int progressValue = 0;
  125. List<string> allAssets = new List<string>(1000);
  126. // 获取所有打包的资源
  127. CollectResult collectResult = AssetBundleCollectorSettingData.Setting.GetPackageAssets(EBuildMode.DryRunBuild, _packageName);
  128. foreach (var assetInfo in collectResult.CollectAssets)
  129. {
  130. string[] depends = AssetDatabase.GetDependencies(assetInfo.AssetPath, true);
  131. foreach (var dependAsset in depends)
  132. {
  133. if (allAssets.Contains(dependAsset) == false)
  134. allAssets.Add(dependAsset);
  135. }
  136. EditorTools.DisplayProgressBar("获取所有打包资源", ++progressValue, collectResult.CollectAssets.Count);
  137. }
  138. EditorTools.ClearProgressBar();
  139. // 搜集所有材质球
  140. progressValue = 0;
  141. List<string> allMaterial = new List<string>(1000);
  142. foreach (var assetPath in allAssets)
  143. {
  144. System.Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
  145. if (assetType == typeof(UnityEngine.Material))
  146. {
  147. allMaterial.Add(assetPath);
  148. }
  149. EditorTools.DisplayProgressBar("搜集所有材质球", ++progressValue, allAssets.Count);
  150. }
  151. EditorTools.ClearProgressBar();
  152. // 返回结果
  153. return allMaterial;
  154. }
  155. private static void CollectVariants(List<string> materials)
  156. {
  157. Camera camera = Camera.main;
  158. if (camera == null)
  159. throw new System.Exception("Not found main camera.");
  160. // 设置主相机
  161. float aspect = camera.aspect;
  162. int totalMaterials = materials.Count;
  163. float height = Mathf.Sqrt(totalMaterials / aspect) + 1;
  164. float width = Mathf.Sqrt(totalMaterials / aspect) * aspect + 1;
  165. float halfHeight = Mathf.CeilToInt(height / 2f);
  166. float halfWidth = Mathf.CeilToInt(width / 2f);
  167. camera.orthographic = true;
  168. camera.orthographicSize = halfHeight;
  169. camera.transform.position = new Vector3(0f, 0f, -10f);
  170. // 创建测试球体
  171. int xMax = (int)(width - 1);
  172. int x = 0, y = 0;
  173. int progressValue = 0;
  174. for (int i = 0; i < materials.Count; i++)
  175. {
  176. var material = materials[i];
  177. var position = new Vector3(x - halfWidth + 1f, y - halfHeight + 1f, 0f);
  178. var go = CreateSphere(material, position, i);
  179. if (go != null)
  180. _allSpheres.Add(go);
  181. if (x == xMax)
  182. {
  183. x = 0;
  184. y++;
  185. }
  186. else
  187. {
  188. x++;
  189. }
  190. EditorTools.DisplayProgressBar("照射所有材质球", ++progressValue, materials.Count);
  191. }
  192. EditorTools.ClearProgressBar();
  193. }
  194. private static GameObject CreateSphere(string assetPath, Vector3 position, int index)
  195. {
  196. var material = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
  197. var shader = material.shader;
  198. if (shader == null)
  199. return null;
  200. var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
  201. go.GetComponent<Renderer>().sharedMaterial = material;
  202. go.transform.position = position;
  203. go.name = $"Sphere_{index} | {material.name}";
  204. return go;
  205. }
  206. private static void DestroyAllSpheres()
  207. {
  208. foreach(var go in _allSpheres)
  209. {
  210. GameObject.DestroyImmediate(go);
  211. }
  212. _allSpheres.Clear();
  213. // 尝试释放编辑器加载的资源
  214. EditorUtility.UnloadUnusedAssetsImmediate(true);
  215. }
  216. private static void CreateManifest()
  217. {
  218. AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
  219. ShaderVariantCollection svc = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(_savePath);
  220. if (svc != null)
  221. {
  222. var wrapper = ShaderVariantCollectionManifest.Extract(svc);
  223. string jsonData = JsonUtility.ToJson(wrapper, true);
  224. string savePath = _savePath.Replace(".shadervariants", ".json");
  225. File.WriteAllText(savePath, jsonData);
  226. }
  227. AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
  228. }
  229. }
  230. }