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