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