]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/finalize.cpp
Propagate locations from existing variables before optimization
[libs/gl.git] / source / glsl / finalize.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/hash.h>
3 #include <msp/core/raii.h>
4 #include <msp/strings/lexicalcast.h>
5 #include "finalize.h"
6 #include "glsl_error.h"
7 #include "reflect.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace GL {
13 namespace SL {
14
15 void StructOrganizer::visit(StructDeclaration &strct)
16 {
17         SetForScope<int> set_offset(offset, 0);
18         TraversingVisitor::visit(strct);
19 }
20
21 void StructOrganizer::visit(VariableDeclaration &var)
22 {
23         if(offset>=0)
24         {
25                 int *layout_offset = 0;
26                 bool has_matrix_order = false;
27                 if(var.layout)
28                 {
29                         for(Layout::Qualifier &q: var.layout->qualifiers)
30                         {
31                                 if(q.name=="offset" && q.has_value)
32                                 {
33                                         layout_offset = &q.value;
34                                         if(q.value>=offset)
35                                                 offset = q.value;
36                                 }
37                                 else if(q.name=="column_major" || q.name=="row_major")
38                                         has_matrix_order = true;
39                         }
40                 }
41
42                 MemoryRequirementsCalculator::Result mem_reqs = MemoryRequirementsCalculator().apply(var);
43                 offset += mem_reqs.alignment-1;
44                 offset -= offset%mem_reqs.alignment;
45
46                 if(layout_offset)
47                         *layout_offset = offset;
48                 else
49                         add_layout_qualifier(var.layout, Layout::Qualifier("offset", offset));
50
51                 if(!has_matrix_order)
52                 {
53                         const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(var.type_declaration);
54                         while(basic && basic->kind==BasicTypeDeclaration::ARRAY)
55                                 basic = dynamic_cast<const BasicTypeDeclaration *>(basic->base_type);
56                         if(basic && basic->kind==BasicTypeDeclaration::MATRIX)
57                                 add_layout_qualifier(var.layout, Layout::Qualifier("column_major"));
58                 }
59
60                 offset += mem_reqs.size;
61         }
62 }
63
64
65 void LocationAllocator::apply(Module &module, const Features &features, bool a)
66 {
67         alloc_new = a;
68         for(Stage &s: module.stages)
69                 apply(s);
70
71         if(features.target_api!=VULKAN)
72                 allocate_locations("uniform");
73
74         for(InterfaceBlock *b: unbound_blocks)
75                 bind_uniform(b->layout, b->block_name, features.uniform_binding_range);
76         for(VariableDeclaration *t: unbound_textures)
77                 bind_uniform(t->layout, t->name, features.texture_binding_range);
78 }
79
80 void LocationAllocator::apply(Stage &stage)
81 {
82         swap(used_locations["in"], used_locations["out"]);
83         used_locations["out"].clear();
84
85         stage.content.visit(*this);
86
87         allocate_locations("in");
88         allocate_locations("out");
89 }
90
91 void LocationAllocator::allocate_locations(const string &iface)
92 {
93         auto write = unplaced_variables.begin();
94         unsigned next = 0;
95         for(auto i=unplaced_variables.begin(); i!=unplaced_variables.end(); ++i)
96         {
97                 if((*i)->interface!=iface)
98                 {
99                         if(write!=i)
100                                 *write = *i;
101                         ++write;
102                         continue;
103                 }
104
105                 if((*i)->interface=="uniform")
106                 {
107                         auto j = uniforms.find((*i)->name);
108                         if(j!=uniforms.end() && j->second.location>=0)
109                         {
110                                 add_layout_qualifier((*i)->layout, Layout::Qualifier("location", j->second.location));
111                                 continue;
112                         }
113                 }
114
115                 if(!alloc_new)
116                         continue;
117
118                 set<unsigned> &used = used_locations[(*i)->interface];
119
120                 unsigned size = LocationCounter().apply(**i);
121                 while(1)
122                 {
123                         int blocking = -1;
124                         for(unsigned j=0; j<size; ++j)
125                                 if(used.count(next+j))
126                                         blocking = next+j;
127                         if(blocking<0)
128                                 break;
129                         next = blocking+1;
130                 }
131
132                 add_layout_qualifier((*i)->layout, Layout::Qualifier("location", next));
133                 if((*i)->interface=="uniform")
134                         uniforms[(*i)->name].location = next;
135
136                 for(unsigned j=0; j<size; ++j)
137                         used.insert(next+j);
138                 next += size;
139         }
140
141         unplaced_variables.erase(write, unplaced_variables.end());
142 }
143
144 void LocationAllocator::bind_uniform(RefPtr<Layout> &layout, const string &name, unsigned range)
145 {
146         auto i = uniforms.find(name);
147         if(i!=uniforms.end() && i->second.bind_point>=0)
148                 add_layout_qualifier(layout, Layout::Qualifier("binding", i->second.bind_point));
149         else if(alloc_new)
150         {
151                 set<unsigned> &used = used_bindings[0];
152
153                 unsigned bind_point = fold32(hash64(name))%range;
154                 while(used.count(bind_point))
155                         bind_point = (bind_point+1)%range;
156
157                 add_layout_qualifier(layout, Layout::Qualifier("binding", bind_point));
158                 uniforms[name].bind_point = bind_point;
159                 used.insert(bind_point);
160         }
161 }
162
163 bool LocationAllocator::visit_uniform(const string &name, RefPtr<Layout> &layout)
164 {
165         int bind_point = get_layout_value(layout.get(), "binding");
166         if(bind_point>=0)
167         {
168                 used_bindings[0].insert(bind_point);
169                 uniforms[name].bind_point = bind_point;
170         }
171         return bind_point>=0;
172 }
173
174 void LocationAllocator::visit(VariableDeclaration &var)
175 {
176         if(!var.name.compare(0, 3, "gl_"))
177                 return;
178
179         if(!var.interface.empty())
180         {
181                 int location = get_layout_value(var.layout.get(), "location");
182
183                 if(location<0 && var.linked_declaration && var.linked_declaration->layout)
184                 {
185                         location = get_layout_value(var.linked_declaration->layout.get(), "location");
186                         if(location>=0)
187                                 add_layout_qualifier(var.layout, Layout::Qualifier("location", location));
188                 }
189
190                 if(location>=0)
191                 {
192                         unsigned size = LocationCounter().apply(var);
193                         for(unsigned i=0; i<size; ++i)
194                                 used_locations[var.interface].insert(location+i);
195                         if(var.interface=="uniform")
196                                 uniforms[var.name].location = location;
197                 }
198                 else
199                         unplaced_variables.push_back(&var);
200         }
201
202         if(var.interface=="uniform")
203         {
204                 const TypeDeclaration *base_type = get_ultimate_base_type(var.type_declaration);
205                 if(dynamic_cast<const ImageTypeDeclaration *>(base_type) && !visit_uniform(var.name, var.layout))
206                         unbound_textures.push_back(&var);
207         }
208 }
209
210 void LocationAllocator::visit(InterfaceBlock &iface)
211 {
212         if(!iface.instance_name.compare(0, 3, "gl_"))
213                 return;
214
215         if(iface.interface=="uniform")
216         {
217                 bool push_constant = has_layout_qualifier(iface.layout.get(), "push_constant");
218                 if(!push_constant && !visit_uniform(iface.block_name, iface.layout))
219                         unbound_blocks.push_back(&iface);
220         }
221 }
222
223
224 void PrecisionConverter::apply(Stage &s)
225 {
226         stage = &s;
227         s.content.visit(*this);
228         NodeRemover().apply(s, nodes_to_remove);
229 }
230
231 void PrecisionConverter::visit(Block &block)
232 {
233         for(auto i=block.body.begin(); i!=block.body.end(); ++i)
234         {
235                 if(&block==&stage->content)
236                         insert_point = i;
237                 (*i)->visit(*this);
238         }
239 }
240
241 void PrecisionConverter::visit(Precision &prec)
242 {
243         if(stage->required_features.target_api==OPENGL_ES)
244                 have_default.insert(prec.type);
245         else
246                 nodes_to_remove.insert(&prec);
247 }
248
249 void PrecisionConverter::visit(VariableDeclaration &var)
250 {
251         if(stage->required_features.target_api!=OPENGL_ES)
252         {
253                 var.precision.clear();
254                 return;
255         }
256
257         const char *default_prec = (stage->type==Stage::FRAGMENT ? "mediump" : "highp");
258         const TypeDeclaration *type = var.type_declaration;
259         while(type)
260         {
261                 if(dynamic_cast<const ImageTypeDeclaration *>(type))
262                 {
263                         default_prec = "lowp";
264                         break;
265                 }
266                 else if(const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(type))
267                 {
268                         if(basic->kind==BasicTypeDeclaration::INT || basic->kind==BasicTypeDeclaration::FLOAT)
269                                 break;
270                         type = basic->base_type;
271                 }
272                 else
273                         return;
274         }
275         if(!type)
276                 return;
277
278         if(!have_default.count(type->name))
279         {
280                 Precision *prec = new Precision;
281                 prec->precision = default_prec;
282                 prec->type = type->name;
283                 stage->content.body.insert(insert_point, prec);
284
285                 have_default.insert(type->name);
286         }
287 }
288
289
290 void FeatureConverter::apply(Stage &s, const Features &feat)
291 {
292         stage = &s;
293         features = feat;
294
295         if(!stage->required_features.glsl_version)
296                 stage->required_features.glsl_version = Version(1, (stage->required_features.target_api==OPENGL_ES ? 0 : 10));
297
298         apply();
299 }
300
301 void FeatureConverter::unsupported(const string &reason)
302 {
303         Diagnostic diagnostic;
304         diagnostic.severity = Diagnostic::ERR;
305         diagnostic.source = GENERATED_SOURCE;
306         diagnostic.line = 0;
307         diagnostic.message = reason;
308         stage->diagnostics.push_back(diagnostic);
309 }
310
311 bool FeatureConverter::check_version(const Version &feature_version) const
312 {
313         if(features.glsl_version<feature_version)
314                 return false;
315         else if(stage->required_features.glsl_version<feature_version)
316                 stage->required_features.glsl_version = feature_version;
317
318         return true;
319 }
320
321 bool FeatureConverter::check_extension(bool Features::*extension) const
322 {
323         if(!(features.*extension))
324                 return false;
325
326         stage->required_features.*extension = true;
327
328         return true;
329 }
330
331
332 void StructuralFeatureConverter::apply()
333 {
334         if(supports_stage(stage->type))
335         {
336                 stage->content.visit(*this);
337                 NodeRemover().apply(*stage, nodes_to_remove);
338         }
339         else
340                 unsupported(format("Stage %s is not supported", Stage::get_stage_name(stage->type)));
341 }
342
343 void StructuralFeatureConverter::visit(Block &block)
344 {
345         for(auto i=block.body.begin(); i!=block.body.end(); ++i)
346         {
347                 if(&block==&stage->content)
348                         uniform_insert_point = i;
349                 (*i)->visit(*this);
350         }
351 }
352
353 void StructuralFeatureConverter::visit(RefPtr<Expression> &expr)
354 {
355         r_replaced_reference = 0;
356         expr->visit(*this);
357         if(r_replaced_reference)
358                 expr = r_replaced_reference;
359         r_replaced_reference = 0;
360 }
361
362 bool StructuralFeatureConverter::supports_stage(Stage::Type st) const
363 {
364         if(st==Stage::GEOMETRY)
365         {
366                 if(features.target_api==VULKAN)
367                         return true;
368                 else if(features.target_api==OPENGL_ES)
369                         return check_version(Version(3, 20));
370                 else
371                         return check_version(Version(1, 50));
372         }
373         else
374                 return true;
375 }
376
377 bool StructuralFeatureConverter::supports_unified_interface_syntax() const
378 {
379         if(features.target_api==VULKAN)
380                 return true;
381         else if(features.target_api==OPENGL_ES)
382                 return check_version(Version(3, 0));
383         else
384                 return check_version(Version(1, 30));
385 }
386
387 void StructuralFeatureConverter::visit(VariableReference &var)
388 {
389         if(var.declaration==frag_out && !supports_unified_interface_syntax())
390         {
391                 var.name = "gl_FragColor";
392                 var.declaration = 0;
393         }
394 }
395
396 void StructuralFeatureConverter::visit(InterfaceBlockReference &iface)
397 {
398         r_flattened_interface = nodes_to_remove.count(iface.declaration);
399 }
400
401 void StructuralFeatureConverter::visit(MemberAccess &memacc)
402 {
403         r_flattened_interface = false;
404         visit(memacc.left);
405         if(r_flattened_interface)
406         {
407                 VariableReference *var = new VariableReference;
408                 var->name = memacc.member;
409                 r_replaced_reference = var;
410         }
411 }
412
413 void StructuralFeatureConverter::visit(Assignment &assign)
414 {
415         TraversingVisitor::visit(assign);
416         if(assign.target.declaration==frag_out && !supports_unified_interface_syntax())
417                 assign.target.declaration = 0;
418 }
419
420 bool StructuralFeatureConverter::supports_unified_sampling_functions() const
421 {
422         if(features.target_api==VULKAN)
423                 return true;
424         else if(features.target_api==OPENGL_ES)
425                 return check_version(Version(3, 0));
426         else
427                 return check_version(Version(1, 30));
428 }
429
430 void StructuralFeatureConverter::visit(FunctionCall &call)
431 {
432         if(call.declaration && call.declaration->source==BUILTIN_SOURCE)
433         {
434                 if(!call.name.compare(0, 7, "texture") && call.arguments.size()>=1)
435                 {
436                         const ImageTypeDeclaration *arg_image = dynamic_cast<const ImageTypeDeclaration *>(call.arguments.front()->type);
437                         if(arg_image && !supports_unified_sampling_functions())
438                         {
439                                 string suffix = call.name.substr(7);
440                                 call.name = (arg_image->shadow ? "shadow" : "texture");
441
442                                 switch(arg_image->dimensions)
443                                 {
444                                 case ImageTypeDeclaration::ONE: call.name += "1D"; break;
445                                 case ImageTypeDeclaration::TWO: call.name += "2D"; break;
446                                 case ImageTypeDeclaration::THREE: call.name += "3D"; break;
447                                 case ImageTypeDeclaration::CUBE: call.name += "Cube"; break;
448                                 }
449
450                                 if(arg_image->array)
451                                 {
452                                         /* Array textures and the unified sampling function name were
453                                         both introduced in GLSL 1.30. */
454                                         if(arg_image->dimensions==ImageTypeDeclaration::ONE || arg_image->dimensions==ImageTypeDeclaration::TWO)
455                                                 check_extension(&Features::ext_texture_array);
456                                         call.name += "Array";
457                                 }
458
459                                 call.name += suffix;
460                         }
461                 }
462         }
463
464         TraversingVisitor::visit(call);
465 }
466
467 void StructuralFeatureConverter::visit(VariableDeclaration &var)
468 {
469         if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
470                 if(stage->type==Stage::FRAGMENT && var.interface=="out")
471                 {
472                         frag_out = &var;
473                         nodes_to_remove.insert(&var);
474                 }
475
476         TraversingVisitor::visit(var);
477 }
478
479 bool StructuralFeatureConverter::supports_interface_blocks(const string &iface) const
480 {
481         if(features.target_api==VULKAN)
482                 return true;
483         else if(features.target_api==OPENGL_ES)
484         {
485                 if(iface=="uniform")
486                         return check_version(Version(3, 0));
487                 else
488                         return check_version(Version(3, 20));
489         }
490         else if(check_version(Version(1, 50)))
491                 return true;
492         else if(iface=="uniform")
493                 return check_extension(&Features::arb_uniform_buffer_object);
494         else
495                 return false;
496 }
497
498 void StructuralFeatureConverter::visit(InterfaceBlock &iface)
499 {
500         bool push_constant = has_layout_qualifier(iface.layout.get(), "push_constant");
501         if((!supports_interface_blocks(iface.interface) || (push_constant && features.target_api!=VULKAN)) && iface.type_declaration)
502         {
503                 if(!iface.instance_name.empty())
504                         unsupported("ARB_uniform_buffer_object required for interface block instances");
505                 else if(iface.struct_declaration)
506                 {
507                         for(const RefPtr<Statement> &s: iface.struct_declaration->members.body)
508                                 if(VariableDeclaration *var = dynamic_cast<VariableDeclaration *>(s.get()))
509                                         var->interface = iface.interface;
510                         stage->content.body.splice(uniform_insert_point, iface.struct_declaration->members.body);
511                         nodes_to_remove.insert(&iface);
512                         nodes_to_remove.insert(iface.struct_declaration);
513                 }
514                 else
515                         /* If the interface block is an array, it should have an instance
516                         name too, so this should never be reached */
517                         throw logic_error("Unexpected interface block configuration");
518         }
519 }
520
521
522 void QualifierConverter::apply()
523 {
524         stage->content.visit(*this);
525 }
526
527 bool QualifierConverter::supports_interface_layouts() const
528 {
529         if(features.target_api==VULKAN)
530                 return true;
531         else if(features.target_api==OPENGL_ES)
532                 return check_version(Version(3, 0));
533         else if(check_version(Version(3, 30)))
534                 return true;
535         else if(check_version(Version(1, 30)))
536                 return check_extension(&Features::arb_explicit_attrib_location);
537         else
538                 return false;
539 }
540
541 bool QualifierConverter::supports_stage_interface_layouts() const
542 {
543         if(features.target_api==VULKAN)
544                 return true;
545         else if(features.target_api==OPENGL_ES)
546                 return check_version(Version(3, 10));
547         else if(check_version(Version(4, 10)))
548                 return true;
549         else
550                 return check_extension(&Features::arb_separate_shader_objects);
551 }
552
553 bool QualifierConverter::supports_centroid_sampling() const
554 {
555         if(features.target_api==VULKAN)
556                 return true;
557         else if(features.target_api==OPENGL_ES)
558                 return check_version(Version(3, 0));
559         else if(check_version(Version(1, 20)))
560                 return true;
561         else
562                 return check_extension(&Features::ext_gpu_shader4);
563 }
564
565 bool QualifierConverter::supports_sample_sampling() const
566 {
567         if(features.target_api==VULKAN)
568                 return true;
569         else if(features.target_api==OPENGL_ES)
570                 return check_version(Version(3, 20));
571         else if(check_version(Version(4, 0)))
572                 return true;
573         else
574                 return check_extension(&Features::arb_gpu_shader5);
575 }
576
577 bool QualifierConverter::supports_uniform_location() const
578 {
579         if(features.target_api==VULKAN)
580                 return true;
581         else if(features.target_api==OPENGL_ES)
582                 return check_version(Version(3, 10));
583         else if(check_version(Version(4, 30)))
584                 return true;
585         else
586                 return check_extension(&Features::arb_explicit_uniform_location);
587 }
588
589 bool QualifierConverter::supports_binding() const
590 {
591         if(features.target_api==VULKAN)
592                 return true;
593         else if(features.target_api==OPENGL_ES)
594                 return check_version(Version(3, 10));
595         else
596                 return check_version(Version(4, 20));
597 }
598
599 void QualifierConverter::visit(VariableDeclaration &var)
600 {
601         if(var.layout)
602         {
603                 for(auto i=var.layout->qualifiers.begin(); i!=var.layout->qualifiers.end(); )
604                 {
605                         if(i->name=="location")
606                         {
607                                 bool supported = true;
608                                 bool external = false;
609                                 if(var.interface=="in")
610                                 {
611                                         external = (stage->type==Stage::VERTEX);
612                                         supported = (external ? supports_interface_layouts() : supports_stage_interface_layouts());
613                                 }
614                                 else if(var.interface=="out")
615                                 {
616                                         external = (stage->type==Stage::FRAGMENT);
617                                         supported = (external ? supports_interface_layouts() : supports_stage_interface_layouts());
618                                         if(external && !supported && !check_extension(&Features::ext_gpu_shader4))
619                                         {
620                                                 external = false;
621                                                 if(i->value!=0)
622                                                         unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs");
623                                         }
624                                 }
625                                 else if(var.interface=="uniform")
626                                         supported = supports_uniform_location();
627
628                                 if(!supported)
629                                 {
630                                         if(external)
631                                                 stage->locations[var.name] = i->value;
632                                         i = var.layout->qualifiers.erase(i);
633                                 }
634                                 else
635                                         ++i;
636                         }
637                         else if(i->name=="binding" && !supports_binding())
638                         {
639                                 if(dynamic_cast<const ImageTypeDeclaration *>(get_ultimate_base_type(var.type_declaration)))
640                                         stage->texture_bindings[var.name] = i->value;
641
642                                 i = var.layout->qualifiers.erase(i);
643                         }
644                         else
645                                 ++i;
646                 }
647
648                 if(var.layout->qualifiers.empty())
649                         var.layout = 0;
650         }
651
652         if(var.sampling=="centroid")
653         {
654                 if(!supports_centroid_sampling())
655                         var.sampling = string();
656         }
657         else if(var.sampling=="sample")
658         {
659                 if(!supports_sample_sampling())
660                         var.sampling = string();
661         }
662
663         if(var.name=="gl_ClipDistance")
664                 if(const Literal *literal_size = dynamic_cast<const Literal *>(var.array_size.get()))
665                         stage->n_clip_distances = literal_size->value.value<int>();
666
667         TraversingVisitor::visit(var);
668 }
669
670 bool QualifierConverter::supports_interface_block_location() const
671 {
672         if(features.target_api==VULKAN)
673                 return true;
674         else if(features.target_api==OPENGL_ES)
675                 return check_version(Version(3, 20));
676         else if(check_version(Version(4, 40)))
677                 return true;
678         else
679                 return check_extension(&Features::arb_enhanced_layouts);
680 }
681
682 void QualifierConverter::visit(InterfaceBlock &iface)
683 {
684         if(iface.layout)
685         {
686                 for(auto i=iface.layout->qualifiers.begin(); i!=iface.layout->qualifiers.end(); )
687                 {
688                         if(i->name=="location" && !supports_interface_block_location())
689                                 i = iface.layout->qualifiers.erase(i);
690                         else if(i->name=="binding" && !supports_binding())
691                         {
692                                 stage->uniform_block_bindings[iface.block_name] = i->value;
693                                 i = iface.layout->qualifiers.erase(i);
694                         }
695                         else
696                                 ++i;
697                 }
698
699                 if(iface.layout->qualifiers.empty())
700                         iface.layout = 0;
701         }
702 }
703
704 } // namespace SL
705 } // namespace GL
706 } // namespace Msp