]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/debug.cpp
5ee30e0996c4dfc6ff5331d1c1b200f472c6fcf1
[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         append_subtree(branches);
353 }
354
355 void DumpTree::visit(ImageTypeDeclaration &type)
356 {
357         static const char *const dims[] = { "1D", "2D", "3D", "Cube" };
358
359         append(type, format("%s typedef %s", get_label(type), format_type(type.name)));
360
361         vector<Branch> branches;
362         branches.emplace_back(format("Dimensions: %s%s", dims[type.dimensions-1], (type.array ? " array" : "")));
363         if(type.base_type)
364                 branches.emplace_back(format("Element type: %s %s", get_label(*type.base_type), format_type(type.base_type->name)));
365         if(type.shadow)
366                 branches.emplace_back("Shadow");
367         append_subtree(branches);
368 }
369
370 void DumpTree::visit(StructDeclaration &strct)
371 {
372         append(strct, format("%s struct %s", get_label(strct), strct.name));
373         append_subtree(strct.members);
374 }
375
376 void DumpTree::visit(VariableDeclaration &var)
377 {
378         string decl = format("%s ", get_label(var));
379         if(var.constant)
380                 decl += "const ";
381         if(!var.interpolation.empty())
382                 decl += format("%s ", var.interpolation);
383         if(!var.sampling.empty())
384                 decl += format("%s ", var.sampling);
385         if(!var.interface.empty())
386                 decl += format("%s ", var.interface);
387         if(!var.precision.empty())
388                 decl += format("%s ", var.precision);
389         decl += format("%s %s", format_type(var.type), format_name(var.name));
390         if(var.source==BUILTIN_SOURCE)
391                 decl += " (builtin)";
392         else if(var.linked_declaration)
393                 decl += " (linked)";
394         append(var, decl);
395
396         vector<Branch> branches;
397         if(var.type_declaration)
398                 branches.emplace_back(format("Type: %s %s", get_label(*var.type_declaration), format_type(var.type_declaration->name)));
399         if(var.layout)
400                 branches.emplace_back(var.layout.get());
401         if(var.array)
402         {
403                 if(var.array_size)
404                         branches.emplace_back("Array []", var.array_size.get());
405                 else
406                         branches.emplace_back("Array []");
407         }
408         if(var.init_expression)
409                 branches.emplace_back(var.init_expression.get());
410         append_subtree(branches);
411 }
412
413 void DumpTree::visit(InterfaceBlock &iface)
414 {
415         string head = format("%s %s %s", get_label(iface), iface.interface, format_name(iface.block_name));
416         if(!iface.instance_name.empty())
417                 head += format(" %s", format_name(iface.instance_name));
418         if(iface.array)
419                 head += "[]";
420         if(iface.source==BUILTIN_SOURCE)
421                 head += " (builtin)";
422         else if(iface.linked_block)
423                 head += " (linked)";
424         append(iface, head);
425
426         vector<Branch> branches;
427         if(iface.type_declaration)
428                 branches.emplace_back(format("Type: %s %s", get_label(*iface.type_declaration), format_type(iface.type_declaration->name)));
429         if(iface.layout)
430                 branches.emplace_back(iface.layout.get());
431         if(iface.members)
432                 branches.emplace_back(iface.members.get());
433         append_subtree(branches);
434 }
435
436 void DumpTree::visit(FunctionDeclaration &func)
437 {
438         string text = format("%s %s %s%s", get_label(func), format_type(func.return_type), format_name(func.name), (func.signature.empty() ? "(?)" : func.signature));
439         if(func.source==BUILTIN_SOURCE)
440                 text += " (builtin)";
441         else if(!func.definition)
442                 text += " (undefined)";
443         append(func, text);
444
445         begin_sub();
446         if(func.return_type_declaration)
447                 append(format("Return type: %s %s", get_label(*func.return_type_declaration), format_type(func.return_type_declaration)));
448         for(const RefPtr<VariableDeclaration> &p: func.parameters)
449                 p->visit(*this);
450         last_branch();
451         if(func.definition==&func)
452                 func.body.visit(*this);
453         else if(func.definition)
454                 append(format("Definition: %s", get_label(*func.definition)));
455         end_sub();
456 }
457
458 void DumpTree::visit(Conditional &cond)
459 {
460         append(cond, "if()");
461
462         vector<Branch> branches;
463         branches.emplace_back(cond.condition.get());
464         branches.emplace_back("then", &cond.body);
465         if(!cond.else_body.body.empty())
466                 branches.emplace_back("else", &cond.else_body);
467         append_subtree(branches);
468 }
469
470 void DumpTree::visit(Iteration &iter)
471 {
472         append(iter, "for()");
473
474         vector<Branch> branches;
475         if(iter.init_statement)
476                 branches.emplace_back("Initialization", iter.init_statement.get());
477         if(iter.condition)
478                 branches.emplace_back("Condition", iter.condition.get());
479         if(iter.loop_expression)
480                 branches.emplace_back("Loop", iter.loop_expression.get());
481         branches.emplace_back(&iter.body);
482         append_subtree(branches);
483 }
484
485 void DumpTree::visit(Passthrough &pass)
486 {
487         append(pass, (pass.subscript ? "passthrough[]" : "passthrough"));
488         if(pass.subscript)
489                 append_subtree(*pass.subscript);
490 }
491
492 void DumpTree::visit(Return &ret)
493 {
494         append(ret, (ret.expression ? "return" : "return;"));
495         if(ret.expression)
496                 append_subtree(*ret.expression);
497 }
498
499 void DumpTree::visit(Jump &jump)
500 {
501         append(jump, format("%s;", jump.keyword));
502 }
503
504 } // namespace SL
505 } // namespace GL
506 } // namespace Msp