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 "angle_gl.h"
8#include "compiler/translator/BuiltInFunctionEmulator.h"
9#include "compiler/translator/SymbolTable.h"
10
11namespace {
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.
18const 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
50const 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
82const 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
139const 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
196class BuiltInFunctionEmulationMarker : public TIntermTraverser {
197public:
198    BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
199        : mEmulator(emulator)
200    {
201    }
202
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
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
262private:
263    BuiltInFunctionEmulator& mEmulator;
264};
265
266}  // anonymous namepsace
267
268BuiltInFunctionEmulator::BuiltInFunctionEmulator(sh::GLenum shaderType)
269{
270    if (shaderType == GL_FRAGMENT_SHADER) {
271        mFunctionMask = kFunctionEmulationFragmentMask;
272        mFunctionSource = kFunctionEmulationFragmentSource;
273    } else {
274        mFunctionMask = kFunctionEmulationVertexMask;
275        mFunctionSource = kFunctionEmulationVertexSource;
276    }
277}
278
279bool BuiltInFunctionEmulator::SetFunctionCalled(
280    TOperator op, const TType& param)
281{
282    TBuiltInFunction function = IdentifyFunction(op, param);
283    return SetFunctionCalled(function);
284}
285
286bool 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
293bool 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
305void 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
326BuiltInFunctionEmulator::TBuiltInFunction
327BuiltInFunctionEmulator::IdentifyFunction(
328    TOperator op, const TType& param)
329{
330    if (param.getNominalSize() > 4 || param.getSecondarySize() > 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
353BuiltInFunctionEmulator::TBuiltInFunction
354BuiltInFunctionEmulator::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.getNominalSize()     != param2.getNominalSize()   ||
360        param1.getSecondarySize()   != param2.getSecondarySize() ||
361        param1.getNominalSize() > 4 || param1.getSecondarySize() > 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
385void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
386    TIntermNode* root)
387{
388    ASSERT(root);
389
390    BuiltInFunctionEmulationMarker marker(*this);
391    root->traverse(&marker);
392}
393
394void BuiltInFunctionEmulator::Cleanup()
395{
396    mFunctions.clear();
397}
398
399//static
400TString 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