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