]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/compatibility.cpp
Transform interface block contents into structs
[libs/gl.git] / source / glsl / compatibility.cpp
1 #include <msp/core/algorithm.h>
2 #include <msp/core/raii.h>
3 #include <msp/strings/lexicalcast.h>
4 #include "compatibility.h"
5 #include "glsl_error.h"
6
7 using namespace std;
8
9 namespace Msp {
10 namespace GL {
11 namespace SL {
12
13 DefaultPrecisionGenerator::DefaultPrecisionGenerator():
14         stage(0)
15 { }
16
17 void DefaultPrecisionGenerator::apply(Stage &s)
18 {
19         stage = &s;
20         s.content.visit(*this);
21 }
22
23 void DefaultPrecisionGenerator::visit(Block &block)
24 {
25         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
26         {
27                 if(&block==&stage->content)
28                         insert_point = i;
29                 (*i)->visit(*this);
30         }
31 }
32
33 void DefaultPrecisionGenerator::visit(Precision &prec)
34 {
35         have_default.insert(prec.type);
36 }
37
38 void DefaultPrecisionGenerator::visit(VariableDeclaration &var)
39 {
40         if(var.type_declaration)
41                 return;
42
43         string type = var.type;
44         if(!type.compare(0, 3, "vec") || !type.compare(0, 3, "mat"))
45                 type = "float";
46         else if(!type.compare(0, 3, "ivec") || type=="uint")
47                 type = "int";
48
49         if(!have_default.count(type))
50         {
51                 Precision *prec = new Precision;
52                 if(!type.compare(0, 7, "sampler"))
53                         prec->precision = "lowp";
54                 else if(stage->type==Stage::FRAGMENT)
55                         prec->precision = "mediump";
56                 else
57                         prec->precision = "highp";
58                 prec->type = type;
59                 stage->content.body.insert(insert_point, prec);
60
61                 have_default.insert(type);
62         }
63 }
64
65
66 void PrecisionRemover::apply(Stage &stage)
67 {
68         stage.content.visit(*this);
69         NodeRemover().apply(stage, nodes_to_remove);
70 }
71
72 void PrecisionRemover::visit(Precision &prec)
73 {
74         nodes_to_remove.insert(&prec);
75 }
76
77 void PrecisionRemover::visit(VariableDeclaration &var)
78 {
79         var.precision.clear();
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                 s.content.visit(*this);
93         else
94                 unsupported(format("Stage %s is not supported", Stage::get_stage_name(s.type)));
95 }
96
97 void LegacyConverter::unsupported(const string &reason)
98 {
99         Diagnostic diagnostic;
100         diagnostic.severity = Diagnostic::ERR;
101         diagnostic.source = GENERATED_SOURCE;
102         diagnostic.line = 0;
103         diagnostic.message = reason;
104         stage->diagnostics.push_back(diagnostic);
105 }
106
107 void LegacyConverter::visit(Block &block)
108 {
109         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
110         {
111                 if(&block==&stage->content)
112                         uniform_insert_point = i;
113                 (*i)->visit(*this);
114         }
115 }
116
117 bool LegacyConverter::check_version(const Version &feature_version) const
118 {
119         if(features.glsl_version<feature_version)
120                 return false;
121         else if(stage->required_features.glsl_version<feature_version)
122                 stage->required_features.glsl_version = feature_version;
123
124         return true;
125 }
126
127 bool LegacyConverter::check_extension(bool Features::*extension) const
128 {
129         if(!(features.*extension))
130                 return false;
131
132         stage->required_features.*extension = true;
133
134         return true;
135 }
136
137 bool LegacyConverter::supports_stage(Stage::Type st) const
138 {
139         if(st==Stage::GEOMETRY)
140         {
141                 if(features.gl_api==OPENGL_ES2)
142                         return check_version(Version(3, 20));
143                 else
144                         return check_version(Version(1, 50));
145         }
146         else
147                 return true;
148 }
149
150 bool LegacyConverter::supports_unified_interface_syntax() const
151 {
152         if(features.gl_api==OPENGL_ES2)
153                 return check_version(Version(3, 0));
154         else
155                 return check_version(Version(1, 30));
156 }
157
158 void LegacyConverter::visit(VariableReference &var)
159 {
160         if(var.declaration==frag_out && !supports_unified_interface_syntax())
161         {
162                 var.name = "gl_FragColor";
163                 var.declaration = 0;
164                 r_type = "vec4";
165         }
166         else if(var.declaration)
167                 r_type = var.declaration->type;
168         else
169                 r_type.clear();
170 }
171
172 void LegacyConverter::visit(Assignment &assign)
173 {
174         TraversingVisitor::visit(assign);
175         if(assign.target_declaration==frag_out && !supports_unified_interface_syntax())
176                 assign.target_declaration = 0;
177 }
178
179 bool LegacyConverter::supports_unified_sampling_functions() const
180 {
181         if(features.gl_api==OPENGL_ES2)
182                 return check_version(Version(3, 0));
183         else
184                 return check_version(Version(1, 30));
185 }
186
187 void LegacyConverter::visit(FunctionCall &call)
188 {
189         if(call.name=="texture")
190         {
191                 string sampler_type;
192                 r_type.clear();
193                 NodeArray<Expression>::iterator i = call.arguments.begin();
194                 if(i!=call.arguments.end())
195                 {
196                         (*i)->visit(*this);
197                         sampler_type = r_type;
198
199                         for(; i!=call.arguments.end(); ++i)
200                                 (*i)->visit(*this);
201                 }
202
203                 if(!supports_unified_sampling_functions())
204                 {
205                         if(sampler_type=="sampler1D")
206                                 call.name = "texture1D";
207                         else if(sampler_type=="sampler2D")
208                                 call.name = "texture2D";
209                         else if(sampler_type=="sampler3D")
210                                 call.name = "texture3D";
211                         else if(sampler_type=="samplerCube")
212                                 call.name = "textureCube";
213                         else if(sampler_type=="sampler1DShadow")
214                                 call.name = "shadow1D";
215                         else if(sampler_type=="sampler2DShadow")
216                                 call.name = "shadow2D";
217                         else if(sampler_type=="sampler1DArray")
218                         {
219                                 check_extension(&Features::ext_texture_array);
220                                 call.name = "texture1DArray";
221                         }
222                         else if(sampler_type=="sampler2DArray")
223                         {
224                                 check_extension(&Features::ext_texture_array);
225                                 call.name = "texture2DArray";
226                         }
227                         else if(sampler_type=="sampler1DArrayShadow")
228                         {
229                                 check_extension(&Features::ext_texture_array);
230                                 call.name = "shadow1DArray";
231                         }
232                         else if(sampler_type=="sampler2DArrayShadow")
233                         {
234                                 check_extension(&Features::ext_texture_array);
235                                 call.name = "shadow2DArray";
236                         }
237                 }
238         }
239         else
240                 TraversingVisitor::visit(call);
241 }
242
243 bool LegacyConverter::supports_interface_layouts() const
244 {
245         if(features.gl_api==OPENGL_ES2)
246                 return check_version(Version(3, 0));
247         else if(check_version(Version(3, 30)))
248                 return true;
249         else
250                 return check_extension(&Features::arb_explicit_attrib_location);
251 }
252
253 bool LegacyConverter::supports_centroid_sampling() const
254 {
255         if(features.gl_api==OPENGL_ES2)
256                 return check_version(Version(3, 0));
257         else if(check_version(Version(1, 20)))
258                 return true;
259         else
260                 return check_extension(&Features::ext_gpu_shader4);
261 }
262
263 bool LegacyConverter::supports_sample_sampling() const
264 {
265         if(features.gl_api==OPENGL_ES2)
266                 return check_version(Version(3, 20));
267         else if(check_version(Version(4, 0)))
268                 return true;
269         else
270                 return check_extension(&Features::arb_gpu_shader5);
271 }
272
273 void LegacyConverter::visit(VariableDeclaration &var)
274 {
275         if(var.layout && !supports_interface_layouts())
276         {
277                 vector<Layout::Qualifier>::iterator i;
278                 for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->name!="location"); ++i) ;
279                 if(i!=var.layout->qualifiers.end())
280                 {
281                         if(stage->type==Stage::VERTEX && var.interface=="in")
282                         {
283                                 stage->locations[var.name] = i->value;
284                                 var.layout->qualifiers.erase(i);
285                         }
286                         else if(stage->type==Stage::FRAGMENT && var.interface=="out")
287                         {
288                                 if(check_extension(&Features::ext_gpu_shader4))
289                                 {
290                                         stage->locations[var.name] = i->value;
291                                         var.layout->qualifiers.erase(i);
292                                 }
293                                 else if(i->value!=0)
294                                         unsupported("EXT_gpu_shader4 required for multiple fragment shader outputs");
295                         }
296
297                         if(var.layout->qualifiers.empty())
298                                 var.layout = 0;
299                 }
300         }
301
302         if(var.sampling=="centroid")
303         {
304                 if(!supports_centroid_sampling())
305                         var.sampling = string();
306         }
307         else if(var.sampling=="sample")
308         {
309                 if(!supports_sample_sampling())
310                         var.sampling = string();
311         }
312
313         if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
314         {
315                 if(stage->type==Stage::FRAGMENT && var.interface=="out")
316                 {
317                         frag_out = &var;
318                         nodes_to_remove.insert(&var);
319                 }
320         }
321
322         TraversingVisitor::visit(var);
323 }
324
325 bool LegacyConverter::supports_interface_blocks(const string &iface) const
326 {
327         if(features.gl_api==OPENGL_ES2)
328         {
329                 if(iface=="uniform")
330                         return check_version(Version(3, 0));
331                 else
332                         return check_version(Version(3, 20));
333         }
334         else if(check_version(Version(1, 50)))
335                 return true;
336         else if(iface=="uniform")
337                 return check_extension(&Features::arb_uniform_buffer_object);
338         else
339                 return false;
340 }
341
342 void LegacyConverter::visit(InterfaceBlock &iface)
343 {
344         if(!supports_interface_blocks(iface.interface) && iface.type_declaration)
345         {
346                 if(!iface.instance_name.empty())
347                         unsupported("ARB_uniform_buffer_object required for interface block instances");
348                 else if(iface.struct_declaration)
349                 {
350                         stage->content.body.splice(uniform_insert_point, iface.struct_declaration->members.body);
351                         nodes_to_remove.insert(&iface);
352                         nodes_to_remove.insert(iface.struct_declaration);
353                 }
354                 else
355                         /* If the interface block is an array, it should have an instance
356                         name too, so this should never be reached */
357                         throw logic_error("Unexpected interface block configuration");
358         }
359 }
360
361 } // namespace SL
362 } // namespace GL
363 } // namespace Msp