gfx/angle/src/compiler/BuiltInFunctionEmulator.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/angle/src/compiler/BuiltInFunctionEmulator.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,481 @@
     1.4 +//
     1.5 +// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
     1.6 +// Use of this source code is governed by a BSD-style license that can be
     1.7 +// found in the LICENSE file.
     1.8 +//
     1.9 +
    1.10 +#include "compiler/BuiltInFunctionEmulator.h"
    1.11 +
    1.12 +#include "compiler/SymbolTable.h"
    1.13 +
    1.14 +namespace {
    1.15 +
    1.16 +// we use macros here instead of function definitions to work around more GLSL
    1.17 +// compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are
    1.18 +// problematic because if the argument has side-effects they will be repeatedly
    1.19 +// evaluated. This is unlikely to show up in real shaders, but is something to
    1.20 +// consider.
    1.21 +const char* kFunctionEmulationVertexSource[] = {
    1.22 +    "#error no emulation for cos(float)",
    1.23 +    "#error no emulation for cos(vec2)",
    1.24 +    "#error no emulation for cos(vec3)",
    1.25 +    "#error no emulation for cos(vec4)",
    1.26 +
    1.27 +    "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
    1.28 +    "#error no emulation for distance(vec2, vec2)",
    1.29 +    "#error no emulation for distance(vec3, vec3)",
    1.30 +    "#error no emulation for distance(vec4, vec4)",
    1.31 +
    1.32 +    "#define webgl_dot_emu(x, y) ((x) * (y))",
    1.33 +    "#error no emulation for dot(vec2, vec2)",
    1.34 +    "#error no emulation for dot(vec3, vec3)",
    1.35 +    "#error no emulation for dot(vec4, vec4)",
    1.36 +
    1.37 +    // |faceforward(N, I, Nref)| is |dot(NRef, I) < 0 ? N : -N|
    1.38 +    "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))",
    1.39 +    "#error no emulation for faceforward(vec2, vec2, vec2)",
    1.40 +    "#error no emulation for faceforward(vec3, vec3, vec3)",
    1.41 +    "#error no emulation for faceforward(vec4, vec4, vec4)",
    1.42 +
    1.43 +    "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
    1.44 +    "#error no emulation for length(vec2)",
    1.45 +    "#error no emulation for length(vec3)",
    1.46 +    "#error no emulation for length(vec4)",
    1.47 +
    1.48 +    "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
    1.49 +    "#error no emulation for normalize(vec2)",
    1.50 +    "#error no emulation for normalize(vec3)",
    1.51 +    "#error no emulation for normalize(vec4)",
    1.52 +
    1.53 +    "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
    1.54 +    "#error no emulation for reflect(vec2, vec2)",
    1.55 +    "#error no emulation for reflect(vec3, vec3)",
    1.56 +    "#error no emulation for reflect(vec4, vec4)"
    1.57 +};
    1.58 +
    1.59 +const char* kFunctionEmulationFragmentSource[] = {
    1.60 +    "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }",
    1.61 +    "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }",
    1.62 +    "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }",
    1.63 +    "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }",
    1.64 +
    1.65 +    "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
    1.66 +    "#error no emulation for distance(vec2, vec2)",
    1.67 +    "#error no emulation for distance(vec3, vec3)",
    1.68 +    "#error no emulation for distance(vec4, vec4)",
    1.69 +
    1.70 +    "#define webgl_dot_emu(x, y) ((x) * (y))",
    1.71 +    "#error no emulation for dot(vec2, vec2)",
    1.72 +    "#error no emulation for dot(vec3, vec3)",
    1.73 +    "#error no emulation for dot(vec4, vec4)",
    1.74 +
    1.75 +    // |faceforward(N, I, Nref)| is |dot(NRef, I) < 0 ? N : -N|
    1.76 +    "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))",
    1.77 +    "#error no emulation for faceforward(vec2, vec2, vec2)",
    1.78 +    "#error no emulation for faceforward(vec3, vec3, vec3)",
    1.79 +    "#error no emulation for faceforward(vec4, vec4, vec4)",
    1.80 +
    1.81 +    "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
    1.82 +    "#error no emulation for length(vec2)",
    1.83 +    "#error no emulation for length(vec3)",
    1.84 +    "#error no emulation for length(vec4)",
    1.85 +
    1.86 +    "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
    1.87 +    "#error no emulation for normalize(vec2)",
    1.88 +    "#error no emulation for normalize(vec3)",
    1.89 +    "#error no emulation for normalize(vec4)",
    1.90 +
    1.91 +    "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
    1.92 +    "#error no emulation for reflect(vec2, vec2)",
    1.93 +    "#error no emulation for reflect(vec3, vec3)",
    1.94 +    "#error no emulation for reflect(vec4, vec4)"
    1.95 +};
    1.96 +
    1.97 +const bool kFunctionEmulationVertexMask[] = {
    1.98 +#if defined(__APPLE__)
    1.99 +    // Work around ATI driver bugs in Mac.
   1.100 +    false, // TFunctionCos1
   1.101 +    false, // TFunctionCos2
   1.102 +    false, // TFunctionCos3
   1.103 +    false, // TFunctionCos4
   1.104 +    true,  // TFunctionDistance1_1
   1.105 +    false, // TFunctionDistance2_2
   1.106 +    false, // TFunctionDistance3_3
   1.107 +    false, // TFunctionDistance4_4
   1.108 +    true,  // TFunctionDot1_1
   1.109 +    false, // TFunctionDot2_2
   1.110 +    false, // TFunctionDot3_3
   1.111 +    false, // TFunctionDot4_4
   1.112 +    true,  // TFunctionFaceForward1_1_1
   1.113 +    false, // TFunctionFaceForward2_2_2
   1.114 +    false, // TFunctionFaceForward3_3_3
   1.115 +    false, // TFunctionFaceForward4_4_4
   1.116 +    true,  // TFunctionLength1
   1.117 +    false, // TFunctionLength2
   1.118 +    false, // TFunctionLength3
   1.119 +    false, // TFunctionLength4
   1.120 +    true,  // TFunctionNormalize1
   1.121 +    false, // TFunctionNormalize2
   1.122 +    false, // TFunctionNormalize3
   1.123 +    false, // TFunctionNormalize4
   1.124 +    true,  // TFunctionReflect1_1
   1.125 +    false, // TFunctionReflect2_2
   1.126 +    false, // TFunctionReflect3_3
   1.127 +    false, // TFunctionReflect4_4
   1.128 +#else
   1.129 +    // Work around D3D driver bug in Win.
   1.130 +    false, // TFunctionCos1
   1.131 +    false, // TFunctionCos2
   1.132 +    false, // TFunctionCos3
   1.133 +    false, // TFunctionCos4
   1.134 +    false, // TFunctionDistance1_1
   1.135 +    false, // TFunctionDistance2_2
   1.136 +    false, // TFunctionDistance3_3
   1.137 +    false, // TFunctionDistance4_4
   1.138 +    false, // TFunctionDot1_1
   1.139 +    false, // TFunctionDot2_2
   1.140 +    false, // TFunctionDot3_3
   1.141 +    false, // TFunctionDot4_4
   1.142 +    false, // TFunctionFaceForward1_1_1
   1.143 +    false, // TFunctionFaceForward2_2_2
   1.144 +    false, // TFunctionFaceForward3_3_3
   1.145 +    false, // TFunctionFaceForward4_4_4
   1.146 +    false, // TFunctionLength1
   1.147 +    false, // TFunctionLength2
   1.148 +    false, // TFunctionLength3
   1.149 +    false, // TFunctionLength4
   1.150 +    false, // TFunctionNormalize1
   1.151 +    false, // TFunctionNormalize2
   1.152 +    false, // TFunctionNormalize3
   1.153 +    false, // TFunctionNormalize4
   1.154 +    false, // TFunctionReflect1_1
   1.155 +    false, // TFunctionReflect2_2
   1.156 +    false, // TFunctionReflect3_3
   1.157 +    false, // TFunctionReflect4_4
   1.158 +#endif
   1.159 +    false  // TFunctionUnknown
   1.160 +};
   1.161 +
   1.162 +const bool kFunctionEmulationFragmentMask[] = {
   1.163 +#if defined(__APPLE__)
   1.164 +    // Work around ATI driver bugs in Mac.
   1.165 +    true,  // TFunctionCos1
   1.166 +    true,  // TFunctionCos2
   1.167 +    true,  // TFunctionCos3
   1.168 +    true,  // TFunctionCos4
   1.169 +    true,  // TFunctionDistance1_1
   1.170 +    false, // TFunctionDistance2_2
   1.171 +    false, // TFunctionDistance3_3
   1.172 +    false, // TFunctionDistance4_4
   1.173 +    true,  // TFunctionDot1_1
   1.174 +    false, // TFunctionDot2_2
   1.175 +    false, // TFunctionDot3_3
   1.176 +    false, // TFunctionDot4_4
   1.177 +    true,  // TFunctionFaceForward1_1_1
   1.178 +    false, // TFunctionFaceForward2_2_2
   1.179 +    false, // TFunctionFaceForward3_3_3
   1.180 +    false, // TFunctionFaceForward4_4_4
   1.181 +    true,  // TFunctionLength1
   1.182 +    false, // TFunctionLength2
   1.183 +    false, // TFunctionLength3
   1.184 +    false, // TFunctionLength4
   1.185 +    true,  // TFunctionNormalize1
   1.186 +    false, // TFunctionNormalize2
   1.187 +    false, // TFunctionNormalize3
   1.188 +    false, // TFunctionNormalize4
   1.189 +    true,  // TFunctionReflect1_1
   1.190 +    false, // TFunctionReflect2_2
   1.191 +    false, // TFunctionReflect3_3
   1.192 +    false, // TFunctionReflect4_4
   1.193 +#else
   1.194 +    // Work around D3D driver bug in Win.
   1.195 +    false, // TFunctionCos1
   1.196 +    false, // TFunctionCos2
   1.197 +    false, // TFunctionCos3
   1.198 +    false, // TFunctionCos4
   1.199 +    false, // TFunctionDistance1_1
   1.200 +    false, // TFunctionDistance2_2
   1.201 +    false, // TFunctionDistance3_3
   1.202 +    false, // TFunctionDistance4_4
   1.203 +    false, // TFunctionDot1_1
   1.204 +    false, // TFunctionDot2_2
   1.205 +    false, // TFunctionDot3_3
   1.206 +    false, // TFunctionDot4_4
   1.207 +    false, // TFunctionFaceForward1_1_1
   1.208 +    false, // TFunctionFaceForward2_2_2
   1.209 +    false, // TFunctionFaceForward3_3_3
   1.210 +    false, // TFunctionFaceForward4_4_4
   1.211 +    false, // TFunctionLength1
   1.212 +    false, // TFunctionLength2
   1.213 +    false, // TFunctionLength3
   1.214 +    false, // TFunctionLength4
   1.215 +    false, // TFunctionNormalize1
   1.216 +    false, // TFunctionNormalize2
   1.217 +    false, // TFunctionNormalize3
   1.218 +    false, // TFunctionNormalize4
   1.219 +    false, // TFunctionReflect1_1
   1.220 +    false, // TFunctionReflect2_2
   1.221 +    false, // TFunctionReflect3_3
   1.222 +    false, // TFunctionReflect4_4
   1.223 +#endif
   1.224 +    false  // TFunctionUnknown
   1.225 +};
   1.226 +
   1.227 +class BuiltInFunctionEmulationMarker : public TIntermTraverser {
   1.228 +public:
   1.229 +    BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
   1.230 +        : mEmulator(emulator)
   1.231 +    {
   1.232 +    }
   1.233 +
   1.234 +    virtual bool visitUnary(Visit visit, TIntermUnary* node)
   1.235 +    {
   1.236 +        if (visit == PreVisit) {
   1.237 +            bool needToEmulate = mEmulator.SetFunctionCalled(
   1.238 +                node->getOp(), node->getOperand()->getType());
   1.239 +            if (needToEmulate)
   1.240 +                node->setUseEmulatedFunction();
   1.241 +        }
   1.242 +        return true;
   1.243 +    }
   1.244 +
   1.245 +    virtual bool visitAggregate(Visit visit, TIntermAggregate* node)
   1.246 +    {
   1.247 +        if (visit == PreVisit) {
   1.248 +            // Here we handle all the built-in functions instead of the ones we
   1.249 +            // currently identified as problematic.
   1.250 +            switch (node->getOp()) {
   1.251 +                case EOpLessThan:
   1.252 +                case EOpGreaterThan:
   1.253 +                case EOpLessThanEqual:
   1.254 +                case EOpGreaterThanEqual:
   1.255 +                case EOpVectorEqual:
   1.256 +                case EOpVectorNotEqual:
   1.257 +                case EOpMod:
   1.258 +                case EOpPow:
   1.259 +                case EOpAtan:
   1.260 +                case EOpMin:
   1.261 +                case EOpMax:
   1.262 +                case EOpClamp:
   1.263 +                case EOpMix:
   1.264 +                case EOpStep:
   1.265 +                case EOpSmoothStep:
   1.266 +                case EOpDistance:
   1.267 +                case EOpDot:
   1.268 +                case EOpCross:
   1.269 +                case EOpFaceForward:
   1.270 +                case EOpReflect:
   1.271 +                case EOpRefract:
   1.272 +                case EOpMul:
   1.273 +                    break;
   1.274 +                default:
   1.275 +                    return true;
   1.276 +            };
   1.277 +            const TIntermSequence& sequence = node->getSequence();
   1.278 +            bool needToEmulate = false;
   1.279 +
   1.280 +            if (sequence.size() == 2) {
   1.281 +                TIntermTyped* param1 = sequence[0]->getAsTyped();
   1.282 +                TIntermTyped* param2 = sequence[1]->getAsTyped();
   1.283 +                if (!param1 || !param2)
   1.284 +                    return true;
   1.285 +                needToEmulate = mEmulator.SetFunctionCalled(
   1.286 +                    node->getOp(), param1->getType(), param2->getType());
   1.287 +            } else if (sequence.size() == 3) {
   1.288 +                TIntermTyped* param1 = sequence[0]->getAsTyped();
   1.289 +                TIntermTyped* param2 = sequence[1]->getAsTyped();
   1.290 +                TIntermTyped* param3 = sequence[2]->getAsTyped();
   1.291 +                if (!param1 || !param2 || !param3)
   1.292 +                    return true;
   1.293 +                needToEmulate = mEmulator.SetFunctionCalled(
   1.294 +                    node->getOp(), param1->getType(), param2->getType(), param3->getType());
   1.295 +            } else {
   1.296 +                return true;
   1.297 +            }
   1.298 +
   1.299 +            if (needToEmulate)
   1.300 +                node->setUseEmulatedFunction();
   1.301 +        }
   1.302 +        return true;
   1.303 +    }
   1.304 +
   1.305 +private:
   1.306 +    BuiltInFunctionEmulator& mEmulator;
   1.307 +};
   1.308 +
   1.309 +}  // anonymous namepsace
   1.310 +
   1.311 +BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType)
   1.312 +{
   1.313 +    if (shaderType == SH_FRAGMENT_SHADER) {
   1.314 +        mFunctionMask = kFunctionEmulationFragmentMask;
   1.315 +        mFunctionSource = kFunctionEmulationFragmentSource;
   1.316 +    } else {
   1.317 +        mFunctionMask = kFunctionEmulationVertexMask;
   1.318 +        mFunctionSource = kFunctionEmulationVertexSource;
   1.319 +    }
   1.320 +}
   1.321 +
   1.322 +bool BuiltInFunctionEmulator::SetFunctionCalled(
   1.323 +    TOperator op, const TType& param)
   1.324 +{
   1.325 +    TBuiltInFunction function = IdentifyFunction(op, param);
   1.326 +    return SetFunctionCalled(function);
   1.327 +}
   1.328 +
   1.329 +bool BuiltInFunctionEmulator::SetFunctionCalled(
   1.330 +    TOperator op, const TType& param1, const TType& param2)
   1.331 +{
   1.332 +    TBuiltInFunction function = IdentifyFunction(op, param1, param2);
   1.333 +    return SetFunctionCalled(function);
   1.334 +}
   1.335 +
   1.336 +bool BuiltInFunctionEmulator::SetFunctionCalled(
   1.337 +    TOperator op, const TType& param1, const TType& param2, const TType& param3)
   1.338 +{
   1.339 +    TBuiltInFunction function = IdentifyFunction(op, param1, param2, param3);
   1.340 +    return SetFunctionCalled(function);
   1.341 +}
   1.342 +
   1.343 +bool BuiltInFunctionEmulator::SetFunctionCalled(
   1.344 +    BuiltInFunctionEmulator::TBuiltInFunction function) {
   1.345 +    if (function == TFunctionUnknown || mFunctionMask[function] == false)
   1.346 +        return false;
   1.347 +    for (size_t i = 0; i < mFunctions.size(); ++i) {
   1.348 +        if (mFunctions[i] == function)
   1.349 +            return true;
   1.350 +    }
   1.351 +    mFunctions.push_back(function);
   1.352 +    return true;
   1.353 +}
   1.354 +
   1.355 +void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
   1.356 +    TInfoSinkBase& out, bool withPrecision) const
   1.357 +{
   1.358 +    if (mFunctions.size() == 0)
   1.359 +        return;
   1.360 +    out << "// BEGIN: Generated code for built-in function emulation\n\n";
   1.361 +    if (withPrecision) {
   1.362 +        out << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
   1.363 +            << "#define webgl_emu_precision highp\n"
   1.364 +            << "#else\n"
   1.365 +            << "#define webgl_emu_precision mediump\n"
   1.366 +            << "#endif\n\n";
   1.367 +    } else {
   1.368 +        out << "#define webgl_emu_precision\n\n";
   1.369 +    }
   1.370 +    for (size_t i = 0; i < mFunctions.size(); ++i) {
   1.371 +        out << mFunctionSource[mFunctions[i]] << "\n\n";
   1.372 +    }
   1.373 +    out << "// END: Generated code for built-in function emulation\n\n";
   1.374 +}
   1.375 +
   1.376 +BuiltInFunctionEmulator::TBuiltInFunction
   1.377 +BuiltInFunctionEmulator::IdentifyFunction(
   1.378 +    TOperator op, const TType& param)
   1.379 +{
   1.380 +    if (param.getNominalSize() > 4)
   1.381 +        return TFunctionUnknown;
   1.382 +    unsigned int function = TFunctionUnknown;
   1.383 +    switch (op) {
   1.384 +        case EOpCos:
   1.385 +            function = TFunctionCos1;
   1.386 +            break;
   1.387 +        case EOpLength:
   1.388 +            function = TFunctionLength1;
   1.389 +            break;
   1.390 +        case EOpNormalize:
   1.391 +            function = TFunctionNormalize1;
   1.392 +            break;
   1.393 +        default:
   1.394 +            break;
   1.395 +    }
   1.396 +    if (function == TFunctionUnknown)
   1.397 +        return TFunctionUnknown;
   1.398 +    if (param.isVector())
   1.399 +        function += param.getNominalSize() - 1;
   1.400 +    return static_cast<TBuiltInFunction>(function);
   1.401 +}
   1.402 +
   1.403 +BuiltInFunctionEmulator::TBuiltInFunction
   1.404 +BuiltInFunctionEmulator::IdentifyFunction(
   1.405 +    TOperator op, const TType& param1, const TType& param2)
   1.406 +{
   1.407 +    // Right now for all the emulated functions with two parameters, the two
   1.408 +    // parameters have the same type.
   1.409 +    if (param1.isVector() != param2.isVector() ||
   1.410 +        param1.getNominalSize() != param2.getNominalSize() ||
   1.411 +        param1.getNominalSize() > 4)
   1.412 +        return TFunctionUnknown;
   1.413 +
   1.414 +    unsigned int function = TFunctionUnknown;
   1.415 +    switch (op) {
   1.416 +        case EOpDistance:
   1.417 +            function = TFunctionDistance1_1;
   1.418 +            break;
   1.419 +        case EOpDot:
   1.420 +            function = TFunctionDot1_1;
   1.421 +            break;
   1.422 +        case EOpReflect:
   1.423 +            function = TFunctionReflect1_1;
   1.424 +            break;
   1.425 +        default:
   1.426 +            break;
   1.427 +    }
   1.428 +    if (function == TFunctionUnknown)
   1.429 +        return TFunctionUnknown;
   1.430 +    if (param1.isVector())
   1.431 +        function += param1.getNominalSize() - 1;
   1.432 +    return static_cast<TBuiltInFunction>(function);
   1.433 +}
   1.434 +
   1.435 +BuiltInFunctionEmulator::TBuiltInFunction
   1.436 +BuiltInFunctionEmulator::IdentifyFunction(
   1.437 +    TOperator op, const TType& param1, const TType& param2, const TType& param3)
   1.438 +{
   1.439 +    // Check that all params have the same type, length,
   1.440 +    // and that they're not too large.
   1.441 +    if (param1.isVector() != param2.isVector() ||
   1.442 +        param2.isVector() != param3.isVector() ||
   1.443 +        param1.getNominalSize() != param2.getNominalSize() ||
   1.444 +        param2.getNominalSize() != param3.getNominalSize() ||
   1.445 +        param1.getNominalSize() > 4)
   1.446 +        return TFunctionUnknown;
   1.447 +
   1.448 +    unsigned int function = TFunctionUnknown;
   1.449 +    switch (op) {
   1.450 +        case EOpFaceForward:
   1.451 +            function = TFunctionFaceForward1_1_1;
   1.452 +            break;
   1.453 +        default:
   1.454 +            break;
   1.455 +    }
   1.456 +    if (function == TFunctionUnknown)
   1.457 +        return TFunctionUnknown;
   1.458 +    if (param1.isVector())
   1.459 +        function += param1.getNominalSize() - 1;
   1.460 +    return static_cast<TBuiltInFunction>(function);
   1.461 +}
   1.462 +
   1.463 +void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
   1.464 +    TIntermNode* root)
   1.465 +{
   1.466 +    ASSERT(root);
   1.467 +
   1.468 +    BuiltInFunctionEmulationMarker marker(*this);
   1.469 +    root->traverse(&marker);
   1.470 +}
   1.471 +
   1.472 +void BuiltInFunctionEmulator::Cleanup()
   1.473 +{
   1.474 +    mFunctions.clear();
   1.475 +}
   1.476 +
   1.477 +//static
   1.478 +TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
   1.479 +    const TString& name)
   1.480 +{
   1.481 +    ASSERT(name[name.length() - 1] == '(');
   1.482 +    return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
   1.483 +}
   1.484 +

mercurial