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