forked from SixWays/UnityShaderStripper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathShaderStripperBase.cs
308 lines (284 loc) · 9.54 KB
/
ShaderStripperBase.cs
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
#if UNITY_2018_2_OR_NEWER
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine;
using UnityEngine.Rendering;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.Rendering;
#endif
namespace Sigtrap.Editors.ShaderStripper {
public class ShaderLog {
public List<string> log = new List<string>();
public string logName {get; private set;}
public int Count {get {return log.Count;}}
public ShaderLog(string logName){
this.logName = logName;
}
public void Add(string s){
log.Add(s);
}
public void Clear(){
log.Clear();
}
public void Insert(int index, string s){
log.Insert(index, s);
}
public bool Contains(string s){
return log.Contains(s);
}
public string[] ToArray(){
return log.ToArray();
}
}
/// <summary>
/// Base class for stripping shaders.
/// </summary>
public abstract class ShaderStripperBase : ScriptableObject {
[System.Serializable]
protected class StringMatch {
public enum MatchType {EQUALS, CONTAINS, STARTSWITH, ENDSWITH}
public MatchType matchType;
public string namePattern;
public bool caseInsensitive;
public bool Evaluate(string name){
if (string.IsNullOrEmpty(namePattern)) return false;
string n = caseInsensitive ? name.ToLower() : name;
string p = caseInsensitive ? namePattern.ToLower() : namePattern;
switch (matchType){
case MatchType.EQUALS:
return n == p;
case MatchType.CONTAINS:
return n.Contains(p);
case MatchType.STARTSWITH:
return n.StartsWith(p);
case MatchType.ENDSWITH:
return n.EndsWith(p);
}
return false;
}
}
#region Static
#if UNITY_EDITOR
static bool _deepLogs = false;
static List<string> _log = new List<string>();
public static string GetKeywordName(ShaderKeyword k){
#if UNITY_2018_3_OR_NEWER
return k.GetKeywordName();
#else
return k.GetName();
#endif
}
public static void OnPreBuild(bool deepLogs){
_log.Clear();
_deepLogs = deepLogs;
}
public static void OnPostBuild(string logFolderPath, string header, params ShaderLog[] logs){
if (!string.IsNullOrEmpty(logFolderPath)){
string logPath = logFolderPath;
if (!logPath.EndsWith("/") && !logPath.EndsWith("\\")){
logPath += "/";
}
string date = System.DateTime.Now.ToString("yyyy-MM-dd");
string strippedLogFile = string.Format(
"{0}ShaderStripperLog_{1}.txt",
logPath, date
);
_log.Insert(0, header);
System.IO.File.WriteAllLines(strippedLogFile, _log.ToArray());
foreach (var l in logs){
if (l != null && l.Count > 0){
l.Insert(0, header);
string logFile = string.Format(
"{0}ShaderStripperLog_{1}_{2}.txt",
logPath, date, l.logName
);
System.IO.File.WriteAllLines(logFile, l.ToArray());
}
}
Debug.Log("ShaderStripper logs created at "+logPath);
}
_log.Clear();
}
static protected void LogRemoval(ShaderStripperBase stripper, Shader shader, ShaderSnippetData pass){
if (!stripper._logOutput) return;
string log = string.Format(
"Stripping shader [{0}] pass type [{1}]\n\tShaderStripper: {2}",
shader.name, pass.passType, stripper.name
);
_log.Add(log);
}
static protected void LogRemoval(ShaderStripperBase stripper, Shader shader, ShaderSnippetData pass, int variantIndex, int variantCount, ShaderCompilerData variant){
if (!stripper._logOutput) return;
string log = null;
if (_deepLogs){
log = string.Format(
"Stripping shader [{0}] pass type [{1}] variant [{2}/{3}] [{4}]\n\tShaderStripper: {5}\n\tKeywords:",
shader.name, pass.passType, variantIndex, variantCount-1, variant.graphicsTier, stripper.name
);
var ks = variant.shaderKeywordSet.GetShaderKeywords();
if (ks != null && ks.Length > 0){
bool first = true;
foreach (var k in variant.shaderKeywordSet.GetShaderKeywords()){
if (!first) log += ",";
log += " " + GetKeywordName(k);
first = false;
}
} else {
log += " <no keywords>";
}
} else {
log = string.Format(
"Stripping shader [{0}] pass type [{1}] variant [{2}/{3}]\n\tShaderStripper: {4}",
shader.name, pass.passType, variantIndex, variantCount-1, stripper.name
);
}
_log.Add(log);
}
static protected void LogMessage(ShaderStripperBase stripper, string message, MessageType type=MessageType.None){
if (!stripper._logOutput) return;
string log = string.Format("ShaderStripper {0}: {1}", stripper.name, message);
switch (type){
case MessageType.Info:
case MessageType.None:
_log.Add(log);
break;
case MessageType.Warning:
log = "WARN: " + log;
_log.Add(log);
break;
case MessageType.Error:
log = "ERR: " + log;
_log.Add(log);
break;
}
}
#endif
#endregion
#region Serialized
[SerializeField, HideInInspector]
bool _expanded = true;
[SerializeField, HideInInspector]
int _order = -1;
[SerializeField]
bool _active = false;
public bool active {get {return _active;}}
[SerializeField]
string _notes;
[SerializeField]
protected bool _logOutput;
#endregion
#region Instance
#if UNITY_EDITOR
public virtual string description {get {return null;}}
public virtual string help {get {return null;}}
/// <summary>
/// Does this ShaderStripper check <see cref="Shader"> data (e.g. shader name)?
/// Must also override MatchShader().
/// </summary>
protected abstract bool _checkShader {get;}
/// <summary>
/// Does this ShaderStripper check shader pass data (e.g. pass type)?
/// Must also override MatchPass().
/// </summary>
protected abstract bool _checkPass {get;}
/// <summary>
/// Does this ShaderStripper check shader variant data (e.g. keywords)?
/// Must also override MatchVariant().
/// </summary>
protected abstract bool _checkVariants {get;}
/// <summary>
/// Selectively strip shader variants.
/// <para />Returns number of variants stripped.
/// <para />If StripCustom() is overridden and returns true, ONLY runs StripCustom.
/// <para />Otherwise:
/// <para />If (_checkShader) runs MatchShader().
/// <para />If (_checkPass) then runs MatchPass().
/// <para />If (_checkVariants) then runs MatchVariant().
/// <para />e.g. if (_checkShader && MatchShader() && !_checkPass && !_checkVariants) strip shader.
/// <para />e.g. if (_checkShader && MatchShader() && _checkPass && MatchPass() && !_checkVariants) strip this pass.
/// <para />e.g. if (!_checkShader && _checkPass && MatchPass() && _checkVariants && MatchVariant()) strip this variant.
/// </summary>
public int Strip(Shader shader, ShaderSnippetData passData, IList<ShaderCompilerData> variantData){
if (!active) return 0;
int initialVariants = variantData.Count;
bool skipMatch = StripCustom(shader, passData, variantData);
if (!skipMatch){
if (!_checkShader && !_checkPass && !_checkVariants){
LogMessage(this, "Checks nothing; skipping.", MessageType.Warning);
return 0;
}
// If match shader OR match pass, strip all variants
// If match variant, only strip one variant
bool matchedShader = true;
if (_checkShader){
matchedShader = MatchShader(shader);
}
if (matchedShader){
bool matchedPass = true;
if (_checkPass){
matchedPass = MatchPass(passData);
}
if (matchedPass){
if (_checkVariants){
// Iterate backwards to allow index-based removal
int c = variantData.Count;
for (int i=variantData.Count-1; i>=0; --i){
if (MatchVariant(variantData[i])){
LogRemoval(this, shader, passData, i, c, variantData[i]);
variantData.RemoveAt(i);
}
}
} else {
variantData.Clear();
LogRemoval(this, shader, passData);
}
}
}
}
return initialVariants - variantData.Count;
}
/// <summary>
/// Override to get a callback before the build starts.
/// <summary>
public virtual void Initialize(){}
/// <summary>
/// Override to perform completely custom stripping.
/// In most cases, override Match methods instead.
/// Return true to skip Match-based stripping.
/// </summary>
protected virtual bool StripCustom(Shader shader, ShaderSnippetData passData, IList<ShaderCompilerData> variantData){
return false;
}
/// <summary>
/// Override to strip based on shader data.
/// Return true to strip this shader, OR move to next match stage if (_checkPass || _checkVariants)
/// Must override <see cref="_checkShader"> to return <see cref="true">.
/// </summary>
protected virtual bool MatchShader(Shader shader){
throw new System.NotImplementedException("If _checkShader is true, must override MatchShader()");
}
/// <summary>
/// Override to strip based on pass data.
/// Return true to strip this pass, OR move to variant matching if (_checkVariants)
/// Must override <see cref="_checkPass"> to return <see cref="true">.
/// </summary>
protected virtual bool MatchPass(ShaderSnippetData passData){
throw new System.NotImplementedException("If _checkPass is true, must override MatchPass()");
}
/// <summary>
/// Override to strip based on variant data.
/// Return true to strip this variant.
/// Must override <see cref="_checkVariants"> to return <see cref="true">.
/// </summary>
protected virtual bool MatchVariant(ShaderCompilerData variantData){
throw new System.NotImplementedException("If _checkVariants is true, must override MatchVariant()");
}
public virtual void OnGUI(){}
#endif
#endregion
}
}
#endif