Add/remove more functions to use shims.

Remove normalize and add cos instead to avoid a crash in Mac with ATI cards (angle bug 193, 202).

Also add atan and mod as it's also buggy on Mac/Win with NVIDIA cards.

Also, trying to minimize emulated functions by adding masks for fragment/vertex shaders.

ANGLEBUG=196
Review URL: http://codereview.appspot.com/4992047

git-svn-id: https://angleproject.googlecode.com/svn/trunk@748 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/BuiltInFunctionEmulator.cpp b/src/compiler/BuiltInFunctionEmulator.cpp
index 86f67f4..f44a044 100644
--- a/src/compiler/BuiltInFunctionEmulator.cpp
+++ b/src/compiler/BuiltInFunctionEmulator.cpp
@@ -11,20 +11,110 @@
 namespace {
 
 const char* kFunctionEmulationSource[] = {
-    "float webgl_normalize_emu(float a) { return normalize(a) * 1; }",
-    "vec2 webgl_normalize_emu(vec2 a) { return normalize(a) * 1; }",
-    "vec3 webgl_normalize_emu(vec3 a) { return normalize(a) * 1; }",
-    "vec4 webgl_normalize_emu(vec4 a) { return normalize(a) * 1; }",
     "float webgl_abs_emu(float a) { float rt = abs(a); if (rt < 0.0) rt = 0.0;  return rt; }",
     "vec2 webgl_abs_emu(vec2 a) { vec2 rt = abs(a); if (rt[0] < 0.0) rt[0] = 0.0;  return rt; }",
     "vec3 webgl_abs_emu(vec3 a) { vec3 rt = abs(a); if (rt[0] < 0.0) rt[0] = 0.0;  return rt; }",
     "vec4 webgl_abs_emu(vec4 a) { vec4 rt = abs(a); if (rt[0] < 0.0) rt[0] = 0.0;  return rt; }",
+
+    "float webgl_atan_emu(float y, float x) { float rt = atan(y, x); if (rt > 2.0) rt = 0.0;  return rt; }",
+    "vec2 webgl_atan_emu(vec2 y, vec2 x) { vec2 rt = atan(y, x); if (rt[0] > 2.0) rt[0] = 0.0;  return rt; }",
+    "vec3 webgl_atan_emu(vec3 y, vec3 x) { vec3 rt = atan(y, x); if (rt[0] > 2.0) rt[0] = 0.0;  return rt; }",
+    "vec4 webgl_atan_emu(vec4 y, vec4 x) { vec4 rt = atan(y, x); if (rt[0] > 2.0) rt[0] = 0.0;  return rt; }",
+    "float webgl_atan_emu(float y_over_x) { float rt = atan(y_over_x); if (rt > 2.0) rt = 0.0;  return rt; }",
+    "vec2 webgl_atan_emu(vec2 y_over_x) { vec2 rt = atan(y_over_x); if (rt[0] > 2.0) rt[0] = 0.0;  return rt; }",
+    "vec3 webgl_atan_emu(vec3 y_over_x) { vec3 rt = atan(y_over_x); if (rt[0] > 2.0) rt[0] = 0.0;  return rt; }",
+    "vec4 webgl_atan_emu(vec4 y_over_x) { vec4 rt = atan(y_over_x); if (rt[0] > 2.0) rt[0] = 0.0;  return rt; }",
+
+    "float webgl_cos_emu(float a) { return cos(a); }",
+    "vec2 webgl_cos_emu(vec2 a) { return cos(a); }",
+    "vec3 webgl_cos_emu(vec3 a) { return cos(a); }",
+    "vec4 webgl_cos_emu(vec4 a) { return cos(a); }",
+
+    "float webgl_mod_emu(float x, float y) { float rt = mod(x, y); if (rt > x) rt = 0.0;  return rt; }",
+    "vec2 webgl_mod_emu(vec2 x, vec2 y) { vec2 rt = mod(x, y); if (rt[0] > x[0]) rt[0] = 0.0;  return rt; }",
+    "vec3 webgl_mod_emu(vec3 x, vec3 y) { vec3 rt = mod(x, y); if (rt[0] > x[0]) rt[0] = 0.0;  return rt; }",
+    "vec4 webgl_mod_emu(vec4 x, vec4 y) { vec4 rt = mod(x, y); if (rt[0] > x[0]) rt[0] = 0.0;  return rt; }",
+
     "float webgl_sign_emu(float a) { float rt = sign(a); if (rt > 1.0) rt = 1.0;  return rt; }",
     "vec2 webgl_sign_emu(vec2 a) { float rt = sign(a); if (rt[0] > 1.0) rt[0] = 1.0;  return rt; }",
     "vec3 webgl_sign_emu(vec3 a) { float rt = sign(a); if (rt[0] > 1.0) rt[0] = 1.0;  return rt; }",
     "vec4 webgl_sign_emu(vec4 a) { float rt = sign(a); if (rt[0] > 1.0) rt[0] = 1.0;  return rt; }",
 };
 
+const bool kFunctionEmulationVertexMask[] = {
+    true,  // TFunctionAbs1
+    false, // TFunctionAbs2
+    false, // TFunctionAbs3
+    false, // TFunctionAbs4
+
+    true,  // TFunctionAtan1
+    false, // TFunctionAtan2
+    false, // TFunctionAtan3
+    false, // TFunctionAtan4
+    false, // TFunctionAtan1_1
+    true,  // TFunctionAtan2_2
+    true,  // TFunctionAtan3_3
+    true,  // TFunctionAtan4_4
+
+    false, // TFunctionCos1
+    false, // TFunctionCos2
+    false, // TFunctionCos3
+    false, // TFunctionCos4
+
+    false, // TFunctionMod1_1
+    true,  // TFunctionMod2_2
+    true,  // TFunctionMod3_3
+    true,  // TFunctionMod4_4
+
+    true,  // TFunctionSign1
+    false, // TFunctionSign2
+    false, // TFunctionSign3
+    false, // TFunctionSign4
+
+    false  // TFunctionUnknown
+};
+
+const bool kFunctionEmulationFragmentMask[] = {
+    false, // TFunctionAbs1
+    false, // TFunctionAbs2
+    false, // TFunctionAbs3
+    false, // TFunctionAbs4
+
+    false, // TFunctionAtan1
+    false, // TFunctionAtan2
+    false, // TFunctionAtan3
+    false, // TFunctionAtan4
+    false, // TFunctionAtan1_1
+    false, // TFunctionAtan2_2
+    false, // TFunctionAtan3_3
+    false, // TFunctionAtan4_4
+
+#if defined(__APPLE__)
+    // Work around a ATI driver bug in Mac that causes crashes.
+    true,  // TFunctionCos1
+    true,  // TFunctionCos2
+    true,  // TFunctionCos3
+    true,  // TFunctionCos4
+#else
+    false, // TFunctionCos1
+    false, // TFunctionCos2
+    false, // TFunctionCos3
+    false, // TFunctionCos4
+#endif
+
+    false, // TFunctionMod1_1
+    false, // TFunctionMod2_2
+    false, // TFunctionMod3_3
+    false, // TFunctionMod4_4
+
+    false, // TFunctionSign1
+    false, // TFunctionSign2
+    false, // TFunctionSign3
+    false, // TFunctionSign4
+
+    false  // TFunctionUnknown
+};
+
 class BuiltInFunctionEmulationMarker : public TIntermTraverser {
 public:
     BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
@@ -43,15 +133,67 @@
         return true;
     }
 
+    virtual bool visitAggregate(Visit visit, TIntermAggregate* node)
+    {
+        if (visit == PreVisit) {
+            // Here we handle all the built-in functions instead of the ones we
+            // currently identified as problematic.
+            switch (node->getOp()) {
+                case EOpLessThan:
+                case EOpGreaterThan:
+                case EOpLessThanEqual:
+                case EOpGreaterThanEqual:
+                case EOpVectorEqual:
+                case EOpVectorNotEqual:
+                case EOpMod:
+                case EOpPow:
+                case EOpAtan:
+                case EOpMin:
+                case EOpMax:
+                case EOpClamp:
+                case EOpMix:
+                case EOpStep:
+                case EOpSmoothStep:
+                case EOpDistance:
+                case EOpDot:
+                case EOpCross:
+                case EOpFaceForward:
+                case EOpReflect:
+                case EOpRefract:
+                case EOpMul:
+                    break;
+                default:
+                    return true;
+            };
+            const TIntermSequence& sequence = node->getSequence();
+            // Right now we only handle built-in functions with two parameters.
+            if (sequence.size() != 2)
+                return true;
+            TIntermTyped* param1 = sequence[0]->getAsTyped();
+            TIntermTyped* param2 = sequence[1]->getAsTyped();
+            if (!param1 || !param2)
+                return true;
+            bool needToEmulate = mEmulator.SetFunctionCalled(
+                node->getOp(), param1->getType(), param2->getType());
+            if (needToEmulate)
+                node->setUseEmulatedFunction();
+        }
+        return true;
+    }
+
 private:
     BuiltInFunctionEmulator& mEmulator;
 };
 
 }  // anonymous namepsace
 
-BuiltInFunctionEmulator::BuiltInFunctionEmulator()
+BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType)
     : mFunctionGroupMask(TFunctionGroupAll)
 {
+    if (shaderType == SH_FRAGMENT_SHADER)
+        mFunctionMask = kFunctionEmulationFragmentMask;
+    else
+        mFunctionMask = kFunctionEmulationVertexMask;
 }
 
 void BuiltInFunctionEmulator::SetFunctionGroupMask(
@@ -61,25 +203,28 @@
 }
 
 bool BuiltInFunctionEmulator::SetFunctionCalled(
-    TOperator op, const TType& returnType)
+    TOperator op, const TType& param)
 {
-    TBuiltInFunction function = IdentifyFunction(op, returnType);
-    if (function == TFunctionUnknown)
+    TBuiltInFunction function = IdentifyFunction(op, param);
+    return SetFunctionCalled(function);
+}
+
+bool BuiltInFunctionEmulator::SetFunctionCalled(
+    TOperator op, const TType& param1, const TType& param2)
+{
+    TBuiltInFunction function = IdentifyFunction(op, param1, param2);
+    return SetFunctionCalled(function);
+}
+
+bool BuiltInFunctionEmulator::SetFunctionCalled(
+    BuiltInFunctionEmulator::TBuiltInFunction function) {
+    if (function == TFunctionUnknown || mFunctionMask[function] == false)
         return false;
     for (size_t i = 0; i < mFunctions.size(); ++i) {
         if (mFunctions[i] == function)
             return true;
     }
     switch (function) {
-        case TFunctionNormalize1:
-        case TFunctionNormalize2:
-        case TFunctionNormalize3:
-        case TFunctionNormalize4:
-            if (mFunctionGroupMask & TFunctionGroupNormalize) {
-                mFunctions.push_back(function);
-                return true;
-            }
-            break;
         case TFunctionAbs1:
         case TFunctionAbs2:
         case TFunctionAbs3:
@@ -89,6 +234,37 @@
                 return true;
             }
             break;
+        case TFunctionAtan1:
+        case TFunctionAtan2:
+        case TFunctionAtan3:
+        case TFunctionAtan4:
+        case TFunctionAtan1_1:
+        case TFunctionAtan2_2:
+        case TFunctionAtan3_3:
+        case TFunctionAtan4_4:
+            if (mFunctionGroupMask & TFunctionGroupAtan) {
+                mFunctions.push_back(function);
+                return true;
+            }
+            break;
+        case TFunctionCos1:
+        case TFunctionCos2:
+        case TFunctionCos3:
+        case TFunctionCos4:
+            if (mFunctionGroupMask & TFunctionGroupCos) {
+                mFunctions.push_back(function);
+                return true;
+            }
+            break;
+        case TFunctionMod1_1:
+        case TFunctionMod2_2:
+        case TFunctionMod3_3:
+        case TFunctionMod4_4:
+            if (mFunctionGroupMask & TFunctionGroupMod) {
+                mFunctions.push_back(function);
+                return true;
+            }
+            break;
         case TFunctionSign1:
         case TFunctionSign2:
         case TFunctionSign3:
@@ -125,20 +301,62 @@
 }
 
 BuiltInFunctionEmulator::TBuiltInFunction
-BuiltInFunctionEmulator::IdentifyFunction(TOperator op, const TType& returnType)
+BuiltInFunctionEmulator::IdentifyFunction(
+    TOperator op, const TType& param)
 {
     unsigned int function = TFunctionUnknown;
-    if (op == EOpNormalize)
-        function = TFunctionNormalize1;
-    else if (op == EOpAbs)
-        function = TFunctionAbs1;
-    else if (op == EOpSign)
-        function = TFunctionSign1;
-    else
-        return static_cast<TBuiltInFunction>(function);
+    switch (op) {
+        case EOpAbs:
+            function = TFunctionAbs1;
+            break;
+        case EOpAtan:
+            function = TFunctionAtan1;
+            break;
+        case EOpCos:
+            function = TFunctionCos1;
+            break;
+        case EOpSign:
+            function = TFunctionSign1;
+            break;
+        default:
+            break;
+    }
+    if (function == TFunctionUnknown)
+        return TFunctionUnknown;
+    if (param.isVector())
+        function += param.getNominalSize() - 1;
+    return static_cast<TBuiltInFunction>(function);
+}
 
-    if (returnType.isVector())
-        function += returnType.getNominalSize() - 1;
+BuiltInFunctionEmulator::TBuiltInFunction
+BuiltInFunctionEmulator::IdentifyFunction(
+    TOperator op, const TType& param1, const TType& param2)
+{
+    // Right now for all the emulated functions with two parameters, the two
+    // parameters have the same type.
+    if (param1.isVector() != param2.isVector() ||
+        param1.getNominalSize() != param2.getNominalSize() ||
+        param1.getNominalSize() > 4)
+        return TFunctionUnknown;
+
+    unsigned int function = TFunctionUnknown;
+    switch (op) {
+        case EOpAtan:
+            function = TFunctionAtan1_1;
+            break;
+        case EOpMod:
+            function = TFunctionMod1_1;
+            break;
+        case EOpSign:
+            function = TFunctionSign1;
+            break;
+        default:
+            break;
+    }
+    if (function == TFunctionUnknown)
+        return TFunctionUnknown;
+    if (param1.isVector())
+        function += param1.getNominalSize() - 1;
     return static_cast<TBuiltInFunction>(function);
 }