]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/generate.cpp
Use forward references for entry point interfaces in SPIR-V
[libs/gl.git] / source / glsl / generate.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/hash.h>
3 #include <msp/core/raii.h>
4 #include "generate.h"
5 #include "reflect.h"
6
7 using namespace std;
8
9 namespace Msp {
10 namespace GL {
11 namespace SL {
12
13 void ConstantIdAssigner::apply(Module &module, const Features &features)
14 {
15         for(Stage &s: module.stages)
16                 s.content.visit(*this);
17
18         for(VariableDeclaration *v: auto_constants)
19         {
20                 unsigned id;
21                 auto j = existing_constants.find(v->name);
22                 if(j!=existing_constants.end())
23                         id = j->second;
24                 else
25                 {
26                         id = hash<32>(v->name)%features.constant_id_range;
27                         while(used_ids.count(id))
28                                 id = (id+1)%features.constant_id_range;
29                 }
30
31                 auto i = find_member(v->layout->qualifiers, string("constant_id"), &Layout::Qualifier::name);
32                 if(i!=v->layout->qualifiers.end())
33                         i->value = id;
34
35                 used_ids.insert(id);
36                 existing_constants[v->name] = id;
37         }
38 }
39
40 void ConstantIdAssigner::visit(VariableDeclaration &var)
41 {
42         if(var.layout)
43         {
44                 auto i = find_member(var.layout->qualifiers, string("constant_id"), &Layout::Qualifier::name);
45                 if(i!=var.layout->qualifiers.end() && i->has_value)
46                 {
47                         if(i->value==-1)
48                                 auto_constants.push_back(&var);
49                         else
50                         {
51                                 existing_constants[var.name] = i->value;
52                                 used_ids.insert(i->value);
53                         }
54                 }
55         }
56 }
57
58
59 string InterfaceGenerator::get_out_prefix(Stage::Type type)
60 {
61         if(type==Stage::VERTEX)
62                 return "_vs_out_";
63         else if(type==Stage::GEOMETRY)
64                 return "_gs_out_";
65         else
66                 return string();
67 }
68
69 void InterfaceGenerator::apply(Stage &s)
70 {
71         stage = &s;
72         iface_target_block = &stage->content;
73         if(stage->previous)
74                 in_prefix = get_out_prefix(stage->previous->type);
75         out_prefix = get_out_prefix(stage->type);
76         s.content.visit(*this);
77         NodeRemover().apply(s, nodes_to_remove);
78 }
79
80 void InterfaceGenerator::visit(Block &block)
81 {
82         SetForScope<Block *> set_block(current_block, &block);
83         for(auto i=block.body.begin(); i!=block.body.end(); ++i)
84         {
85                 assignment_insert_point = i;
86                 if(&block==&stage->content)
87                         iface_insert_point = i;
88
89                 (*i)->visit(*this);
90         }
91 }
92
93 string InterfaceGenerator::change_prefix(const string &name, const string &prefix) const
94 {
95         unsigned offset = (name.compare(0, in_prefix.size(), in_prefix) ? 0 : in_prefix.size());
96         return prefix+name.substr(offset);
97 }
98
99 VariableDeclaration *InterfaceGenerator::generate_interface(VariableDeclaration &var, const string &iface, const string &name)
100 {
101         if(stage->content.variables.count(name))
102                 return 0;
103
104         if(stage->type==Stage::GEOMETRY && var.interface=="out" && var.array)
105                 return 0;
106
107         VariableDeclaration* iface_var = new VariableDeclaration;
108         iface_var->sampling = var.sampling;
109         if(stage->type==Stage::FRAGMENT && iface=="in")
110                 if(BasicTypeDeclaration *basic = dynamic_cast<BasicTypeDeclaration *>(var.type_declaration))
111                         if(BasicTypeDeclaration *elem = get_element_type(*basic))
112                                 if(elem->kind==BasicTypeDeclaration::INT)
113                                         iface_var->interpolation = "flat";
114         iface_var->interface = iface;
115         iface_var->type = var.type;
116         iface_var->name = name;
117         // Tessellation and geometry inputs may be arrayed.
118         if(stage->type==Stage::TESS_CONTROL)
119                 // VS out -> TCS in: add | TCS in -> TCS out: unchanged | VS out -> TCS out: add
120                 iface_var->array = (var.array || var.interface!="in");
121         else if(stage->type==Stage::TESS_EVAL)
122                 // TCS out -> TES in: unchanged | TES in -> TES out: remove | TCS out -> TES out: remove
123                 iface_var->array = (var.array && iface=="in");
124         else if(stage->type==Stage::GEOMETRY)
125                 // VS/TES out -> GS in: add | GS in -> GS out: remove | VS/TES out -> GS out: unchanged
126                 iface_var->array = ((var.array && var.interface!="in") || iface=="in");
127         else
128                 iface_var->array = var.array;
129         if(iface_var->array)
130                 iface_var->array_size = var.array_size;
131         if(iface=="in")
132         {
133                 iface_var->layout = var.layout;
134                 iface_var->linked_declaration = &var;
135                 var.linked_declaration = iface_var;
136         }
137
138         if(var.block_declaration)
139         {
140                 StructDeclaration *iface_type = var.block_declaration->clone();
141                 iface_type->name = format("_%s_%s", iface, var.block_declaration->block_name);
142                 iface_target_block->body.insert(iface_insert_point, iface_type);
143
144                 iface_var->type = iface_type->name;
145                 if(name.empty())
146                         iface_var->name = format("%s %s", iface, var.block_declaration->block_name);
147
148                 stage->interface_blocks.insert(make_pair("in "+var.block_declaration->block_name, iface_var));
149                 if(!name.empty())
150                         stage->interface_blocks.insert(make_pair(name, iface_var));
151         }
152
153         iface_target_block->body.insert(iface_insert_point, iface_var);
154         iface_target_block->variables.insert(make_pair(name, iface_var));
155         if(iface_target_block==&stage->content && iface=="in")
156                 declared_inputs.push_back(iface_var);
157
158         return iface_var;
159 }
160
161 ExpressionStatement &InterfaceGenerator::insert_assignment(const string &left, Expression *right)
162 {
163         Assignment *assign = new Assignment;
164
165         string::size_type dot = left.find('.');
166         VariableReference *ref = new VariableReference;
167         ref->name = left.substr(0, dot);
168         assign->left = ref;
169
170         while(dot!=string::npos)
171         {
172                 string::size_type start = dot+1;
173                 dot = left.find('.', start);
174
175                 MemberAccess *memacc = new MemberAccess;
176                 memacc->left = assign->left;
177                 memacc->member = left.substr(start, dot-start);
178                 assign->left = memacc;
179         }
180
181         assign->oper = &Operator::get_operator("=", Operator::BINARY);
182         assign->right = right;
183
184         ExpressionStatement *stmt = new ExpressionStatement;
185         stmt->expression = assign;
186         current_block->body.insert(assignment_insert_point, stmt);
187         stmt->visit(*this);
188
189         return *stmt;
190 }
191
192 void InterfaceGenerator::visit(VariableReference &var)
193 {
194         if(var.declaration || !stage->previous)
195                 return;
196         /* Don't pull a variable from previous stage if we just generated an output
197         interface in this stage */
198         if(stage->content.variables.count(var.name))
199                 return;
200
201         const map<string, VariableDeclaration *> &prev_vars = stage->previous->content.variables;
202         auto i = prev_vars.find(var.name);
203         if(i==prev_vars.end() || i->second->interface!="out")
204                 i = prev_vars.find(in_prefix+var.name);
205         if(i!=prev_vars.end() && i->second->interface=="out")
206         {
207                 if(stage->type==Stage::GEOMETRY && i->second->array)
208                         stage->diagnostics.push_back(Diagnostic(Diagnostic::WARN, var.source, var.line,
209                                 format("Can't access '%s' through automatic interface because it's an array", var.name)));
210                 else
211                 {
212                         generate_interface(*i->second, "in", i->second->name);
213                         var.name = i->second->name;
214                 }
215                 return;
216         }
217
218         for(const auto &kvp: stage->previous->interface_blocks)
219                 if(kvp.second->name.find(' ')!=string::npos)
220                 {
221                         const map<string, VariableDeclaration *> &iface_vars = kvp.second->block_declaration->members.variables;
222                         i = iface_vars.find(var.name);
223                         if(i!=iface_vars.end())
224                         {
225                                 generate_interface(*kvp.second, "in", string());
226                                 return;
227                         }
228                 }
229 }
230
231 void InterfaceGenerator::visit(VariableDeclaration &var)
232 {
233         if(var.interface=="out")
234         {
235                 /* For output variables in function scope, generate a global interface
236                 and replace the local declaration with an assignment. */
237                 VariableDeclaration *out_var = 0;
238                 if(function_scope && (out_var=generate_interface(var, "out", var.name)))
239                 {
240                         out_var->source = var.source;
241                         out_var->line = var.line;
242                         nodes_to_remove.insert(&var);
243                         if(var.init_expression)
244                         {
245                                 ExpressionStatement &stmt = insert_assignment(var.name, var.init_expression->clone());
246                                 stmt.source = var.source;
247                                 stmt.line = var.line;
248                                 return;
249                         }
250                 }
251         }
252         else if(var.interface=="in" && current_block==&stage->content)
253         {
254                 if(var.name.compare(0, 3, "gl_"))
255                         declared_inputs.push_back(&var);
256
257                 /* Try to link input variables in global scope with output variables from
258                 previous stage. */
259                 if(!var.linked_declaration && stage->previous)
260                 {
261                         const map<string, VariableDeclaration *> *prev_vars;
262                         string name;
263                         // Blocks are linked by their block name, not instance name
264                         if(var.block_declaration)
265                         {
266                                 prev_vars = &stage->previous->interface_blocks;
267                                 name = "out "+var.block_declaration->block_name;
268                         }
269                         else
270                         {
271                                 prev_vars = &stage->previous->content.variables;
272                                 name = var.name;
273                         }
274
275                         auto i = prev_vars->find(name);
276                         if(i!=prev_vars->end() && i->second->interface=="out")
277                         {
278                                 var.linked_declaration = i->second;
279                                 i->second->linked_declaration = &var;
280                         }
281                 }
282         }
283
284         TraversingVisitor::visit(var);
285 }
286
287 void InterfaceGenerator::visit(FunctionDeclaration &func)
288 {
289         SetFlag set_scope(function_scope, true);
290         // Skip parameters because they're not useful here
291         func.body.visit(*this);
292 }
293
294 void InterfaceGenerator::visit(Passthrough &pass)
295 {
296         // Pass through all input variables declared so far.
297         vector<VariableDeclaration *> pass_vars = declared_inputs;
298
299         if(stage->previous)
300         {
301                 for(const auto &kvp: stage->previous->content.variables)
302                 {
303                         if(kvp.second->interface!="out")
304                                 continue;
305
306                         /* Pass through output variables from the previous stage, but only
307                         those which are not already linked to an input here. */
308                         if(!kvp.second->linked_declaration && generate_interface(*kvp.second, "in", kvp.second->name))
309                                 pass_vars.push_back(kvp.second);
310                 }
311         }
312
313         if(stage->type==Stage::GEOMETRY)
314         {
315                 /* Special case for geometry shader: copy gl_Position from input to
316                 output. */
317                 VariableReference *ref = new VariableReference;
318                 ref->name = "gl_in";
319
320                 BinaryExpression *subscript = new BinaryExpression;
321                 subscript->left = ref;
322                 subscript->oper = &Operator::get_operator("[", Operator::BINARY);
323                 subscript->right = pass.subscript;
324
325                 MemberAccess *memacc = new MemberAccess;
326                 memacc->left = subscript;
327                 memacc->member = "gl_Position";
328
329                 insert_assignment("out gl_PerVertex.gl_Position", memacc);
330         }
331
332         for(VariableDeclaration *v: pass_vars)
333         {
334                 string out_name = change_prefix(v->name, out_prefix);
335                 generate_interface(*v, "out", out_name);
336
337                 VariableReference *ref = new VariableReference;
338                 ref->name = v->name;
339                 if(pass.subscript)
340                 {
341                         BinaryExpression *subscript = new BinaryExpression;
342                         subscript->left = ref;
343                         subscript->oper = &Operator::get_operator("[", Operator::BINARY);
344                         subscript->right = pass.subscript;
345                         insert_assignment(out_name, subscript);
346                 }
347                 else
348                         insert_assignment(out_name, ref);
349         }
350
351         nodes_to_remove.insert(&pass);
352 }
353
354
355 void LayoutDefaulter::apply(Stage &stage)
356 {
357         if(stage.type==Stage::TESS_EVAL)
358         {
359                 stage.content.visit(*this);
360                 if((need_winding || need_spacing) && in_iface)
361                 {
362                         if(need_winding)
363                                 in_iface->layout.qualifiers.emplace_back("ccw");
364                         if(need_spacing)
365                                 in_iface->layout.qualifiers.emplace_back("equal_spacing");
366                 }
367         }
368 }
369
370 void LayoutDefaulter::visit(InterfaceLayout &iface)
371 {
372         if(iface.interface=="in")
373         {
374                 if(!in_iface)
375                         in_iface = &iface;
376                 for(const Layout::Qualifier &q: iface.layout.qualifiers)
377                 {
378                         if(q.name=="cw" || q.name=="ccw")
379                                 need_winding = false;
380                         else if(q.name=="equal_spacing" || q.name=="fractional_even_spacing" || q.name=="fractional_odd_spacing")
381                                 need_spacing = false;
382                 }
383         }
384 }
385
386
387 void ArraySizer::apply(Stage &stage)
388 {
389         stage.content.visit(*this);
390         for(const auto &kvp: max_indices)
391                 if(kvp.first->array && !kvp.first->array_size)
392                 {
393                         int size = 0;
394                         if(stage.type==Stage::GEOMETRY && kvp.first->interface=="in")
395                                 size = input_size;
396                         else if(kvp.second>=0)
397                                 size = kvp.second+1;
398                         if(!size && !kvp.first->name.compare(0, 3, "gl_"))
399                                 size = 1;
400
401                         if(size>0)
402                         {
403                                 Literal *literal_size = new Literal;
404                                 literal_size->token = lexical_cast<string>(size);
405                                 literal_size->value = size;
406                                 kvp.first->array_size = literal_size;
407                         }
408                 }
409 }
410
411 void ArraySizer::visit(VariableReference &var)
412 {
413         r_declaration = var.declaration;
414 }
415
416 void ArraySizer::visit(MemberAccess &memacc)
417 {
418         r_declaration = 0;
419         TraversingVisitor::visit(memacc);
420         VariableDeclaration *member_declaration = 0;
421         if(r_declaration)
422                 if(StructDeclaration *strct = dynamic_cast<StructDeclaration *>(r_declaration->type_declaration))
423                 {
424                         auto i = strct->members.variables.find(memacc.member);
425                         if(i!=strct->members.variables.end())
426                                 member_declaration = i->second;
427                 }
428         r_declaration = member_declaration;
429 }
430
431 void ArraySizer::visit(Swizzle &swizzle)
432 {
433         TraversingVisitor::visit(swizzle);
434         r_declaration = 0;
435 }
436
437 void ArraySizer::visit(UnaryExpression &unary)
438 {
439         TraversingVisitor::visit(unary);
440         r_declaration = 0;
441 }
442
443 void ArraySizer::visit(BinaryExpression &binary)
444 {
445         if(binary.oper->token[0]=='[')
446                 if(const Literal *literal_index = dynamic_cast<const Literal *>(binary.right.get()))
447                         if(literal_index->value.check_type<int>())
448                         {
449                                 r_declaration = 0;
450                                 binary.left->visit(*this);
451                                 if(r_declaration)
452                                 {
453                                         max_indices[r_declaration] = literal_index->value.value<int>();
454                                         return;
455                                 }
456                         }
457
458         TraversingVisitor::visit(binary);
459 }
460
461 void ArraySizer::visit(TernaryExpression &ternary)
462 {
463         TraversingVisitor::visit(ternary);
464         r_declaration = 0;
465 }
466
467 void ArraySizer::visit(FunctionCall &call)
468 {
469         TraversingVisitor::visit(call);
470         r_declaration = 0;
471 }
472
473 void ArraySizer::visit(InterfaceLayout &layout)
474 {
475         if(layout.interface=="in")
476         {
477                 for(const Layout::Qualifier &q: layout.layout.qualifiers)
478                 {
479                         if(q.name=="points")
480                                 input_size = 1;
481                         else if(q.name=="lines")
482                                 input_size = 2;
483                         else if(q.name=="triangles")
484                                 input_size = 3;
485                         else if(q.name=="lines_adjacency")
486                                 input_size = 4;
487                         else if(q.name=="triangles_adjacency")
488                                 input_size = 6;
489                 }
490         }
491 }
492
493 void ArraySizer::visit(VariableDeclaration &var)
494 {
495         if(var.array && !var.array_size)
496                 max_indices[&var] = 0;
497 }
498
499 } // namespace SL
500 } // namespace GL
501 } // namespace Msp