1 //
2 // Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "compiler/BuiltInFunctionEmulator.h"
8
9 #include "compiler/SymbolTable.h"
10
11 namespace {
12
13 // we use macros here instead of function definitions to work around more GLSL
14 // compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are
15 // problematic because if the argument has side-effects they will be repeatedly
16 // evaluated. This is unlikely to show up in real shaders, but is something to
17 // consider.
18 const char* kFunctionEmulationVertexSource[] = {
19 "#error no emulation for cos(float)",
20 "#error no emulation for cos(vec2)",
21 "#error no emulation for cos(vec3)",
22 "#error no emulation for cos(vec4)",
23
24 "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
25 "#error no emulation for distance(vec2, vec2)",
26 "#error no emulation for distance(vec3, vec3)",
27 "#error no emulation for distance(vec4, vec4)",
28
29 "#define webgl_dot_emu(x, y) ((x) * (y))",
30 "#error no emulation for dot(vec2, vec2)",
31 "#error no emulation for dot(vec3, vec3)",
32 "#error no emulation for dot(vec4, vec4)",
33
34 "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
35 "#error no emulation for length(vec2)",
36 "#error no emulation for length(vec3)",
37 "#error no emulation for length(vec4)",
38
39 "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
40 "#error no emulation for normalize(vec2)",
41 "#error no emulation for normalize(vec3)",
42 "#error no emulation for normalize(vec4)",
43
44 "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
45 "#error no emulation for reflect(vec2, vec2)",
46 "#error no emulation for reflect(vec3, vec3)",
47 "#error no emulation for reflect(vec4, vec4)"
48 };
49
50 const char* kFunctionEmulationFragmentSource[] = {
51 "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }",
52 "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }",
53 "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }",
54 "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }",
55
56 "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
57 "#error no emulation for distance(vec2, vec2)",
58 "#error no emulation for distance(vec3, vec3)",
59 "#error no emulation for distance(vec4, vec4)",
60
61 "#define webgl_dot_emu(x, y) ((x) * (y))",
62 "#error no emulation for dot(vec2, vec2)",
63 "#error no emulation for dot(vec3, vec3)",
64 "#error no emulation for dot(vec4, vec4)",
65
66 "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
67 "#error no emulation for length(vec2)",
68 "#error no emulation for length(vec3)",
69 "#error no emulation for length(vec4)",
70
71 "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
72 "#error no emulation for normalize(vec2)",
73 "#error no emulation for normalize(vec3)",
74 "#error no emulation for normalize(vec4)",
75
76 "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
77 "#error no emulation for reflect(vec2, vec2)",
78 "#error no emulation for reflect(vec3, vec3)",
79 "#error no emulation for reflect(vec4, vec4)"
80 };
81
82 const bool kFunctionEmulationVertexMask[] = {
83 #if defined(__APPLE__)
84 // Work around ATI driver bugs in Mac.
85 false, // TFunctionCos1
86 false, // TFunctionCos2
87 false, // TFunctionCos3
88 false, // TFunctionCos4
89 true, // TFunctionDistance1_1
90 false, // TFunctionDistance2_2
91 false, // TFunctionDistance3_3
92 false, // TFunctionDistance4_4
93 true, // TFunctionDot1_1
94 false, // TFunctionDot2_2
95 false, // TFunctionDot3_3
96 false, // TFunctionDot4_4
97 true, // TFunctionLength1
98 false, // TFunctionLength2
99 false, // TFunctionLength3
100 false, // TFunctionLength4
101 true, // TFunctionNormalize1
102 false, // TFunctionNormalize2
103 false, // TFunctionNormalize3
104 false, // TFunctionNormalize4
105 true, // TFunctionReflect1_1
106 false, // TFunctionReflect2_2
107 false, // TFunctionReflect3_3
108 false, // TFunctionReflect4_4
109 #else
110 // Work around D3D driver bug in Win.
111 false, // TFunctionCos1
112 false, // TFunctionCos2
113 false, // TFunctionCos3
114 false, // TFunctionCos4
115 false, // TFunctionDistance1_1
116 false, // TFunctionDistance2_2
117 false, // TFunctionDistance3_3
118 false, // TFunctionDistance4_4
119 false, // TFunctionDot1_1
120 false, // TFunctionDot2_2
121 false, // TFunctionDot3_3
122 false, // TFunctionDot4_4
123 false, // TFunctionLength1
124 false, // TFunctionLength2
125 false, // TFunctionLength3
126 false, // TFunctionLength4
127 false, // TFunctionNormalize1
128 false, // TFunctionNormalize2
129 false, // TFunctionNormalize3
130 false, // TFunctionNormalize4
131 false, // TFunctionReflect1_1
132 false, // TFunctionReflect2_2
133 false, // TFunctionReflect3_3
134 false, // TFunctionReflect4_4
135 #endif
136 false // TFunctionUnknown
137 };
138
139 const bool kFunctionEmulationFragmentMask[] = {
140 #if defined(__APPLE__)
141 // Work around ATI driver bugs in Mac.
142 true, // TFunctionCos1
143 true, // TFunctionCos2
144 true, // TFunctionCos3
145 true, // TFunctionCos4
146 true, // TFunctionDistance1_1
147 false, // TFunctionDistance2_2
148 false, // TFunctionDistance3_3
149 false, // TFunctionDistance4_4
150 true, // TFunctionDot1_1
151 false, // TFunctionDot2_2
152 false, // TFunctionDot3_3
153 false, // TFunctionDot4_4
154 true, // TFunctionLength1
155 false, // TFunctionLength2
156 false, // TFunctionLength3
157 false, // TFunctionLength4
158 true, // TFunctionNormalize1
159 false, // TFunctionNormalize2
160 false, // TFunctionNormalize3
161 false, // TFunctionNormalize4
162 true, // TFunctionReflect1_1
163 false, // TFunctionReflect2_2
164 false, // TFunctionReflect3_3
165 false, // TFunctionReflect4_4
166 #else
167 // Work around D3D driver bug in Win.
168 false, // TFunctionCos1
169 false, // TFunctionCos2
170 false, // TFunctionCos3
171 false, // TFunctionCos4
172 false, // TFunctionDistance1_1
173 false, // TFunctionDistance2_2
174 false, // TFunctionDistance3_3
175 false, // TFunctionDistance4_4
176 false, // TFunctionDot1_1
177 false, // TFunctionDot2_2
178 false, // TFunctionDot3_3
179 false, // TFunctionDot4_4
180 false, // TFunctionLength1
181 false, // TFunctionLength2
182 false, // TFunctionLength3
183 false, // TFunctionLength4
184 false, // TFunctionNormalize1
185 false, // TFunctionNormalize2
186 false, // TFunctionNormalize3
187 false, // TFunctionNormalize4
188 false, // TFunctionReflect1_1
189 false, // TFunctionReflect2_2
190 false, // TFunctionReflect3_3
191 false, // TFunctionReflect4_4
192 #endif
193 false // TFunctionUnknown
194 };
195
196 class BuiltInFunctionEmulationMarker : public TIntermTraverser {
197 public:
BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator & emulator)198 BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
199 : mEmulator(emulator)
200 {
201 }
202
visitUnary(Visit visit,TIntermUnary * node)203 virtual bool visitUnary(Visit visit, TIntermUnary* node)
204 {
205 if (visit == PreVisit) {
206 bool needToEmulate = mEmulator.SetFunctionCalled(
207 node->getOp(), node->getOperand()->getType());
208 if (needToEmulate)
209 node->setUseEmulatedFunction();
210 }
211 return true;
212 }
213
visitAggregate(Visit visit,TIntermAggregate * node)214 virtual bool visitAggregate(Visit visit, TIntermAggregate* node)
215 {
216 if (visit == PreVisit) {
217 // Here we handle all the built-in functions instead of the ones we
218 // currently identified as problematic.
219 switch (node->getOp()) {
220 case EOpLessThan:
221 case EOpGreaterThan:
222 case EOpLessThanEqual:
223 case EOpGreaterThanEqual:
224 case EOpVectorEqual:
225 case EOpVectorNotEqual:
226 case EOpMod:
227 case EOpPow:
228 case EOpAtan:
229 case EOpMin:
230 case EOpMax:
231 case EOpClamp:
232 case EOpMix:
233 case EOpStep:
234 case EOpSmoothStep:
235 case EOpDistance:
236 case EOpDot:
237 case EOpCross:
238 case EOpFaceForward:
239 case EOpReflect:
240 case EOpRefract:
241 case EOpMul:
242 break;
243 default:
244 return true;
245 };
246 const TIntermSequence& sequence = node->getSequence();
247 // Right now we only handle built-in functions with two parameters.
248 if (sequence.size() != 2)
249 return true;
250 TIntermTyped* param1 = sequence[0]->getAsTyped();
251 TIntermTyped* param2 = sequence[1]->getAsTyped();
252 if (!param1 || !param2)
253 return true;
254 bool needToEmulate = mEmulator.SetFunctionCalled(
255 node->getOp(), param1->getType(), param2->getType());
256 if (needToEmulate)
257 node->setUseEmulatedFunction();
258 }
259 return true;
260 }
261
262 private:
263 BuiltInFunctionEmulator& mEmulator;
264 };
265
266 } // anonymous namepsace
267
BuiltInFunctionEmulator(ShShaderType shaderType)268 BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType)
269 {
270 if (shaderType == SH_FRAGMENT_SHADER) {
271 mFunctionMask = kFunctionEmulationFragmentMask;
272 mFunctionSource = kFunctionEmulationFragmentSource;
273 } else {
274 mFunctionMask = kFunctionEmulationVertexMask;
275 mFunctionSource = kFunctionEmulationVertexSource;
276 }
277 }
278
SetFunctionCalled(TOperator op,const TType & param)279 bool BuiltInFunctionEmulator::SetFunctionCalled(
280 TOperator op, const TType& param)
281 {
282 TBuiltInFunction function = IdentifyFunction(op, param);
283 return SetFunctionCalled(function);
284 }
285
SetFunctionCalled(TOperator op,const TType & param1,const TType & param2)286 bool BuiltInFunctionEmulator::SetFunctionCalled(
287 TOperator op, const TType& param1, const TType& param2)
288 {
289 TBuiltInFunction function = IdentifyFunction(op, param1, param2);
290 return SetFunctionCalled(function);
291 }
292
SetFunctionCalled(BuiltInFunctionEmulator::TBuiltInFunction function)293 bool BuiltInFunctionEmulator::SetFunctionCalled(
294 BuiltInFunctionEmulator::TBuiltInFunction function) {
295 if (function == TFunctionUnknown || mFunctionMask[function] == false)
296 return false;
297 for (size_t i = 0; i < mFunctions.size(); ++i) {
298 if (mFunctions[i] == function)
299 return true;
300 }
301 mFunctions.push_back(function);
302 return true;
303 }
304
OutputEmulatedFunctionDefinition(TInfoSinkBase & out,bool withPrecision) const305 void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
306 TInfoSinkBase& out, bool withPrecision) const
307 {
308 if (mFunctions.size() == 0)
309 return;
310 out << "// BEGIN: Generated code for built-in function emulation\n\n";
311 if (withPrecision) {
312 out << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
313 << "#define webgl_emu_precision highp\n"
314 << "#else\n"
315 << "#define webgl_emu_precision mediump\n"
316 << "#endif\n\n";
317 } else {
318 out << "#define webgl_emu_precision\n\n";
319 }
320 for (size_t i = 0; i < mFunctions.size(); ++i) {
321 out << mFunctionSource[mFunctions[i]] << "\n\n";
322 }
323 out << "// END: Generated code for built-in function emulation\n\n";
324 }
325
326 BuiltInFunctionEmulator::TBuiltInFunction
IdentifyFunction(TOperator op,const TType & param)327 BuiltInFunctionEmulator::IdentifyFunction(
328 TOperator op, const TType& param)
329 {
330 if (param.getNominalSize() > 4)
331 return TFunctionUnknown;
332 unsigned int function = TFunctionUnknown;
333 switch (op) {
334 case EOpCos:
335 function = TFunctionCos1;
336 break;
337 case EOpLength:
338 function = TFunctionLength1;
339 break;
340 case EOpNormalize:
341 function = TFunctionNormalize1;
342 break;
343 default:
344 break;
345 }
346 if (function == TFunctionUnknown)
347 return TFunctionUnknown;
348 if (param.isVector())
349 function += param.getNominalSize() - 1;
350 return static_cast<TBuiltInFunction>(function);
351 }
352
353 BuiltInFunctionEmulator::TBuiltInFunction
IdentifyFunction(TOperator op,const TType & param1,const TType & param2)354 BuiltInFunctionEmulator::IdentifyFunction(
355 TOperator op, const TType& param1, const TType& param2)
356 {
357 // Right now for all the emulated functions with two parameters, the two
358 // parameters have the same type.
359 if (param1.isVector() != param2.isVector() ||
360 param1.getNominalSize() != param2.getNominalSize() ||
361 param1.getNominalSize() > 4)
362 return TFunctionUnknown;
363
364 unsigned int function = TFunctionUnknown;
365 switch (op) {
366 case EOpDistance:
367 function = TFunctionDistance1_1;
368 break;
369 case EOpDot:
370 function = TFunctionDot1_1;
371 break;
372 case EOpReflect:
373 function = TFunctionReflect1_1;
374 break;
375 default:
376 break;
377 }
378 if (function == TFunctionUnknown)
379 return TFunctionUnknown;
380 if (param1.isVector())
381 function += param1.getNominalSize() - 1;
382 return static_cast<TBuiltInFunction>(function);
383 }
384
MarkBuiltInFunctionsForEmulation(TIntermNode * root)385 void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
386 TIntermNode* root)
387 {
388 ASSERT(root);
389
390 BuiltInFunctionEmulationMarker marker(*this);
391 root->traverse(&marker);
392 }
393
Cleanup()394 void BuiltInFunctionEmulator::Cleanup()
395 {
396 mFunctions.clear();
397 }
398
399 //static
GetEmulatedFunctionName(const TString & name)400 TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
401 const TString& name)
402 {
403 ASSERT(name[name.length() - 1] == '(');
404 return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
405 }
406
407