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