Files
plugin-library/Assets/00.StaryEvo/Editor/YooAsset/ShaderVariantCollector.cs

331 lines
11 KiB
C#
Raw Normal View History

2025-06-25 15:47:51 +08:00
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.SceneManagement;
using YooAsset.Editor;
public static class ShaderVariantCollector
{
private enum ESteps
{
None,
Prepare,
CollectAllMaterial,
CollectVariants,
CollectSleeping,
WaitingDone,
}
private const float WaitMilliseconds = 3000f;
2025-07-23 10:01:24 +08:00
2025-06-25 15:47:51 +08:00
private const float SleepMilliseconds = 3000f;
2025-07-22 16:08:43 +08:00
// private static string _savePath;
// private static string _packageName;
// private static int _processMaxNum;
// private static Action _completedCallback;
//
// private static ESteps _steps = ESteps.None;
// private static Stopwatch _elapsedTime;
//private static List<string> _allMaterials;
// private static List<GameObject> _allSpheres = new List<GameObject>(1000);
2025-06-25 15:47:51 +08:00
2025-07-22 16:08:43 +08:00
// 新增任务队列
private static Queue<CollectTask> _taskQueue = new Queue<CollectTask>();
private static CollectTask _currentTask;
2025-07-23 10:01:24 +08:00
public static string OriginalScenePath { get; set; }
static ShaderVariantCollector()
{
// 保存当前场景路径
OriginalScenePath = EditorSceneManager.GetActiveScene().path;
}
2025-06-25 15:47:51 +08:00
2025-07-22 16:08:43 +08:00
// 新增初始化方法
private static void InitializeTask(CollectTask task)
{
// 原有Run方法中的初始化逻辑迁移到这里
if (Path.HasExtension(task.SavePath) == false)
task.SavePath = $"{task.SavePath}.shadervariants";
AssetDatabase.DeleteAsset(task.SavePath);
EditorTools.CreateFileDirectory(task.SavePath);
2025-06-25 15:47:51 +08:00
2025-07-22 16:08:43 +08:00
task.Steps = ESteps.Prepare;
}
2025-07-23 10:01:24 +08:00
2025-06-25 15:47:51 +08:00
/// <summary>
/// 开始收集
/// </summary>
public static void Run(string savePath, string packageName, int processMaxNum, Action completedCallback)
{
if (Path.HasExtension(savePath) == false)
savePath = $"{savePath}.shadervariants";
if (Path.GetExtension(savePath) != ".shadervariants")
throw new System.Exception("Shader variant file extension is invalid.");
if (string.IsNullOrEmpty(packageName))
throw new System.Exception("Package name is null or empty !");
2025-07-22 16:08:43 +08:00
// 将新任务加入队列
_taskQueue.Enqueue(new CollectTask
{
SavePath = savePath,
PackageName = packageName,
ProcessMaxNum = processMaxNum,
CompletedCallback = completedCallback
});
// 如果当前没有正在执行的任务,开始执行队列
if (_currentTask == null)
{
EditorApplication.update -= EditorUpdate;
EditorApplication.update += EditorUpdate;
2025-07-23 10:01:24 +08:00
// 聚焦到游戏窗口
//EditorTools.FocusUnityGameWindow();
// 创建临时测试场景
CreateTempScene();
}
2025-06-25 15:47:51 +08:00
}
private static void EditorUpdate()
{
2025-07-22 16:08:43 +08:00
// 当前没有活动任务时从队列获取新任务
if (_currentTask == null && _taskQueue.Count > 0)
{
_currentTask = _taskQueue.Dequeue();
InitializeTask(_currentTask);
}
2025-07-23 10:01:24 +08:00
2025-07-22 16:08:43 +08:00
if (_currentTask == null)
2025-06-25 15:47:51 +08:00
return;
2025-07-22 16:08:43 +08:00
if (_currentTask.Steps == ESteps.None)
return;
if (_currentTask.Steps == ESteps.Prepare)
2025-06-25 15:47:51 +08:00
{
ShaderVariantCollectionHelper.ClearCurrentShaderVariantCollection();
2025-07-22 16:08:43 +08:00
_currentTask.Steps = ESteps.CollectAllMaterial;
2025-06-25 15:47:51 +08:00
return; //等待一帧
}
2025-07-22 16:08:43 +08:00
if (_currentTask.Steps == ESteps.CollectAllMaterial)
2025-06-25 15:47:51 +08:00
{
2025-07-22 16:08:43 +08:00
_currentTask.AllMaterials = GetAllMaterials();
_currentTask.Steps = ESteps.CollectVariants;
2025-06-25 15:47:51 +08:00
return; //等待一帧
}
2025-07-22 16:08:43 +08:00
if (_currentTask.Steps == ESteps.CollectVariants)
2025-06-25 15:47:51 +08:00
{
2025-07-23 10:01:24 +08:00
int count = Mathf.Min(_currentTask.ProcessMaxNum, _currentTask.AllMaterials.Count);
List<string> range = _currentTask.AllMaterials.GetRange(0, count);
2025-07-22 16:08:43 +08:00
_currentTask.AllMaterials.RemoveRange(0, count);
2025-06-25 15:47:51 +08:00
CollectVariants(range);
2025-07-23 10:01:24 +08:00
if (_currentTask.AllMaterials.Count > 0)
2025-06-25 15:47:51 +08:00
{
2025-07-22 16:08:43 +08:00
_currentTask.ElapsedTime = Stopwatch.StartNew();
_currentTask.Steps = ESteps.CollectSleeping;
2025-06-25 15:47:51 +08:00
}
else
{
2025-07-22 16:08:43 +08:00
_currentTask.ElapsedTime = Stopwatch.StartNew();
_currentTask.Steps = ESteps.WaitingDone;
2025-06-25 15:47:51 +08:00
}
}
2025-07-22 16:08:43 +08:00
if (_currentTask.Steps == ESteps.CollectSleeping)
2025-06-25 15:47:51 +08:00
{
2025-07-22 16:08:43 +08:00
if (_currentTask.ElapsedTime.ElapsedMilliseconds > SleepMilliseconds)
2025-06-25 15:47:51 +08:00
{
DestroyAllSpheres();
2025-07-22 16:08:43 +08:00
_currentTask.ElapsedTime.Stop();
2025-07-23 10:01:24 +08:00
_currentTask.Steps = ESteps.CollectVariants;
2025-06-25 15:47:51 +08:00
}
}
2025-07-23 10:01:24 +08:00
if (_currentTask.Steps == ESteps.WaitingDone)
2025-06-25 15:47:51 +08:00
{
// 注意:一定要延迟保存才会起效
2025-07-23 10:01:24 +08:00
if (_currentTask.ElapsedTime.ElapsedMilliseconds > WaitMilliseconds)
2025-06-25 15:47:51 +08:00
{
2025-07-22 16:08:43 +08:00
_currentTask.ElapsedTime.Stop();
_currentTask.Steps = ESteps.None;
2025-06-25 15:47:51 +08:00
// 保存结果并创建清单
2025-07-22 16:08:43 +08:00
ShaderVariantCollectionHelper.SaveCurrentShaderVariantCollection(_currentTask.SavePath);
2025-06-25 15:47:51 +08:00
CreateManifest();
UnityEngine.Debug.Log($"搜集SVC完毕");
2025-07-22 16:08:43 +08:00
2025-07-23 10:01:24 +08:00
_currentTask.CompletedCallback?.Invoke();
_currentTask = null;
2025-07-22 16:08:43 +08:00
2025-07-23 10:01:24 +08:00
// 如果队列还有任务,立即触发下一次更新
if (_taskQueue.Count > 0)
{
EditorApplication.QueuePlayerLoopUpdate();
}
else
{
// 恢复原场景
if (!string.IsNullOrEmpty(OriginalScenePath))
{
EditorSceneManager.OpenScene(OriginalScenePath);
}
}
2025-06-25 15:47:51 +08:00
}
}
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
private static void CreateTempScene()
{
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects);
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
private static List<string> GetAllMaterials()
{
// 获取所有打包的资源
2025-07-23 10:01:24 +08:00
CollectResult collectResult =
AssetBundleCollectorSettingData.Setting.BeginCollect(_currentTask.PackageName, false, false);
2025-06-25 15:47:51 +08:00
// 搜集所有材质球
int progressValue = 0;
HashSet<string> result = new HashSet<string>();
foreach (var collectAssetInfo in collectResult.CollectAssets)
{
if (collectAssetInfo.AssetInfo.AssetType == typeof(UnityEngine.Material))
{
string assetPath = collectAssetInfo.AssetInfo.AssetPath;
if (result.Contains(assetPath) == false)
result.Add(assetPath);
}
2025-07-22 16:08:43 +08:00
foreach (var dependAssetInfo in collectAssetInfo.DependAssets)
2025-06-25 15:47:51 +08:00
{
if (dependAssetInfo.AssetType == typeof(UnityEngine.Material))
{
string assetPath = dependAssetInfo.AssetPath;
if (result.Contains(assetPath) == false)
result.Add(assetPath);
}
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
EditorTools.DisplayProgressBar("搜集所有材质球", ++progressValue, collectResult.CollectAssets.Count);
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
EditorTools.ClearProgressBar();
// 返回结果
return result.ToList();
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
private static void CollectVariants(List<string> materials)
{
Camera camera = Camera.main;
if (camera == null)
throw new System.Exception("Not found main camera.");
// 设置主相机
float aspect = camera.aspect;
int totalMaterials = materials.Count;
float height = Mathf.Sqrt(totalMaterials / aspect) + 1;
float width = Mathf.Sqrt(totalMaterials / aspect) * aspect + 1;
float halfHeight = Mathf.CeilToInt(height / 2f);
float halfWidth = Mathf.CeilToInt(width / 2f);
camera.orthographic = true;
camera.orthographicSize = halfHeight;
camera.transform.position = new Vector3(0f, 0f, -10f);
// 创建测试球体
int xMax = (int)(width - 1);
int x = 0, y = 0;
int progressValue = 0;
for (int i = 0; i < materials.Count; i++)
{
var material = materials[i];
var position = new Vector3(x - halfWidth + 1f, y - halfHeight + 1f, 0f);
var go = CreateSphere(material, position, i);
if (go != null)
2025-07-22 16:08:43 +08:00
_currentTask.AllSpheres.Add(go);
2025-06-25 15:47:51 +08:00
if (x == xMax)
{
x = 0;
y++;
}
else
{
x++;
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
EditorTools.DisplayProgressBar("照射所有材质球", ++progressValue, materials.Count);
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
EditorTools.ClearProgressBar();
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
private static GameObject CreateSphere(string assetPath, Vector3 position, int index)
{
var material = AssetDatabase.LoadAssetAtPath<Material>(assetPath);
var shader = material.shader;
if (shader == null)
return null;
var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
go.GetComponent<Renderer>().sharedMaterial = material;
go.transform.position = position;
go.name = $"Sphere_{index} | {material.name}";
return go;
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
private static void DestroyAllSpheres()
{
2025-07-22 16:08:43 +08:00
foreach (var go in _currentTask.AllSpheres)
2025-06-25 15:47:51 +08:00
{
GameObject.DestroyImmediate(go);
}
2025-07-22 16:08:43 +08:00
_currentTask.AllSpheres.Clear();
2025-06-25 15:47:51 +08:00
// 尝试释放编辑器加载的资源
EditorUtility.UnloadUnusedAssetsImmediate(true);
}
2025-07-22 16:08:43 +08:00
2025-06-25 15:47:51 +08:00
private static void CreateManifest()
{
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
2025-07-22 16:08:43 +08:00
ShaderVariantCollection svc = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(_currentTask.SavePath);
2025-06-25 15:47:51 +08:00
if (svc != null)
{
var wrapper = ShaderVariantCollectionManifest.Extract(svc);
string jsonData = JsonUtility.ToJson(wrapper, true);
2025-07-22 16:08:43 +08:00
string savePath = _currentTask.SavePath.Replace(".shadervariants", ".json");
2025-06-25 15:47:51 +08:00
File.WriteAllText(savePath, jsonData);
}
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
}
2025-07-22 16:08:43 +08:00
// 新增任务类
private class CollectTask
{
public string SavePath;
public string PackageName;
public int ProcessMaxNum;
public Action CompletedCallback;
public ESteps Steps = ESteps.None;
public Stopwatch ElapsedTime;
public List<string> AllMaterials;
public List<GameObject> AllSpheres = new List<GameObject>();
}
2025-07-23 10:01:24 +08:00
}