]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/debug.cpp
Use extended alignment in SPIR-V struct layout when necessary
[libs/gl.git] / source / glsl / debug.cpp
1 #include <msp/stringcodec/utf8.h>
2 #include <msp/strings/format.h>
3 #include "debug.h"
4
5 using namespace std;
6
7 namespace Msp {
8 namespace GL {
9 namespace SL {
10
11 const DumpTree::Colors DumpTree::no_colors =
12 { "", "", "", "", "", "", "" };
13
14 const DumpTree::Colors DumpTree::default_colors =
15 {
16         "\033[0m",         // default
17         "\033[38;5;250m",  // tree
18         "\033[38;5;244m",  // location
19         "\033[38;5;146m",  // label
20         "\033[38;5;194m",  // type
21         "\033[38;5;230m",  // name
22         "\033[38;5;210m"   // error
23 };
24
25 DumpTree::DumpTree(bool use_colors):
26         colors(use_colors ? default_colors : no_colors)
27 { }
28
29 string DumpTree::apply(Stage &stage)
30 {
31         formatted = format("Stage: %s\n", Stage::get_stage_name(stage.type));
32         begin_sub();
33         append(format("Version: %d.%02d", stage.required_features.glsl_version.major, stage.required_features.glsl_version.minor));
34
35         for(const auto &kvp: stage.types)
36                 append(format("Type: %s %s%s%s", get_label(*kvp.second), colors.type_color, kvp.first, colors.default_color));
37
38         for(const auto &kvp: stage.interface_blocks)
39                 append(format("Interface block: %s %s", get_label(*kvp.second), format_name(kvp.first)));
40
41         for(const auto &kvp: stage.functions)
42                 append(format("Function: %s %s", get_label(*kvp.second), format_name(kvp.first)));
43
44         last_branch();
45         stage.content.visit(*this);
46         return formatted;
47 }
48
49 void DumpTree::append(const string &line)
50 {
51         formatted += colors.tree_color;
52         StringCodec::Utf8::Encoder enc;
53         for(auto i=tree.begin(); i!=tree.end(); )
54         {
55                 enc.encode_char(*i++, formatted);
56                 enc.encode_char((i==tree.end() ? REACH : EMPTY), formatted);
57         }
58         formatted += colors.default_color;
59         formatted += line;
60         formatted += '\n';
61 }
62
63 void DumpTree::append(const Node &node, const string &line)
64 {
65         string location;
66         if(node.source==BUILTIN_SOURCE)
67                 location = "builtin";
68         else if(node.source==INTERNAL_SOURCE)
69                 location = "internal";
70         else if(node.source==GENERATED_SOURCE)
71                 location = "generated";
72         else
73                 location = format("%s:%s", node.source, node.line);
74         append(format("%s<%s>%s %s", colors.location_color, location, colors.default_color, line));
75 }
76
77 void DumpTree::append_subtree(const vector<Branch> &branches)
78 {
79         begin_sub();
80         for(auto i=branches.begin(); i!=branches.end(); )
81         {
82                 auto j = increment(i, branches);
83                 if(!j->text.empty())
84                 {
85                         append(j->text);
86                         if(j->node)
87                         {
88                                 begin_sub();
89                                 last_branch();
90                                 j->node->visit(*this);
91                                 end_sub();
92                         }
93                 }
94                 else
95                         j->node->visit(*this);
96         }
97         end_sub();
98 }
99
100 void DumpTree::append_subtree(Node &node)
101 {
102         begin_sub();
103         last_branch();
104         node.visit(*this);
105         end_sub();
106 }
107
108 void DumpTree::begin_sub()
109 {
110         if(!tree.empty())
111                 tree.back() = (tree.back()==BRANCH_LAST ? EMPTY : STRAIGHT);
112         tree.push_back(BRANCH);
113 }
114
115 void DumpTree::last_branch()
116 {
117         tree.back() = BRANCH_LAST;
118 }
119
120 void DumpTree::end_sub()
121 {
122         tree.pop_back();
123         if(!tree.empty() && tree.back()==STRAIGHT)
124                 tree.back() = BRANCH;
125 }
126
127 std::string DumpTree::get_label(const Node &node)
128 {
129         unsigned &label = node_labels[&node];
130         if(!label)
131                 label = node_labels.size();
132         return format("%s%%%d%s", colors.label_color, label, colors.default_color);
133 }
134
135 string DumpTree::format_type(TypeDeclaration *type)
136 {
137         return type ? format_type(type->name) : format("%s?%s", colors.error_color, colors.default_color);
138 }
139
140 string DumpTree::format_type(const string &type)
141 {
142         return format("%s%s%s", colors.type_color, type, colors.default_color);
143 }
144
145 string DumpTree::format_name(const string &name)
146 {
147         return format("%s%s%s", colors.name_color, name, colors.default_color);
148 }
149
150 template<typename T>
151 typename T::const_iterator DumpTree::increment(typename T::const_iterator &iter, const T &container)
152 {
153         auto ret = iter++;
154         if(iter==container.end())
155                 last_branch();
156         return ret;
157 }
158
159 void DumpTree::visit(Block &block)
160 {
161         append(block, format("Block %s", (block.use_braces ? "{}" : "(inline)")));
162         begin_sub();
163
164         for(const auto &kvp: block.variables)
165                 append(format("Variable: %s %s %s", get_label(*kvp.second), format_type(kvp.second->type), format_name(kvp.first)));
166
167         for(auto i=block.body.cbegin(); i!=block.body.cend(); )
168         {
169                 auto j = increment(i, block.body);
170                 (*j)->visit(*this);
171         }
172
173         end_sub();
174 }
175
176 void DumpTree::visit(Literal &literal)
177 {
178         append(literal, format("Literal: %s -> %s", literal.token, format_type(literal.type)));
179 }
180
181 void DumpTree::visit(VariableReference &var)
182 {
183         string text;
184         if(var.declaration)
185                 text += format("%s ", get_label(*var.declaration));
186         text += format("%s (var) -> %s", format_name(var.name), format_type(var.type));
187         append(var, text);
188 }
189
190 void DumpTree::visit(InterfaceBlockReference &iface)
191 {
192         string text;
193         if(iface.declaration)
194                 text += format("%s ", get_label(*iface.declaration));
195         text += format("%s (iface) -> %s", format_name(iface.name), format_type(iface.type));
196         append(iface, text);
197 }
198
199 void DumpTree::visit(MemberAccess &memacc)
200 {
201         string text = "Member access:";
202         if(memacc.declaration)
203                 text += format(" %s", get_label(*memacc.declaration));
204         text += format(" .%s (%d) -> %s", format_name(memacc.member), memacc.index, format_type(memacc.type));
205         append(memacc, text);
206         append_subtree(*memacc.left);
207 }
208
209 void DumpTree::visit(Swizzle &swizzle)
210 {
211         static const char components[4] = { 'x', 'y', 'z', 'w' };
212         string text = "Swizzle: .";
213         for(unsigned i=0; i<swizzle.count; ++i)
214                 text += components[swizzle.components[i]];
215         text += format(" -> %s", format_type(swizzle.type));
216         append(swizzle, text);
217         append_subtree(*swizzle.left);
218 }
219
220 void DumpTree::visit(UnaryExpression &unary)
221 {
222         string text = format("Unary: %s, %sfix -> %s", unary.oper->token, (unary.oper->type==Operator::PREFIX ? "pre" : "post"), format_type(unary.type));
223         append(unary, text);
224         append_subtree(*unary.expression);
225 }
226
227 void DumpTree::visit(BinaryExpression &binary)
228 {
229         append(binary, format("Binary: %s%s -> %s", binary.oper->token, binary.oper->token2, format_type(binary.type)));
230         begin_sub();
231         binary.left->visit(*this);
232         last_branch();
233         binary.right->visit(*this);
234         end_sub();
235 }
236
237 void DumpTree::visit(Assignment &assign)
238 {
239         append(assign, format("Assignment: %s%s -> %s", assign.oper->token, (assign.self_referencing ? " (self-referencing)" : ""), format_type(assign.type)));
240         begin_sub();
241         if(assign.target.declaration)
242         {
243                 string text = format("Target: %s", get_label(*assign.target.declaration));
244
245                 static const char swizzle[4] = { 'x', 'y', 'z', 'w' };
246                 for(unsigned i=0; i<assign.target.chain_len; ++i)
247                 {
248                         unsigned component = assign.target.chain[i];
249                         switch(static_cast<Assignment::Target::ChainType>(component&0xC0))
250                         {
251                         case Assignment::Target::MEMBER:
252                                 text += format(" .%d", component&0x3F);
253                                 break;
254                         case Assignment::Target::SWIZZLE:
255                                 text += " .";
256                                 for(unsigned j=0; j<4; ++j)
257                                         if(component&(1<<j))
258                                                 text += swizzle[j];
259                                 break;
260                         case Assignment::Target::ARRAY:
261                                 text += format(" [%d]", component&0x3F);
262                                 break;
263                         }
264                 }
265                 append(text);
266         }
267         assign.left->visit(*this);
268         last_branch();
269         assign.right->visit(*this);
270         end_sub();
271 }
272
273 void DumpTree::visit(TernaryExpression &ternary)
274 {
275         append(ternary, format("Ternary: %s%s -> %s", ternary.oper->token, ternary.oper->token2, format_type(ternary.type)));
276         begin_sub();
277         ternary.condition->visit(*this);
278         ternary.true_expr->visit(*this);
279         last_branch();
280         ternary.false_expr->visit(*this);
281         end_sub();
282 }
283
284 void DumpTree::visit(FunctionCall &call)
285 {
286         string head = "Function call: ";
287         if(call.declaration)
288                 head += format("%s ", get_label(*call.declaration));
289         head += format_name(call.name);
290         if(call.constructor)
291                 head += " (constructor)";
292         head += format(" -> %s", format_type(call.type));
293         append(call, head);
294
295         begin_sub();
296         for(auto i=call.arguments.cbegin(); i!=call.arguments.cend(); )
297         {
298                 auto j = increment(i, call.arguments);
299                 (*j)->visit(*this);
300         }
301         end_sub();
302 }
303
304 void DumpTree::visit(ExpressionStatement &expr)
305 {
306         append(expr, "expr;");
307         append_subtree(*expr.expression);
308 }
309
310 void DumpTree::visit(Import &import)
311 {
312         append(import, format("import %s", import.module));
313 }
314
315 void DumpTree::visit(Precision &prec)
316 {
317         append(prec, format("precision %s %s", prec.precision, prec.type));
318 }
319
320 void DumpTree::visit(Layout &layout)
321 {
322         append(layout, "Layout");
323         begin_sub();
324         for(auto i=layout.qualifiers.cbegin(); i!=layout.qualifiers.cend(); )
325         {
326                 auto j = increment(i, layout.qualifiers);
327                 string qualifier = j->name;
328                 if(j->has_value)
329                         qualifier += format("=%d", j->value);
330                 append(qualifier);
331         }
332         end_sub();
333 }
334
335 void DumpTree::visit(InterfaceLayout &layout)
336 {
337         append(layout, format("Layout: %s", layout.interface));
338         append_subtree(layout.layout);
339 }
340
341 void DumpTree::visit(BasicTypeDeclaration &type)
342 {
343         append(type, format("%s typedef %s", get_label(type), format_type(type.name)));
344
345         vector<Branch> branches;
346         if(type.base_type)
347                 branches.emplace_back(format("%s: %s %s", (type.kind==BasicTypeDeclaration::ALIAS ? "Alias of" : "Base"), get_label(*type.base_type), format_type(type.base_type->name)));
348         if(type.kind==BasicTypeDeclaration::VECTOR)
349                 branches.emplace_back(format("Vector: %d", type.size));
350         else if(type.kind==BasicTypeDeclaration::MATRIX)
351                 branches.emplace_back(format("Matrix: %dx%d", type.size&0xFFFF, type.size>>16));
352         if(type.extended_alignment)
353                 branches.emplace_back("Extended alignment");
354         append_subtree(branches);
355 }
356
357 void DumpTree::visit(ImageTypeDeclaration &type)
358 {
359         static const char *const dims[] = { "1D", "2D", "3D", "Cube" };
360
361         append(type, format("%s typedef %s", get_label(type), format_type(type.name)));
362
363         vector<Branch> branches;
364         branches.emplace_back(format("Dimensions: %s%s", dims[type.dimensions-1], (type.array ? " array" : "")));
365         if(type.base_type)
366                 branches.emplace_back(format("Element type: %s %s", get_label(*type.base_type), format_type(type.base_type->name)));
367         if(type.shadow)
368                 branches.emplace_back("Shadow");
369         append_subtree(branches);
370 }
371
372 void DumpTree::visit(StructDeclaration &strct)
373 {
374         append(strct, format("%s struct %s", get_label(strct), strct.name));
375         vector<Branch> branches;
376         if(strct.extended_alignment)
377                 branches.emplace_back("Extended alignment");
378         branches.emplace_back(&strct.members);
379         append_subtree(branches);
380 }
381
382 void DumpTree::visit(VariableDeclaration &var)
383 {
384         string decl = format("%s ", get_label(var));
385         if(var.constant)
386                 decl += "const ";
387         if(!var.interpolation.empty())
388                 decl += format("%s ", var.interpolation);
389         if(!var.sampling.empty())
390                 decl += format("%s ", var.sampling);
391         if(!var.interface.empty())
392                 decl += format("%s ", var.interface);
393         if(!var.precision.empty())
394                 decl += format("%s ", var.precision);
395         decl += format("%s %s", format_type(var.type), format_name(var.name));
396         if(var.source==BUILTIN_SOURCE)
397                 decl += " (builtin)";
398         else if(var.linked_declaration)
399                 decl += " (linked)";
400         append(var, decl);
401
402         vector<Branch> branches;
403         if(var.type_declaration)
404                 branches.emplace_back(format("Type: %s %s", get_label(*var.type_declaration), format_type(var.type_declaration->name)));
405         if(var.layout)
406                 branches.emplace_back(var.layout.get());
407         if(var.array)
408         {
409                 if(var.array_size)
410                         branches.emplace_back("Array []", var.array_size.get());
411                 else
412                         branches.emplace_back("Array []");
413         }
414         if(var.init_expression)
415                 branches.emplace_back(var.init_expression.get());
416         append_subtree(branches);
417 }
418
419 void DumpTree::visit(InterfaceBlock &iface)
420 {
421         string head = format("%s %s %s", get_label(iface), iface.interface, format_name(iface.block_name));
422         if(!iface.instance_name.empty())
423                 head += format(" %s", format_name(iface.instance_name));
424         if(iface.array)
425                 head += "[]";
426         if(iface.source==BUILTIN_SOURCE)
427                 head += " (builtin)";
428         else if(iface.linked_block)
429                 head += " (linked)";
430         append(iface, head);
431
432         vector<Branch> branches;
433         if(iface.type_declaration)
434                 branches.emplace_back(format("Type: %s %s", get_label(*iface.type_declaration), format_type(iface.type_declaration->name)));
435         if(iface.layout)
436                 branches.emplace_back(iface.layout.get());
437         if(iface.members)
438                 branches.emplace_back(iface.members.get());
439         append_subtree(branches);
440 }
441
442 void DumpTree::visit(FunctionDeclaration &func)
443 {
444         string text = format("%s %s %s%s", get_label(func), format_type(func.return_type), format_name(func.name), (func.signature.empty() ? "(?)" : func.signature));
445         if(func.source==BUILTIN_SOURCE)
446                 text += " (builtin)";
447         else if(!func.definition)
448                 text += " (undefined)";
449         append(func, text);
450
451         begin_sub();
452         if(func.return_type_declaration)
453                 append(format("Return type: %s %s", get_label(*func.return_type_declaration), format_type(func.return_type_declaration)));
454         for(const RefPtr<VariableDeclaration> &p: func.parameters)
455                 p->visit(*this);
456         last_branch();
457         if(func.definition==&func)
458                 func.body.visit(*this);
459         else if(func.definition)
460                 append(format("Definition: %s", get_label(*func.definition)));
461         end_sub();
462 }
463
464 void DumpTree::visit(Conditional &cond)
465 {
466         append(cond, "if()");
467
468         vector<Branch> branches;
469         branches.emplace_back(cond.condition.get());
470         branches.emplace_back("then", &cond.body);
471         if(!cond.else_body.body.empty())
472                 branches.emplace_back("else", &cond.else_body);
473         append_subtree(branches);
474 }
475
476 void DumpTree::visit(Iteration &iter)
477 {
478         append(iter, "for()");
479
480         vector<Branch> branches;
481         if(iter.init_statement)
482                 branches.emplace_back("Initialization", iter.init_statement.get());
483         if(iter.condition)
484                 branches.emplace_back("Condition", iter.condition.get());
485         if(iter.loop_expression)
486                 branches.emplace_back("Loop", iter.loop_expression.get());
487         branches.emplace_back(&iter.body);
488         append_subtree(branches);
489 }
490
491 void DumpTree::visit(Passthrough &pass)
492 {
493         append(pass, (pass.subscript ? "passthrough[]" : "passthrough"));
494         if(pass.subscript)
495                 append_subtree(*pass.subscript);
496 }
497
498 void DumpTree::visit(Return &ret)
499 {
500         append(ret, (ret.expression ? "return" : "return;"));
501         if(ret.expression)
502                 append_subtree(*ret.expression);
503 }
504
505 void DumpTree::visit(Jump &jump)
506 {
507         append(jump, format("%s;", jump.keyword));
508 }
509
510 } // namespace SL
511 } // namespace GL
512 } // namespace Msp