]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/finalize.cpp
0adb2960b1da3784a4554d9a97b447b12163481d
[libs/gl.git] / source / glsl / finalize.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/raii.h>
3 #include <msp/strings/lexicalcast.h>
4 #include "finalize.h"
5 #include "glsl_error.h"
6
7 using namespace std;
8
9 namespace Msp {
10 namespace GL {
11 namespace SL {
12
13 PrecisionConverter::PrecisionConverter():
14         stage(0)
15 { }
16
17 void PrecisionConverter::apply(Stage &s)
18 {
19         stage = &s;
20         s.content.visit(*this);
21         NodeRemover().apply(s, nodes_to_remove);
22 }
23
24 void PrecisionConverter::visit(Block &block)
25 {
26         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
27         {
28                 if(&block==&stage->content)
29                         insert_point = i;
30                 (*i)->visit(*this);
31         }
32 }
33
34 void PrecisionConverter::visit(Precision &prec)
35 {
36         if(stage->required_features.gl_api==OPENGL_ES2)
37                 have_default.insert(prec.type);
38         else
39                 nodes_to_remove.insert(&prec);
40 }
41
42 void PrecisionConverter::visit(VariableDeclaration &var)
43 {
44         if(stage->required_features.gl_api!=OPENGL_ES2)
45         {
46                 var.precision.clear();
47                 return;
48         }
49
50         const char *default_prec = (stage->type==Stage::FRAGMENT ? "mediump" : "highp");
51         const TypeDeclaration *type = var.type_declaration;
52         while(type)
53         {
54                 if(dynamic_cast<const ImageTypeDeclaration *>(type))
55                 {
56                         default_prec = "lowp";
57                         break;
58                 }
59                 else if(const BasicTypeDeclaration *basic = dynamic_cast<const BasicTypeDeclaration *>(type))
60                 {
61                         if(basic->kind==BasicTypeDeclaration::INT || basic->kind==BasicTypeDeclaration::FLOAT)
62                                 break;
63                         type = basic->base_type;
64                 }
65                 else
66                         return;
67         }
68         if(!type)
69                 return;
70
71         if(!have_default.count(type->name))
72         {
73                 Precision *prec = new Precision;
74                 prec->precision = default_prec;
75                 prec->type = type->name;
76                 stage->content.body.insert(insert_point, prec);
77
78                 have_default.insert(type->name);
79         }
80 }
81
82
83 LegacyConverter::LegacyConverter():
84         frag_out(0)
85 { }
86
87 void LegacyConverter::apply(Stage &s, const Features &feat)
88 {
89         stage = &s;
90         features = feat;
91         if(supports_stage(s.type))
92         {
93                 s.content.visit(*this);
94                 NodeRemover().apply(s, nodes_to_remove);
95
96                 if(!stage->required_features.glsl_version)
97                         stage->required_features.glsl_version = Version(1, (stage->required_features.gl_api==OPENGL_ES2 ? 0 : 10));
98         }
99         else
100                 unsupported(format("Stage %s is not supported", Stage::get_stage_name(s.type)));
101 }
102
103 void LegacyConverter::unsupported(const string &reason)
104 {
105         Diagnostic diagnostic;
106         diagnostic.severity = Diagnostic::ERR;
107         diagnostic.source = GENERATED_SOURCE;
108         diagnostic.line = 0;
109         diagnostic.message = reason;
110         stage->diagnostics.push_back(diagnostic);
111 }
112
113 void LegacyConverter::visit(Block &block)
114 {
115         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
116         {
117                 if(&block==&stage->content)
118                         uniform_insert_point = i;
119                 (*i)->visit(*this);
120         }
121 }
122
123 bool LegacyConverter::check_version(const Version &feature_version) const
124 {
125         if(features.glsl_version<feature_version)
126                 return false;
127         else if(stage->required_features.glsl_version<feature_version)
128                 stage->required_features.glsl_version = feature_version;
129
130         return true;
131 }
132
133 bool LegacyConverter::check_extension(bool Features::*extension) const
134 {
135         if(!(features.*extension))
136                 return false;
137
138         stage->required_features.*extension = true;
139
140         return true;
141 }
142
143 bool LegacyConverter::supports_stage(Stage::Type st) const
144 {
145         if(st==Stage::GEOMETRY)
146         {
147                 if(features.gl_api==OPENGL_ES2)
148                         return check_version(Version(3, 20));
149                 else
150                         return check_version(Version(1, 50));
151         }
152         else
153                 return true;
154 }
155
156 bool LegacyConverter::supports_unified_interface_syntax() const
157 {
158         if(features.gl_api==OPENGL_ES2)
159                 return check_version(Version(3, 0));
160         else
161                 return check_version(Version(1, 30));
162 }
163
164 void LegacyConverter::visit(VariableReference &var)
165 {
166         if(var.declaration==frag_out && !supports_unified_interface_syntax())
167         {
168                 var.name = "gl_FragColor";
169                 var.declaration = 0;
170         }
171 }
172
173 void LegacyConverter::visit(Assignment &assign)
174 {
175         TraversingVisitor::visit(assign);
176         if(assign.target.declaration==frag_out && !supports_unified_interface_syntax())
177                 assign.target.declaration = 0;
178 }
179
180 bool LegacyConverter::supports_unified_sampling_functions() const
181 {
182         if(features.gl_api==OPENGL_ES2)
183                 return check_version(Version(3, 0));
184         else
185                 return check_version(Version(1, 30));
186 }
187
188 void LegacyConverter::visit(FunctionCall &call)
189 {
190         if(call.declaration && call.declaration->source==BUILTIN_SOURCE)
191         {
192                 if(!call.name.compare(0, 7, "texture") && call.arguments.size()>=1)
193                 {
194                         const ImageTypeDeclaration *arg_image = dynamic_cast<const ImageTypeDeclaration *>(call.arguments.front()->type);
195                         if(arg_image && !supports_unified_sampling_functions())
196                         {
197                                 string suffix = call.name.substr(7);
198                                 call.name = (arg_image->shadow ? "shadow" : "texture");
199
200                                 switch(arg_image->dimensions)
201                                 {
202                                 case ImageTypeDeclaration::ONE: call.name += "1D"; break;
203                                 case ImageTypeDeclaration::TWO: call.name += "2D"; break;
204                                 case ImageTypeDeclaration::THREE: call.name += "3D"; break;
205                                 case ImageTypeDeclaration::CUBE: call.name += "Cube"; break;
206                                 }
207
208                                 if(arg_image->array)
209                                 {
210                                         /* Array textures and the unified sampling function name were
211                                         both introduced in GLSL 1.30. */
212                                         if(arg_image->dimensions==ImageTypeDeclaration::ONE || arg_image->dimensions==ImageTypeDeclaration::TWO)
213                                                 check_extension(&Features::ext_texture_array);
214                                         call.name += "Array";
215                                 }
216
217                                 call.name += suffix;
218                         }
219                 }
220         }
221
222         TraversingVisitor::visit(call);
223 }
224
225 bool LegacyConverter::supports_interface_layouts() const
226 {
227         if(features.gl_api==OPENGL_ES2)
228                 return check_version(Version(3, 0));
229         else if(check_version(Version(3, 30)))
230                 return true;
231         else if(check_version(Version(1, 30)))
232                 return check_extension(&Features::arb_explicit_attrib_location);
233         else
234                 return false;
235 }
236
237 bool LegacyConverter::supports_stage_interface_layouts() const
238 {
239         if(features.gl_api==OPENGL_ES2)
240                 return check_version(Version(3, 10));
241         else if(check_version(Version(4, 10)))
242                 return true;
243         else
244                 return check_extension(&Features::arb_separate_shader_objects);
245 }
246
247 bool LegacyConverter::supports_centroid_sampling() const
248 {
249         if(features.gl_api==OPENGL_ES2)
250                 return check_version(Version(3, 0));
251         else if(check_version(Version(1, 20)))
252                 return true;
253         else
254                 return check_extension(&Features::ext_gpu_shader4);
255 }
256
257 bool LegacyConverter::supports_sample_sampling() const
258 {
259         if(features.gl_api==OPENGL_ES2)
260                 return check_version(Version(3, 20));
261         else if(check_version(Version(4, 0)))
262                 return true;
263         else
264                 return check_extension(&Features::arb_gpu_shader5);
265 }
266
267 bool LegacyConverter::supports_uniform_location() const
268 {
269         if(features.gl_api==OPENGL_ES2)
270                 return check_version(Version(3, 10));
271         else if(check_version(Version(4, 30)))
272                 return true;
273         else
274                 return check_extension(&Features::arb_explicit_uniform_location);
275 }
276
277 void LegacyConverter::visit(VariableDeclaration &var)
278 {
279         if(var.layout)
280         {
281                 for(vector<Layout::Qualifier>::const_iterator i=var.layout->qualifiers.begin(); i!=var.layout->qualifiers.end(); )
282                 {
283                         if(i->name=="location")
284                         {
285                                 bool supported = true;
286                                 bool external = false;
287                                 if(var.interface=="in")
288                                 {
289                                         external = (stage->type==Stage::VERTEX);
290                                         supported = (external ? supports_interface_layouts() : supports_stage_interface_layouts());
291                                 }
292                                 else if(var.interface=="out")
293                                 {
294                                         external = (stage->type==Stage::FRAGMENT);
295                                         supported = (external ? supports_interface_layouts() : supports_stage_interface_layouts());
296                                         if(external && !supported && !check_extension(&Features::ext_gpu_shader4))
297                                         {
298                                                 external = false;
299                                                 if(i->value!=0)
300                                                         unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs");
301                                         }
302                                 }
303                                 else if(var.interface=="uniform")
304                                         supported = supports_uniform_location();
305
306                                 if(!supported)
307                                 {
308                                         if(external)
309                                                 stage->locations[var.name] = i->value;
310                                         i = var.layout->qualifiers.erase(i);
311                                 }
312                                 else
313                                         ++i;
314                         }
315                         else
316                                 ++i;
317                 }
318
319                 if(var.layout->qualifiers.empty())
320                         var.layout = 0;
321         }
322
323         if(var.sampling=="centroid")
324         {
325                 if(!supports_centroid_sampling())
326                         var.sampling = string();
327         }
328         else if(var.sampling=="sample")
329         {
330                 if(!supports_sample_sampling())
331                         var.sampling = string();
332         }
333
334         if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
335         {
336                 if(stage->type==Stage::FRAGMENT && var.interface=="out")
337                 {
338                         frag_out = &var;
339                         nodes_to_remove.insert(&var);
340                 }
341         }
342
343         TraversingVisitor::visit(var);
344 }
345
346 bool LegacyConverter::supports_interface_blocks(const string &iface) const
347 {
348         if(features.gl_api==OPENGL_ES2)
349         {
350                 if(iface=="uniform")
351                         return check_version(Version(3, 0));
352                 else
353                         return check_version(Version(3, 20));
354         }
355         else if(check_version(Version(1, 50)))
356                 return true;
357         else if(iface=="uniform")
358                 return check_extension(&Features::arb_uniform_buffer_object);
359         else
360                 return false;
361 }
362
363 bool LegacyConverter::supports_interface_block_location() const
364 {
365         if(features.gl_api==OPENGL_ES2)
366                 return check_version(Version(3, 20));
367         else if(check_version(Version(4, 40)))
368                 return true;
369         else
370                 return check_extension(&Features::arb_enhanced_layouts);
371 }
372
373 void LegacyConverter::visit(InterfaceBlock &iface)
374 {
375         if(iface.layout)
376         {
377                 for(vector<Layout::Qualifier>::const_iterator i=iface.layout->qualifiers.begin(); i!=iface.layout->qualifiers.end(); )
378                 {
379                         if(i->name=="location" && !supports_interface_block_location())
380                                 i = iface.layout->qualifiers.erase(i);
381                         else
382                                 ++i;
383                 }
384
385                 if(iface.layout->qualifiers.empty())
386                         iface.layout = 0;
387         }
388
389         if(!supports_interface_blocks(iface.interface) && iface.type_declaration)
390         {
391                 if(!iface.instance_name.empty())
392                         unsupported("ARB_uniform_buffer_object required for interface block instances");
393                 else if(iface.struct_declaration)
394                 {
395                         stage->content.body.splice(uniform_insert_point, iface.struct_declaration->members.body);
396                         nodes_to_remove.insert(&iface);
397                         nodes_to_remove.insert(iface.struct_declaration);
398                 }
399                 else
400                         /* If the interface block is an array, it should have an instance
401                         name too, so this should never be reached */
402                         throw logic_error("Unexpected interface block configuration");
403         }
404 }
405
406 } // namespace SL
407 } // namespace GL
408 } // namespace Msp