]> git.tdb.fi Git - libs/gl.git/blob - source/glsl/compatibility.cpp
Reduce coupling between the GLSL compiler and the graphics engine
[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
6 using namespace std;
7
8 namespace Msp {
9 namespace GL {
10 namespace SL {
11
12 DefaultPrecisionGenerator::DefaultPrecisionGenerator():
13         stage(0)
14 { }
15
16 void DefaultPrecisionGenerator::apply(Stage &s)
17 {
18         stage = &s;
19         visit(s.content);
20 }
21
22 void DefaultPrecisionGenerator::visit(Block &block)
23 {
24         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
25         {
26                 if(&block==&stage->content)
27                         insert_point = i;
28                 (*i)->visit(*this);
29         }
30 }
31
32 void DefaultPrecisionGenerator::visit(Precision &prec)
33 {
34         have_default.insert(prec.type);
35 }
36
37 void DefaultPrecisionGenerator::visit(VariableDeclaration &var)
38 {
39         if(var.type_declaration)
40                 return;
41
42         string type = var.type;
43         if(!type.compare(0, 3, "vec") || !type.compare(0, 3, "mat"))
44                 type = "float";
45         else if(!type.compare(0, 3, "ivec") || type=="uint")
46                 type = "int";
47
48         if(!have_default.count(type))
49         {
50                 Precision *prec = new Precision;
51                 if(!type.compare(0, 7, "sampler"))
52                         prec->precision = "lowp";
53                 else if(stage->type==Stage::FRAGMENT)
54                         prec->precision = "mediump";
55                 else
56                         prec->precision = "highp";
57                 prec->type = type;
58                 stage->content.body.insert(insert_point, prec);
59
60                 have_default.insert(type);
61         }
62 }
63
64
65 void PrecisionRemover::apply(Stage &stage)
66 {
67         visit(stage.content);
68         NodeRemover().apply(stage, nodes_to_remove);
69 }
70
71 void PrecisionRemover::visit(Precision &prec)
72 {
73         nodes_to_remove.insert(&prec);
74 }
75
76 void PrecisionRemover::visit(VariableDeclaration &var)
77 {
78         var.precision.clear();
79 }
80
81
82 LegacyConverter::LegacyConverter():
83         frag_out(0)
84 { }
85
86 void LegacyConverter::apply(Stage &s, const Features &feat)
87 {
88         stage = &s;
89         features = feat;
90         visit(s.content);
91 }
92
93 void LegacyConverter::visit(Block &block)
94 {
95         for(NodeList<Statement>::iterator i=block.body.begin(); i!=block.body.end(); ++i)
96         {
97                 if(&block==&stage->content)
98                         uniform_insert_point = i;
99                 (*i)->visit(*this);
100         }
101 }
102
103 bool LegacyConverter::check_version(const Version &feature_version) const
104 {
105         if(features.glsl_version<feature_version)
106                 return false;
107         else if(stage->required_features.glsl_version<feature_version)
108                 stage->required_features.glsl_version = feature_version;
109
110         return true;
111 }
112
113 bool LegacyConverter::check_extension(bool Features::*extension) const
114 {
115         if(!(features.*extension))
116                 return false;
117
118         stage->required_features.*extension = true;
119
120         return true;
121 }
122
123 bool LegacyConverter::supports_unified_interface_syntax() const
124 {
125         if(features.gl_api==OPENGL_ES2)
126                 return check_version(Version(3, 0));
127         else
128                 return check_version(Version(1, 30));
129 }
130
131 void LegacyConverter::visit(VariableReference &var)
132 {
133         if(var.declaration==frag_out && !supports_unified_interface_syntax())
134         {
135                 var.name = "gl_FragColor";
136                 var.declaration = 0;
137                 type = "vec4";
138         }
139         else if(var.declaration)
140                 type = var.declaration->type;
141         else
142                 type = string();
143 }
144
145 void LegacyConverter::visit(Assignment &assign)
146 {
147         TraversingVisitor::visit(assign);
148         if(assign.target_declaration==frag_out && !supports_unified_interface_syntax())
149                 assign.target_declaration = 0;
150 }
151
152 bool LegacyConverter::supports_unified_sampling_functions() const
153 {
154         if(features.gl_api==OPENGL_ES2)
155                 return check_version(Version(3, 0));
156         else
157                 return check_version(Version(1, 30));
158 }
159
160 void LegacyConverter::visit(FunctionCall &call)
161 {
162         if(call.name=="texture")
163         {
164                 string sampler_type;
165                 type = string();
166                 NodeArray<Expression>::iterator i = call.arguments.begin();
167                 if(i!=call.arguments.end())
168                 {
169                         (*i)->visit(*this);
170                         sampler_type = type;
171
172                         for(; i!=call.arguments.end(); ++i)
173                                 (*i)->visit(*this);
174                 }
175
176                 if(!supports_unified_sampling_functions())
177                 {
178                         if(sampler_type=="sampler1D")
179                                 call.name = "texture1D";
180                         else if(sampler_type=="sampler2D")
181                                 call.name = "texture2D";
182                         else if(sampler_type=="sampler3D")
183                                 call.name = "texture3D";
184                         else if(sampler_type=="samplerCube")
185                                 call.name = "textureCube";
186                         else if(sampler_type=="sampler1DShadow")
187                                 call.name = "shadow1D";
188                         else if(sampler_type=="sampler2DShadow")
189                                 call.name = "shadow2D";
190                         else if(sampler_type=="sampler1DArray")
191                         {
192                                 check_extension(&Features::ext_texture_array);
193                                 call.name = "texture1DArray";
194                         }
195                         else if(sampler_type=="sampler2DArray")
196                         {
197                                 check_extension(&Features::ext_texture_array);
198                                 call.name = "texture2DArray";
199                         }
200                         else if(sampler_type=="sampler1DArrayShadow")
201                         {
202                                 check_extension(&Features::ext_texture_array);
203                                 call.name = "shadow1DArray";
204                         }
205                         else if(sampler_type=="sampler2DArrayShadow")
206                         {
207                                 check_extension(&Features::ext_texture_array);
208                                 call.name = "shadow2DArray";
209                         }
210                 }
211         }
212         else
213                 TraversingVisitor::visit(call);
214 }
215
216 bool LegacyConverter::supports_interface_layouts() const
217 {
218         if(features.gl_api==OPENGL_ES2)
219                 return check_version(Version(3, 0));
220         else if(check_version(Version(3, 30)))
221                 return true;
222         else
223                 return check_extension(&Features::arb_explicit_attrib_location);
224 }
225
226 bool LegacyConverter::supports_centroid_sampling() const
227 {
228         if(features.gl_api==OPENGL_ES2)
229                 return check_version(Version(3, 0));
230         else if(check_version(Version(1, 20)))
231                 return true;
232         else
233                 return check_extension(&Features::ext_gpu_shader4);
234 }
235
236 bool LegacyConverter::supports_sample_sampling() const
237 {
238         if(features.gl_api==OPENGL_ES2)
239                 return check_version(Version(3, 20));
240         else if(check_version(Version(4, 0)))
241                 return true;
242         else
243                 return check_extension(&Features::arb_gpu_shader5);
244 }
245
246 void LegacyConverter::visit(VariableDeclaration &var)
247 {
248         if(var.layout && !supports_interface_layouts())
249         {
250                 vector<Layout::Qualifier>::iterator i;
251                 for(i=var.layout->qualifiers.begin(); (i!=var.layout->qualifiers.end() && i->identifier!="location"); ++i) ;
252                 if(i!=var.layout->qualifiers.end())
253                 {
254                         unsigned location = lexical_cast<unsigned>(i->value);
255                         if(stage->type==Stage::VERTEX && var.interface=="in")
256                         {
257                                 stage->locations[var.name] = location;
258                                 var.layout->qualifiers.erase(i);
259                         }
260                         else if(stage->type==Stage::FRAGMENT && var.interface=="out")
261                         {
262                                 if(location!=0)
263                                         check_extension(&Features::ext_gpu_shader4);
264                                 stage->locations[var.name] = location;
265                                 var.layout->qualifiers.erase(i);
266                         }
267
268                         if(var.layout->qualifiers.empty())
269                                 var.layout = 0;
270                 }
271         }
272
273         if(var.sampling=="centroid")
274         {
275                 if(!supports_centroid_sampling())
276                         var.sampling = string();
277         }
278         else if(var.sampling=="sample")
279         {
280                 if(!supports_sample_sampling())
281                         var.sampling = string();
282         }
283
284         if((var.interface=="in" || var.interface=="out") && !supports_unified_interface_syntax())
285         {
286                 if(stage->type==Stage::FRAGMENT && var.interface=="out")
287                 {
288                         frag_out = &var;
289                         nodes_to_remove.insert(&var);
290                 }
291         }
292
293         TraversingVisitor::visit(var);
294 }
295
296 bool LegacyConverter::supports_interface_blocks(const string &iface) const
297 {
298         if(features.gl_api==OPENGL_ES2)
299         {
300                 if(iface=="uniform")
301                         return check_version(Version(3, 0));
302                 else
303                         return check_version(Version(3, 20));
304         }
305         else if(check_version(Version(1, 50)))
306                 return true;
307         else if(iface=="uniform")
308                 return check_extension(&Features::arb_uniform_buffer_object);
309         else
310                 return false;
311 }
312
313 void LegacyConverter::visit(InterfaceBlock &iface)
314 {
315         if(!supports_interface_blocks(iface.interface))
316         {
317                 stage->content.body.splice(uniform_insert_point, iface.members.body);
318                 nodes_to_remove.insert(&iface);
319         }
320 }
321
322 } // namespace SL
323 } // namespace GL
324 } // namespace Msp