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