]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/debug.cpp
Clear load ID when assigning to a component
[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 -> %s", format_name(var.name), format_type(var.type));
187         append(var, text);
188 }
189
190 void DumpTree::visit(MemberAccess &memacc)
191 {
192         string text = "Member access:";
193         if(memacc.declaration)
194                 text += format(" %s", get_label(*memacc.declaration));
195         text += format(" .%s (%d) -> %s", format_name(memacc.member), memacc.index, format_type(memacc.type));
196         append(memacc, text);
197         append_subtree(*memacc.left);
198 }
199
200 void DumpTree::visit(Swizzle &swizzle)
201 {
202         static const char components[4] = { 'x', 'y', 'z', 'w' };
203         string text = "Swizzle: .";
204         for(unsigned i=0; i<swizzle.count; ++i)
205                 text += components[swizzle.components[i]];
206         text += format(" -> %s", format_type(swizzle.type));
207         append(swizzle, text);
208         append_subtree(*swizzle.left);
209 }
210
211 void DumpTree::visit(UnaryExpression &unary)
212 {
213         string text = format("Unary: %s, %sfix -> %s", unary.oper->token, (unary.oper->type==Operator::PREFIX ? "pre" : "post"), format_type(unary.type));
214         append(unary, text);
215         append_subtree(*unary.expression);
216 }
217
218 void DumpTree::visit(BinaryExpression &binary)
219 {
220         append(binary, format("Binary: %s%s -> %s", binary.oper->token, binary.oper->token2, format_type(binary.type)));
221         begin_sub();
222         binary.left->visit(*this);
223         last_branch();
224         binary.right->visit(*this);
225         end_sub();
226 }
227
228 void DumpTree::visit(Assignment &assign)
229 {
230         append(assign, format("Assignment: %s%s -> %s", assign.oper->token, (assign.self_referencing ? " (self-referencing)" : ""), format_type(assign.type)));
231         begin_sub();
232         if(assign.target.declaration)
233         {
234                 string text = format("Target: %s", get_label(*assign.target.declaration));
235
236                 static const char swizzle[4] = { 'x', 'y', 'z', 'w' };
237                 for(unsigned i=0; i<assign.target.chain_len; ++i)
238                 {
239                         unsigned component = assign.target.chain[i];
240                         switch(static_cast<Assignment::Target::ChainType>(component&0xC0))
241                         {
242                         case Assignment::Target::MEMBER:
243                                 text += format(" .%d", component&0x3F);
244                                 break;
245                         case Assignment::Target::SWIZZLE:
246                                 text += " .";
247                                 for(unsigned j=0; j<4; ++j)
248                                         if(component&(1<<j))
249                                                 text += swizzle[j];
250                                 break;
251                         case Assignment::Target::ARRAY:
252                                 text += format(" [%d]", component&0x3F);
253                                 break;
254                         }
255                 }
256                 append(text);
257         }
258         assign.left->visit(*this);
259         last_branch();
260         assign.right->visit(*this);
261         end_sub();
262 }
263
264 void DumpTree::visit(TernaryExpression &ternary)
265 {
266         append(ternary, format("Ternary: %s%s -> %s", ternary.oper->token, ternary.oper->token2, format_type(ternary.type)));
267         begin_sub();
268         ternary.condition->visit(*this);
269         ternary.true_expr->visit(*this);
270         last_branch();
271         ternary.false_expr->visit(*this);
272         end_sub();
273 }
274
275 void DumpTree::visit(FunctionCall &call)
276 {
277         string head = "Function call: ";
278         if(call.declaration)
279                 head += format("%s ", get_label(*call.declaration));
280         head += format_name(call.name);
281         if(call.constructor)
282                 head += " (constructor)";
283         head += format(" -> %s", format_type(call.type));
284         append(call, head);
285
286         begin_sub();
287         for(auto i=call.arguments.cbegin(); i!=call.arguments.cend(); )
288         {
289                 auto j = increment(i, call.arguments);
290                 (*j)->visit(*this);
291         }
292         end_sub();
293 }
294
295 void DumpTree::visit(ExpressionStatement &expr)
296 {
297         append(expr, "expr;");
298         append_subtree(*expr.expression);
299 }
300
301 void DumpTree::visit(Import &import)
302 {
303         append(import, format("import %s", import.module));
304 }
305
306 void DumpTree::visit(Precision &prec)
307 {
308         append(prec, format("precision %s %s", prec.precision, prec.type));
309 }
310
311 void DumpTree::visit(Layout &layout)
312 {
313         append(layout, "Layout");
314         begin_sub();
315         for(auto i=layout.qualifiers.cbegin(); i!=layout.qualifiers.cend(); )
316         {
317                 auto j = increment(i, layout.qualifiers);
318                 string qualifier = j->name;
319                 if(j->has_value)
320                         qualifier += format("=%d", j->value);
321                 append(qualifier);
322         }
323         end_sub();
324 }
325
326 void DumpTree::visit(InterfaceLayout &layout)
327 {
328         append(layout, format("Layout: %s", layout.interface));
329         append_subtree(layout.layout);
330 }
331
332 void DumpTree::visit(BasicTypeDeclaration &type)
333 {
334         append(type, format("%s typedef %s", get_label(type), format_type(type.name)));
335
336         vector<Branch> branches;
337         if(type.base_type)
338                 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)));
339         if(type.kind==BasicTypeDeclaration::VECTOR)
340                 branches.emplace_back(format("Vector: %d", type.size));
341         else if(type.kind==BasicTypeDeclaration::MATRIX)
342                 branches.emplace_back(format("Matrix: %dx%d", type.size&0xFFFF, type.size>>16));
343         if(type.extended_alignment)
344                 branches.emplace_back("Extended alignment");
345         append_subtree(branches);
346 }
347
348 void DumpTree::visit(ImageTypeDeclaration &type)
349 {
350         static const char *const dims[] = { "1D", "2D", "3D", "Cube" };
351
352         append(type, format("%s typedef %s", get_label(type), format_type(type.name)));
353
354         vector<Branch> branches;
355         branches.emplace_back(format("Dimensions: %s%s", dims[type.dimensions-1], (type.array ? " array" : "")));
356         if(type.base_type)
357                 branches.emplace_back(format("Element type: %s %s", get_label(*type.base_type), format_type(type.base_type->name)));
358         if(!type.format.empty())
359                 branches.emplace_back(format("Texel format: %s", type.format));
360         if(type.sampled)
361                 branches.emplace_back("Sampled");
362         if(type.shadow)
363                 branches.emplace_back("Shadow");
364         if(type.multisample)
365                 branches.emplace_back("Multisample");
366         append_subtree(branches);
367 }
368
369 void DumpTree::visit(StructDeclaration &strct)
370 {
371         append(strct, format("%s struct %s", get_label(strct), strct.name));
372         vector<Branch> branches;
373         if(!strct.block_name.empty())
374                 branches.emplace_back(format("Interface block: %s", format_name(strct.block_name)));
375         if(strct.extended_alignment)
376                 branches.emplace_back("Extended alignment");
377         branches.emplace_back(&strct.members);
378         append_subtree(branches);
379 }
380
381 void DumpTree::visit(VariableDeclaration &var)
382 {
383         string decl = format("%s ", get_label(var));
384         if(var.constant)
385                 decl += "const ";
386         if(!var.interpolation.empty())
387                 decl += format("%s ", var.interpolation);
388         if(!var.sampling.empty())
389                 decl += format("%s ", var.sampling);
390         if(!var.interface.empty())
391                 decl += format("%s ", var.interface);
392         if(!var.precision.empty())
393                 decl += format("%s ", var.precision);
394         decl += format("%s %s", format_type(var.type), format_name(var.name));
395         if(var.source==BUILTIN_SOURCE)
396                 decl += " (builtin)";
397         else if(var.linked_declaration)
398                 decl += " (linked)";
399         append(var, decl);
400
401         vector<Branch> branches;
402         if(var.type_declaration)
403                 branches.emplace_back(format("Type: %s %s", get_label(*var.type_declaration), format_type(var.type_declaration->name)));
404         if(var.block_declaration)
405                 branches.emplace_back(format("Interface block: %s", format_name(var.block_declaration->block_name)));
406         if(var.layout)
407                 branches.emplace_back(var.layout.get());
408         if(var.array)
409         {
410                 if(var.array_size)
411                         branches.emplace_back("Array []", var.array_size.get());
412                 else
413                         branches.emplace_back("Array []");
414         }
415         if(var.init_expression)
416                 branches.emplace_back(var.init_expression.get());
417         append_subtree(branches);
418 }
419
420 void DumpTree::visit(FunctionDeclaration &func)
421 {
422         string text = format("%s %s %s%s", get_label(func), format_type(func.return_type), format_name(func.name), (func.signature.empty() ? "(?)" : func.signature));
423         if(func.source==BUILTIN_SOURCE)
424                 text += " (builtin)";
425         else if(!func.definition)
426                 text += " (undefined)";
427         append(func, text);
428
429         begin_sub();
430         if(func.return_type_declaration)
431                 append(format("Return type: %s %s", get_label(*func.return_type_declaration), format_type(func.return_type_declaration)));
432         for(const RefPtr<VariableDeclaration> &p: func.parameters)
433                 p->visit(*this);
434         last_branch();
435         if(func.definition==&func)
436                 func.body.visit(*this);
437         else if(func.definition)
438                 append(format("Definition: %s", get_label(*func.definition)));
439         end_sub();
440 }
441
442 void DumpTree::visit(Conditional &cond)
443 {
444         append(cond, "if()");
445
446         vector<Branch> branches;
447         branches.emplace_back(cond.condition.get());
448         branches.emplace_back("then", &cond.body);
449         if(!cond.else_body.body.empty())
450                 branches.emplace_back("else", &cond.else_body);
451         append_subtree(branches);
452 }
453
454 void DumpTree::visit(Iteration &iter)
455 {
456         append(iter, "for()");
457
458         vector<Branch> branches;
459         if(iter.init_statement)
460                 branches.emplace_back("Initialization", iter.init_statement.get());
461         if(iter.condition)
462                 branches.emplace_back("Condition", iter.condition.get());
463         if(iter.loop_expression)
464                 branches.emplace_back("Loop", iter.loop_expression.get());
465         branches.emplace_back(&iter.body);
466         append_subtree(branches);
467 }
468
469 void DumpTree::visit(Passthrough &pass)
470 {
471         append(pass, (pass.subscript ? "passthrough[]" : "passthrough"));
472         if(pass.subscript)
473                 append_subtree(*pass.subscript);
474 }
475
476 void DumpTree::visit(Return &ret)
477 {
478         append(ret, (ret.expression ? "return" : "return;"));
479         if(ret.expression)
480                 append_subtree(*ret.expression);
481 }
482
483 void DumpTree::visit(Jump &jump)
484 {
485         append(jump, format("%s;", jump.keyword));
486 }
487
488 } // namespace SL
489 } // namespace GL
490 } // namespace Msp