]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/compatibility.cpp
Throw an exception if a feature required by a shader is missing
[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         visit(s.content);
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         visit(stage.content);
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         visit(s.content);
92 }
93
94 void LegacyConverter::visit(Block &block)
95 {
96         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
97         {
98                 if(&block==&stage->content)
99                         uniform_insert_point = i;
100                 (*i)->visit(*this);
101         }
102 }
103
104 bool LegacyConverter::check_version(const Version &feature_version) const
105 {
106         if(features.glsl_version<feature_version)
107                 return false;
108         else if(stage->required_features.glsl_version<feature_version)
109                 stage->required_features.glsl_version = feature_version;
110
111         return true;
112 }
113
114 bool LegacyConverter::check_extension(bool Features::*extension) const
115 {
116         if(!(features.*extension))
117                 return false;
118
119         stage->required_features.*extension = true;
120
121         return true;
122 }
123
124 bool LegacyConverter::supports_unified_interface_syntax() const
125 {
126         if(features.gl_api==OPENGL_ES2)
127                 return check_version(Version(3, 0));
128         else
129                 return check_version(Version(1, 30));
130 }
131
132 void LegacyConverter::visit(VariableReference &var)
133 {
134         if(var.declaration==frag_out && !supports_unified_interface_syntax())
135         {
136                 var.name = "gl_FragColor";
137                 var.declaration = 0;
138                 type = "vec4";
139         }
140         else if(var.declaration)
141                 type = var.declaration->type;
142         else
143                 type = string();
144 }
145
146 void LegacyConverter::visit(Assignment &assign)
147 {
148         TraversingVisitor::visit(assign);
149         if(assign.target_declaration==frag_out && !supports_unified_interface_syntax())
150                 assign.target_declaration = 0;
151 }
152
153 bool LegacyConverter::supports_unified_sampling_functions() const
154 {
155         if(features.gl_api==OPENGL_ES2)
156                 return check_version(Version(3, 0));
157         else
158                 return check_version(Version(1, 30));
159 }
160
161 void LegacyConverter::visit(FunctionCall &call)
162 {
163         if(call.name=="texture")
164         {
165                 string sampler_type;
166                 type = string();
167                 NodeArray<Expression>::iterator i = call.arguments.begin();
168                 if(i!=call.arguments.end())
169                 {
170                         (*i)->visit(*this);
171                         sampler_type = type;
172
173                         for(; i!=call.arguments.end(); ++i)
174                                 (*i)->visit(*this);
175                 }
176
177                 if(!supports_unified_sampling_functions())
178                 {
179                         if(sampler_type=="sampler1D")
180                                 call.name = "texture1D";
181                         else if(sampler_type=="sampler2D")
182                                 call.name = "texture2D";
183                         else if(sampler_type=="sampler3D")
184                                 call.name = "texture3D";
185                         else if(sampler_type=="samplerCube")
186                                 call.name = "textureCube";
187                         else if(sampler_type=="sampler1DShadow")
188                                 call.name = "shadow1D";
189                         else if(sampler_type=="sampler2DShadow")
190                                 call.name = "shadow2D";
191                         else if(sampler_type=="sampler1DArray")
192                         {
193                                 check_extension(&Features::ext_texture_array);
194                                 call.name = "texture1DArray";
195                         }
196                         else if(sampler_type=="sampler2DArray")
197                         {
198                                 check_extension(&Features::ext_texture_array);
199                                 call.name = "texture2DArray";
200                         }
201                         else if(sampler_type=="sampler1DArrayShadow")
202                         {
203                                 check_extension(&Features::ext_texture_array);
204                                 call.name = "shadow1DArray";
205                         }
206                         else if(sampler_type=="sampler2DArrayShadow")
207                         {
208                                 check_extension(&Features::ext_texture_array);
209                                 call.name = "shadow2DArray";
210                         }
211                 }
212         }
213         else
214                 TraversingVisitor::visit(call);
215 }
216
217 bool LegacyConverter::supports_interface_layouts() const
218 {
219         if(features.gl_api==OPENGL_ES2)
220                 return check_version(Version(3, 0));
221         else if(check_version(Version(3, 30)))
222                 return true;
223         else
224                 return check_extension(&Features::arb_explicit_attrib_location);
225 }
226
227 bool LegacyConverter::supports_centroid_sampling() const
228 {
229         if(features.gl_api==OPENGL_ES2)
230                 return check_version(Version(3, 0));
231         else if(check_version(Version(1, 20)))
232                 return true;
233         else
234                 return check_extension(&Features::ext_gpu_shader4);
235 }
236
237 bool LegacyConverter::supports_sample_sampling() const
238 {
239         if(features.gl_api==OPENGL_ES2)
240                 return check_version(Version(3, 20));
241         else if(check_version(Version(4, 0)))
242                 return true;
243         else
244                 return check_extension(&Features::arb_gpu_shader5);
245 }
246
247 void LegacyConverter::visit(VariableDeclaration &var)
248 {
249         if(var.layout && !supports_interface_layouts())
250         {
251                 vector<Layout::Qualifier>::iterator i;
252                 for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->identifier!="location"); ++i) ;
253                 if(i!=var.layout->qualifiers.end())
254                 {
255                         unsigned location = lexical_cast<unsigned>(i->value);
256                         if(stage->type==Stage::VERTEX && var.interface=="in")
257                         {
258                                 stage->locations[var.name] = location;
259                                 var.layout->qualifiers.erase(i);
260                         }
261                         else if(stage->type==Stage::FRAGMENT && var.interface=="out")
262                         {
263                                 if(location!=0 && !check_extension(&Features::ext_gpu_shader4))
264                                         throw unsupported_shader("EXT_gpu_shader4 is required");
265                                 stage->locations[var.name] = location;
266                                 var.layout->qualifiers.erase(i);
267                         }
268
269                         if(var.layout->qualifiers.empty())
270                                 var.layout = 0;
271                 }
272         }
273
274         if(var.sampling=="centroid")
275         {
276                 if(!supports_centroid_sampling())
277                         var.sampling = string();
278         }
279         else if(var.sampling=="sample")
280         {
281                 if(!supports_sample_sampling())
282                         var.sampling = string();
283         }
284
285         if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
286         {
287                 if(stage->type==Stage::FRAGMENT && var.interface=="out")
288                 {
289                         frag_out = &var;
290                         nodes_to_remove.insert(&var);
291                 }
292         }
293
294         TraversingVisitor::visit(var);
295 }
296
297 bool LegacyConverter::supports_interface_blocks(const string &iface) const
298 {
299         if(features.gl_api==OPENGL_ES2)
300         {
301                 if(iface=="uniform")
302                         return check_version(Version(3, 0));
303                 else
304                         return check_version(Version(3, 20));
305         }
306         else if(check_version(Version(1, 50)))
307                 return true;
308         else if(iface=="uniform")
309                 return check_extension(&Features::arb_uniform_buffer_object);
310         else
311                 return false;
312 }
313
314 void LegacyConverter::visit(InterfaceBlock &iface)
315 {
316         if(!supports_interface_blocks(iface.interface))
317         {
318                 stage->content.body.splice(uniform_insert_point, iface.members.body);
319                 nodes_to_remove.insert(&iface);
320         }
321 }
322
323 } // namespace SL
324 } // namespace GL
325 } // namespace Msp