检测并删除材质中记录的冗余Keywords和Shader属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#region classname  
/*
* title: classname : Ufw
* Description:
* 功能: 检测并删除材质中记录的冗余Keywords和Shader属性
* Author: xxx
* Time: 2022年9月13日11:21:27
* Version: 0.1版本
* Modify Recorde: */
#endregion
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;

namespace Ufw
{
public class UnusedKeywordsAndPropertiesCheckerEditor : Editor
{
//获取shader中所有的宏
private static bool GetShaderKeywords(Shader target, out string[] global, out string[] local)
{
try
{
MethodInfo globalKeywords = typeof(ShaderUtil).GetMethod("GetShaderGlobalKeywords", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo localKeywords = typeof(ShaderUtil).GetMethod("GetShaderLocalKeywords", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
if (globalKeywords != null)
{
global = (string[])globalKeywords.Invoke(null, new object[]
{ target
});
}
else
{
global = null;
}
if (localKeywords != null)
{
local = (string[])localKeywords.Invoke(null, new object[]
{ target
});
}
else
{
local = null;
}
return true;
}
catch (Exception e)
{
Log.Error($"获取shader中所有的宏,错误: {e.Message}");
global = local = null;
return false;
}
}

[MenuItem(UfwMenuItemIni.M_Check_MatKey)]
public static void CheckMaterials()
{
EditorUtility.ClearProgressBar();
ProfilerUtils.BeginWatch("CheckMaterials");
string[] paths = AssetDatabase.FindAssets("t:material", new[]
{
"Assets/Res"
});
if (paths == null || paths.Length == 0)
{
Log.Info($"未找到相匹配的文件 匹配格式: t:material 匹配路径: Assets/Res");
return;
}
int index = 0;
int maxCount = paths.Length;
foreach (string materialGuid in paths)
{
string materialAssetPath = AssetDatabase.GUIDToAssetPath(materialGuid);
Material material = AssetDatabase.LoadAssetAtPath<Material>(materialAssetPath);
EditorUtility.DisplayProgressBar($"读取文件Material文件: {material.name}", $"正在查询内容", (float)index / maxCount);
if (GetShaderKeywords(material.shader, out string[] global, out string[] local))
{
HashSet<string> keywords = new HashSet<string>();
if (global != null)
{ foreach (string g in global)
{
keywords.Add(g);
}
}
if (local != null)
{ foreach (string l in local)
{
keywords.Add(l);
}
}
if (keywords.Count == 0)
{ Log.Warn($"读取文件失效,请检查Material: {material.name} Shader: {material.shader}");
continue;
}
EditorUtility.ClearProgressBar();

//重置keywords
List<string> resetKeywords = new List<string>(material.shaderKeywords);
int removeIndex = 0;
foreach (string item in material.shaderKeywords)
{ EditorUtility.DisplayProgressBar($"检查KeyWords: {material.name}", $"匹配字段 {item}", (float)removeIndex / resetKeywords.Count);
if (!keywords.Contains(item))
{
resetKeywords.Remove(item);
}
removeIndex++;
}
material.shaderKeywords = resetKeywords.ToArray();
}
else
{
Log.Warn($"获取shader中所有的宏,Error: Material:{material.name} shader:{material.shader}");
continue;
}
EditorUtility.ClearProgressBar();
HashSet<string> property = new HashSet<string>();
int count = material.shader.GetPropertyCount();
for (int i = 0; i < count; i++)
{
property.Add(material.shader.GetPropertyName(i));
}
SerializedObject o = new SerializedObject(material);
SerializedProperty disabledShaderPasses = o.FindProperty("disabledShaderPasses");
SerializedProperty savedProperties = o.FindProperty("m_SavedProperties");
SerializedProperty texEnvs = savedProperties.FindPropertyRelative("m_TexEnvs");
SerializedProperty floats = savedProperties.FindPropertyRelative("m_Floats");
SerializedProperty colors = savedProperties.FindPropertyRelative("m_Colors");
int removeAttrId = 0;
int removeAttrCount = disabledShaderPasses.arraySize + texEnvs.arraySize + floats.arraySize + colors.arraySize;
EditorUtility.ClearProgressBar();
//对比属性删除残留的属性
for (int i = disabledShaderPasses.arraySize - 1; i >= 0; i--)
{
string displayName = disabledShaderPasses.GetArrayElementAtIndex(i).displayName;
EditorUtility.DisplayProgressBar($"{material.name} 对比:disabledShaderPasses", $"检查属性{displayName}", (float)removeAttrId / removeAttrCount);
if (!property.Contains(displayName))
{ disabledShaderPasses.DeleteArrayElementAtIndex(i);
}
removeAttrId++;
}
EditorUtility.ClearProgressBar();
for (int i = texEnvs.arraySize - 1; i >= 0; i--)
{
string displayName = texEnvs.GetArrayElementAtIndex(i).displayName;
EditorUtility.DisplayProgressBar($"{material.name} 对比:m_TexEnvs", $"检查属性{displayName}", (float)removeAttrId / removeAttrCount);
if (!property.Contains(displayName))
{ texEnvs.DeleteArrayElementAtIndex(i);
}
removeAttrId++;
}
EditorUtility.ClearProgressBar();
for (int i = floats.arraySize - 1; i >= 0; i--)
{
string displayName = floats.GetArrayElementAtIndex(i).displayName;
EditorUtility.DisplayProgressBar($"{material.name} 对比:m_Floats", $"检查属性{displayName}", (float)removeAttrId / removeAttrCount);
if (!property.Contains(displayName))
{ floats.DeleteArrayElementAtIndex(i);
}
removeAttrId++;
}
EditorUtility.ClearProgressBar();
for (int i = colors.arraySize - 1; i >= 0; i--)
{
string displayName = colors.GetArrayElementAtIndex(i).displayName;
EditorUtility.DisplayProgressBar($"{material.name} 对比:m_Colors", $"检查属性{displayName}", (float)removeAttrId / removeAttrCount);
if (!property.Contains(displayName))
{ colors.DeleteArrayElementAtIndex(i);
}
removeAttrId++;
}
o.ApplyModifiedProperties();
index += 1;
}
UWatchResult result = ProfilerUtils.EndWatch("CheckMaterials");
Log.Info($"清理Material残留 时间{result.costTime} [内存]{result.costMemory}");
EditorUtility.ClearProgressBar();
AssetDatabase.SaveAssets();
Log.Info("检查完毕");
}
}
}