• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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