Home | History | Annotate | Download | only in compiler
      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:
    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 
    262 private:
    263     BuiltInFunctionEmulator& mEmulator;
    264 };
    265 
    266 }  // anonymous namepsace
    267 
    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 
    279 bool BuiltInFunctionEmulator::SetFunctionCalled(
    280     TOperator op, const TType& param)
    281 {
    282     TBuiltInFunction function = IdentifyFunction(op, param);
    283     return SetFunctionCalled(function);
    284 }
    285 
    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 
    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 
    305 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
    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
    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 
    385 void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
    386     TIntermNode* root)
    387 {
    388     ASSERT(root);
    389 
    390     BuiltInFunctionEmulationMarker marker(*this);
    391     root->traverse(&marker);
    392 }
    393 
    394 void BuiltInFunctionEmulator::Cleanup()
    395 {
    396     mFunctions.clear();
    397 }
    398 
    399 //static
    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