Skip to content

Commit f55ac38

Browse files
committed
Added new shader keyword features
1 parent 1d3f67b commit f55ac38

4 files changed

Lines changed: 134 additions & 47 deletions

File tree

Analyzer/Processors/ShaderProcessor.cs

Lines changed: 74 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ public class ShaderProcessor : IProcessor, IDisposable
1212
{
1313
SQLiteCommand m_InsertCommand;
1414
SQLiteCommand m_InsertSubProgramCommand;
15+
SQLiteCommand m_InsertKeywordCommand;
16+
SQLiteCommand m_InsertSubProgramKeywordsCommand;
1517

16-
Dictionary<int, string> m_KeywordNames = new Dictionary<int, string>();
17-
StringBuilder m_Keywords = new StringBuilder();
18-
HashSet<string> m_UniqueKeywords = new HashSet<string>();
19-
HashSet<uint> m_UniquePrograms = new HashSet<uint>();
18+
List<int> m_Keywords = new();
19+
Dictionary<int, string> m_KeywordNames = new();
20+
HashSet<uint> m_UniquePrograms = new();
21+
22+
static Dictionary<string, int> s_Keywords = new();
23+
static long s_SubProgramId = 0;
2024

2125
static readonly List<(string fieldName, string typeName)> s_progTypes = new()
2226
{
@@ -36,37 +40,43 @@ public void Init(SQLiteConnection db)
3640
command.ExecuteNonQuery();
3741

3842
m_InsertCommand = new SQLiteCommand(db);
39-
m_InsertCommand.CommandText = "INSERT INTO shaders(id, decompressed_size, sub_shaders, unique_programs, keywords) VALUES(@id, @decompressed_size, @sub_shaders, @unique_programs, @keywords)";
43+
m_InsertCommand.CommandText = "INSERT INTO shaders(id, decompressed_size, unique_programs) VALUES(@id, @decompressed_size, @unique_programs)";
4044
m_InsertCommand.Parameters.Add("@id", DbType.Int64);
4145
m_InsertCommand.Parameters.Add("@decompressed_size", DbType.Int32);
42-
m_InsertCommand.Parameters.Add("@sub_shaders", DbType.Int32);
4346
m_InsertCommand.Parameters.Add("@unique_programs", DbType.Int32);
44-
m_InsertCommand.Parameters.Add("@keywords", DbType.String);
4547

4648
m_InsertSubProgramCommand = new SQLiteCommand(db);
47-
m_InsertSubProgramCommand.CommandText = "INSERT INTO shader_subprograms(shader, pass, sub_program, hw_tier, shader_type, api, keywords) VALUES(@shader, @pass, @sub_program, @hw_tier, @shader_type, @api, @keywords)";
49+
m_InsertSubProgramCommand.CommandText = "INSERT INTO shader_subprograms(shader, sub_shader, pass, pass_name, sub_program, hw_tier, shader_type, api) VALUES(@shader, @sub_shader, @pass, @pass_name, @sub_program, @hw_tier, @shader_type, @api)";
50+
m_InsertSubProgramCommand.Parameters.Add("@id", DbType.Int64);
4851
m_InsertSubProgramCommand.Parameters.Add("@shader", DbType.Int64);
52+
m_InsertSubProgramCommand.Parameters.Add("@sub_shader", DbType.Int32);
4953
m_InsertSubProgramCommand.Parameters.Add("@pass", DbType.Int32);
54+
m_InsertSubProgramCommand.Parameters.Add("@pass_name", DbType.String);
5055
m_InsertSubProgramCommand.Parameters.Add("@sub_program", DbType.Int32);
5156
m_InsertSubProgramCommand.Parameters.Add("@hw_tier", DbType.Int32);
5257
m_InsertSubProgramCommand.Parameters.Add("@shader_type", DbType.String);
5358
m_InsertSubProgramCommand.Parameters.Add("@api", DbType.Int32);
54-
m_InsertSubProgramCommand.Parameters.Add("@keywords", DbType.String);
59+
60+
m_InsertKeywordCommand = new SQLiteCommand(db);
61+
m_InsertKeywordCommand.CommandText = "INSERT INTO shader_keywords(id, keyword) VALUES(@id, @keyword)";
62+
m_InsertKeywordCommand.Parameters.Add("@id", DbType.Int32);
63+
m_InsertKeywordCommand.Parameters.Add("@keyword", DbType.String);
64+
65+
m_InsertSubProgramKeywordsCommand = new SQLiteCommand(db);
66+
m_InsertSubProgramKeywordsCommand.CommandText = "INSERT INTO shader_subprogram_keywords(subprogram_id, keyword_id) VALUES (@subprogram_id, @keyword_id)";
67+
m_InsertSubProgramKeywordsCommand.Parameters.Add("@subprogram_id", DbType.Int64);
68+
m_InsertSubProgramKeywordsCommand.Parameters.Add("@keyword_id", DbType.Int32);
5569
}
5670

5771
public void Process(AnalyzerTool analyzer, long objectId, Dictionary<int, int> localToDbFileId, RandomAccessReader reader, out string name, out long streamedDataSize)
5872
{
59-
int currentProgram = 0;
60-
6173
streamedDataSize = 0;
6274

63-
m_UniqueKeywords.Clear();
6475
m_UniquePrograms.Clear();
6576

6677
var parsedForm = reader["m_ParsedForm"];
6778

6879
m_InsertCommand.Parameters["@id"].Value = objectId;
69-
m_InsertCommand.Parameters["@sub_shaders"].Value = parsedForm["m_SubShaders"].GetArraySize();
7080

7181
// Starting in some Unity 2021 version, keyword names are stored in m_KeywordNames.
7282
bool keywordsUnity2021 = false;
@@ -84,10 +94,13 @@ public void Process(AnalyzerTool analyzer, long objectId, Dictionary<int, int> l
8494
}
8595
}
8696

97+
int subShaderNum = 0;
8798
foreach (var subShader in parsedForm["m_SubShaders"])
8899
{
89100
int passNum = 0;
90101

102+
m_InsertSubProgramCommand.Parameters["@sub_shader"].Value = subShaderNum++;
103+
91104
foreach (var pass in subShader["m_Passes"])
92105
{
93106
if (!keywordsUnity2021)
@@ -102,6 +115,16 @@ public void Process(AnalyzerTool analyzer, long objectId, Dictionary<int, int> l
102115
}
103116
}
104117

118+
string passName = "";
119+
if (pass.HasChild("m_State"))
120+
{
121+
passName = pass["m_State"]["m_Name"].GetValue<string>();
122+
}
123+
124+
m_InsertSubProgramCommand.Parameters["@shader"].Value = objectId;
125+
m_InsertSubProgramCommand.Parameters["@pass"].Value = passNum;
126+
m_InsertSubProgramCommand.Parameters["@pass_name"].Value = passName;
127+
105128
foreach (var progType in s_progTypes)
106129
{
107130
if (!pass.HasChild(progType.fieldName))
@@ -111,6 +134,8 @@ public void Process(AnalyzerTool analyzer, long objectId, Dictionary<int, int> l
111134

112135
var program = pass[progType.fieldName];
113136

137+
m_InsertSubProgramCommand.Parameters["@shader_type"].Value = progType.typeName;
138+
114139
// Sarting in some Unity 2021.3 version, programs are stored in m_PlayerSubPrograms instead of m_SubPrograms.
115140
if (program.HasChild("m_PlayerSubPrograms"))
116141
{
@@ -119,12 +144,12 @@ public void Process(AnalyzerTool analyzer, long objectId, Dictionary<int, int> l
119144
// And they are stored per hardware tiers.
120145
foreach (var tierProgram in program["m_PlayerSubPrograms"])
121146
{
122-
ProcessProgram(objectId, passNum, ref currentProgram, tierProgram, progType.typeName, hwTier++);
147+
ProcessProgram(tierProgram, hwTier++);
123148
}
124149
}
125150
else
126151
{
127-
ProcessProgram(objectId, passNum, ref currentProgram, program["m_SubPrograms"], progType.typeName);
152+
ProcessProgram(program["m_SubPrograms"]);
128153
}
129154
}
130155

@@ -136,7 +161,7 @@ public void Process(AnalyzerTool analyzer, long objectId, Dictionary<int, int> l
136161

137162
if (!reader["decompressedLengths"].TypeTreeNode.Children[1].IsLeaf)
138163
{
139-
// The decompressed lengths are now stored per graphics API.
164+
// The decompressed lengths are stored per graphics API.
140165
foreach (var apiLengths in reader["decompressedLengths"])
141166
{
142167
foreach (var blockSize in apiLengths.GetValue<int[]>())
@@ -156,20 +181,15 @@ public void Process(AnalyzerTool analyzer, long objectId, Dictionary<int, int> l
156181
}
157182
}
158183

159-
m_Keywords.Clear();
160-
m_Keywords.AppendJoin(' ', m_UniqueKeywords);
161-
162184
m_InsertCommand.Parameters["@id"].Value = objectId;
163185
m_InsertCommand.Parameters["@decompressed_size"].Value = decompressedSize;
164-
m_InsertCommand.Parameters["@sub_shaders"].Value = parsedForm["m_SubShaders"].GetArraySize();
165186
m_InsertCommand.Parameters["@unique_programs"].Value = m_UniquePrograms.Count;
166-
m_InsertCommand.Parameters["@keywords"].Value = m_Keywords.ToString();
167187
m_InsertCommand.ExecuteNonQuery();
168188

169189
name = parsedForm["m_Name"].GetValue<string>();
170190
}
171191

172-
void ProcessProgram(long objectId, int passNum, ref int currentProgram, RandomAccessReader subPrograms, string shaderType, int hwTier = -1)
192+
void ProcessProgram(RandomAccessReader subPrograms, int hwTier = -1)
173193
{
174194
int progNum = 0;
175195

@@ -187,9 +207,7 @@ void ProcessProgram(long objectId, int passNum, ref int currentProgram, RandomAc
187207
{
188208
if (m_KeywordNames.TryGetValue(index, out var name))
189209
{
190-
m_Keywords.Append(name);
191-
m_Keywords.Append(' ');
192-
m_UniqueKeywords.Add(name);
210+
m_Keywords.Add(GetKeywordId(name));
193211
}
194212
}
195213
}
@@ -199,38 +217,59 @@ void ProcessProgram(long objectId, int passNum, ref int currentProgram, RandomAc
199217
{
200218
if (m_KeywordNames.TryGetValue(index, out var name))
201219
{
202-
m_Keywords.Append(name);
203-
m_Keywords.Append(' ');
204-
m_UniqueKeywords.Add(name);
220+
m_Keywords.Add(GetKeywordId(name));
205221
}
206222
}
207223

208224
foreach (var index in subProgram["m_LocalKeywordIndices"].GetValue<ushort[]>())
209225
{
210226
if (m_KeywordNames.TryGetValue(index, out var name))
211227
{
212-
m_Keywords.Append(name);
213-
m_Keywords.Append(' ');
214-
m_UniqueKeywords.Add(name);
228+
m_Keywords.Add(GetKeywordId(name));
215229
}
216230
}
217231
}
218232

219-
m_InsertSubProgramCommand.Parameters["@shader"].Value = objectId;
220-
m_InsertSubProgramCommand.Parameters["@pass"].Value = passNum;
233+
m_InsertSubProgramCommand.Parameters["@id"].Value = s_SubProgramId;
221234
m_InsertSubProgramCommand.Parameters["@sub_program"].Value = progNum++;
222235
m_InsertSubProgramCommand.Parameters["@hw_tier"].Value = hwTier != -1 ? hwTier : subProgram["m_ShaderHardwareTier"].GetValue<sbyte>();
223-
m_InsertSubProgramCommand.Parameters["@shader_type"].Value = shaderType;
224236
m_InsertSubProgramCommand.Parameters["@api"].Value = subProgram["m_GpuProgramType"].GetValue<sbyte>();
225-
m_InsertSubProgramCommand.Parameters["@keywords"].Value = m_Keywords.ToString();
226237
m_InsertSubProgramCommand.ExecuteNonQuery();
238+
239+
m_InsertSubProgramKeywordsCommand.Parameters["@subprogram_id"].Value = s_SubProgramId;
240+
foreach (var keyword in m_Keywords)
241+
{
242+
m_InsertSubProgramKeywordsCommand.Parameters["@keyword_id"].Value = keyword;
243+
m_InsertSubProgramKeywordsCommand.ExecuteNonQuery();
244+
}
245+
246+
++s_SubProgramId;
227247
}
228248
}
229249

250+
int GetKeywordId(string keyword)
251+
{
252+
int id;
253+
254+
if (!s_Keywords.TryGetValue(keyword, out id))
255+
{
256+
id = s_Keywords.Count;
257+
s_Keywords[keyword] = id;
258+
259+
m_InsertKeywordCommand.Parameters["@id"].Value = id;
260+
m_InsertKeywordCommand.Parameters["@keyword"].Value = keyword;
261+
m_InsertKeywordCommand.ExecuteNonQuery();
262+
}
263+
264+
return id;
265+
}
266+
230267
void IDisposable.Dispose()
231268
{
232269
m_InsertCommand.Dispose();
233270
m_InsertSubProgramCommand.Dispose();
271+
m_InsertKeywordCommand.Dispose();
272+
m_InsertSubProgramKeywordsCommand.Dispose();
234273
}
235274
}
236275
}

Analyzer/Resources/Finalize.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
CREATE INDEX refs_object_index ON refs(object);
22
CREATE INDEX refs_referenced_object_index ON refs(referenced_object);
3-
CREATE INDEX shader_sp_index ON shader_subprograms(shader);
3+
CREATE INDEX shader_subprograms_shader_index ON shader_subprograms(shader);
4+
5+
-- TODO: Processors should have a Finalize method and this should be moved into the ShaderProcessor.
6+
CREATE INDEX shader_subprogram_keywords_subprogram_id_index ON shader_subprogram_keywords(subprogram_id);

Analyzer/Resources/Init.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ END AS pretty_total_size,
7878
sum(size) AS total_size,
7979
size,
8080
pretty_size,
81-
REPLACE(GROUP_CONCAT(DISTINCT asset_bundle), ',', ', ') AS in_bundles
81+
REPLACE(GROUP_CONCAT(DISTINCT asset_bundle), ',', ',' || CHAR(13)) AS in_bundles
8282
FROM object_view
8383
GROUP BY name, type, size
8484
HAVING instances > 1

Analyzer/Resources/Shader.sql

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,57 @@
11
CREATE TABLE shaders(
22
id INTEGER,
33
decompressed_size INTEGER,
4-
sub_shaders INTEGER,
54
unique_programs INTEGER,
6-
keywords TEXT,
75
PRIMARY KEY (id)
86
);
97

8+
CREATE TABLE shader_keywords(
9+
id INTEGER,
10+
keyword TEXT,
11+
PRIMARY KEY (id)
12+
);
13+
14+
CREATE TABLE shader_subprogram_keywords(
15+
subprogram_id INTEGER,
16+
keyword_id INTEGER
17+
);
18+
1019
CREATE TABLE shader_apis(
1120
id INTEGER,
1221
name TEXT,
1322
PRIMARY KEY (id)
1423
);
1524

1625
CREATE TABLE shader_subprograms(
26+
id INTEGER,
1727
shader INTEGER,
28+
sub_shader INTEGER,
1829
pass INTEGER,
30+
pass_name TEXT,
1931
sub_program INTEGER,
2032
hw_tier INTEGER,
2133
shader_type TEXT,
2234
api INTEGER,
23-
keywords TEXT
35+
PRIMARY KEY(id)
2436
);
2537

2638
CREATE VIEW shader_view AS
2739
SELECT
2840
o.*,
2941
s.decompressed_size,
30-
s.sub_shaders,
42+
(SELECT MAX(sub_shader) FROM shader_subprograms sp WHERE s.id = sp.shader) + 1 AS sub_shaders,
3143
(SELECT COUNT(*) FROM shader_subprograms sp WHERE s.id = sp.shader) AS sub_programs,
3244
s.unique_programs,
33-
s.keywords
45+
(
46+
SELECT GROUP_CONCAT(k.keyword, ',' || CHAR(13)) FROM
47+
(
48+
SELECT DISTINCT kp.keyword_id FROM
49+
shader_subprograms sp
50+
INNER JOIN shader_subprogram_keywords kp ON sp.id = kp.subprogram_id
51+
WHERE sp.shader = s.id
52+
)
53+
INNER JOIN shader_keywords k ON keyword_id = k.id
54+
) AS keywords
3455
FROM object_view o
3556
INNER JOIN shaders s ON o.id = s.id;
3657

@@ -42,16 +63,40 @@ CASE
4263
WHEN sum(size) >= (1024 * 1024) AND sum(size) < (1024 * 1024 * 1024) THEN printf('%!5.1f MB', sum(size) / 1024.0 / 1024)
4364
WHEN sum(size) >= (1024 * 1024 * 1024) THEN printf('%!5.1f GB', sum(size) / 1024.0 / 1024 / 1024)
4465
END AS pretty_total_size,
45-
sum(size) AS total_size, GROUP_CONCAT(asset_bundle, CHAR(13)) AS in_bundles
66+
sum(size) AS total_size, GROUP_CONCAT(asset_bundle, ',' || CHAR(13)) AS in_bundles
4667
FROM shader_view
4768
GROUP BY name
4869
ORDER BY total_size DESC, instances DESC;
4970

5071
CREATE VIEW shader_subprogram_view AS
51-
SELECT s.*, pt.name AS api, sp.pass, sp.hw_tier, sp.shader_type, sp.keywords AS prog_keywords
52-
FROM shader_view s
53-
LEFT JOIN shader_subprograms sp ON s.id = sp.shader
54-
LEFT JOIN shader_apis pt ON pt.id = sp.api;
72+
SELECT sp.shader AS shader_id, o.name, sp.sub_shader, sp.hw_tier, api.name api, sp.pass, sp.pass_name, sp.shader_type, sp.sub_program, GROUP_CONCAT(k.keyword, ',' || CHAR(13)) AS keywords
73+
FROM shader_subprograms sp
74+
CROSS JOIN objects o ON o.id = sp.shader
75+
CROSS JOIN shader_apis api ON api.id = sp.api
76+
CROSS JOIN shader_subprogram_keywords sk ON sk.subprogram_id = sp.id
77+
CROSS JOIN shader_keywords k ON sk.keyword_id = k.id
78+
GROUP BY sp.id;
79+
80+
CREATE VIEW shader_keyword_ratios AS
81+
SELECT t.shader_id, o.name, t.sub_shader, t.hw_tier, t.pass, api.name AS api, t.pass_name, t.shader_type, t.total_variants, k.keyword, t.variants, t.ratio
82+
FROM
83+
(
84+
SELECT sp.shader AS shader_id, sp.sub_shader, sp.hw_tier, sp.api, sp.pass, sp.pass_name, sp.shader_type, sp.total_variants, sk.keyword_id,
85+
COUNT(*) AS variants,
86+
printf('%.3f', CAST(COUNT(*) AS FLOAT) / sp.total_variants) AS ratio
87+
FROM
88+
(
89+
SELECT id, shader, sub_shader, hw_tier, api, pass, pass_name, shader_type,
90+
COUNT(id) OVER(PARTITION BY shader, sub_shader, hw_tier, api, pass, shader_type) AS total_variants
91+
FROM shader_subprograms
92+
) sp
93+
INNER JOIN shader_subprogram_keywords sk ON sk.subprogram_id = sp.id
94+
GROUP BY shader_id, sp.sub_shader, sp.hw_tier, sp.api, sp.pass, sp.shader_type, sk.keyword_id
95+
ORDER BY shader_id, sp.sub_shader, sp.hw_tier, sp.api, sp.pass, sp.shader_type, ratio DESC
96+
) t
97+
CROSS JOIN objects o ON o.id = t.shader_id
98+
CROSS JOIN shader_apis api ON api.id = t.api
99+
CROSS JOIN shader_keywords k ON k.id = t.keyword_id;
55100

56101
INSERT INTO shader_apis (name, id)
57102
VALUES

0 commit comments

Comments
 (0)