michael@0: // michael@0: // Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: #include "compiler/BuiltInFunctionEmulator.h" michael@0: michael@0: #include "compiler/SymbolTable.h" michael@0: michael@0: namespace { michael@0: michael@0: // we use macros here instead of function definitions to work around more GLSL michael@0: // compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are michael@0: // problematic because if the argument has side-effects they will be repeatedly michael@0: // evaluated. This is unlikely to show up in real shaders, but is something to michael@0: // consider. michael@0: const char* kFunctionEmulationVertexSource[] = { michael@0: "#error no emulation for cos(float)", michael@0: "#error no emulation for cos(vec2)", michael@0: "#error no emulation for cos(vec3)", michael@0: "#error no emulation for cos(vec4)", michael@0: michael@0: "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))", michael@0: "#error no emulation for distance(vec2, vec2)", michael@0: "#error no emulation for distance(vec3, vec3)", michael@0: "#error no emulation for distance(vec4, vec4)", michael@0: michael@0: "#define webgl_dot_emu(x, y) ((x) * (y))", michael@0: "#error no emulation for dot(vec2, vec2)", michael@0: "#error no emulation for dot(vec3, vec3)", michael@0: "#error no emulation for dot(vec4, vec4)", michael@0: michael@0: // |faceforward(N, I, Nref)| is |dot(NRef, I) < 0 ? N : -N| michael@0: "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))", michael@0: "#error no emulation for faceforward(vec2, vec2, vec2)", michael@0: "#error no emulation for faceforward(vec3, vec3, vec3)", michael@0: "#error no emulation for faceforward(vec4, vec4, vec4)", michael@0: michael@0: "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))", michael@0: "#error no emulation for length(vec2)", michael@0: "#error no emulation for length(vec3)", michael@0: "#error no emulation for length(vec4)", michael@0: michael@0: "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))", michael@0: "#error no emulation for normalize(vec2)", michael@0: "#error no emulation for normalize(vec3)", michael@0: "#error no emulation for normalize(vec4)", michael@0: michael@0: "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))", michael@0: "#error no emulation for reflect(vec2, vec2)", michael@0: "#error no emulation for reflect(vec3, vec3)", michael@0: "#error no emulation for reflect(vec4, vec4)" michael@0: }; michael@0: michael@0: const char* kFunctionEmulationFragmentSource[] = { michael@0: "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }", michael@0: "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }", michael@0: "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }", michael@0: "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }", michael@0: michael@0: "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))", michael@0: "#error no emulation for distance(vec2, vec2)", michael@0: "#error no emulation for distance(vec3, vec3)", michael@0: "#error no emulation for distance(vec4, vec4)", michael@0: michael@0: "#define webgl_dot_emu(x, y) ((x) * (y))", michael@0: "#error no emulation for dot(vec2, vec2)", michael@0: "#error no emulation for dot(vec3, vec3)", michael@0: "#error no emulation for dot(vec4, vec4)", michael@0: michael@0: // |faceforward(N, I, Nref)| is |dot(NRef, I) < 0 ? N : -N| michael@0: "#define webgl_faceforward_emu(N, I, Nref) (((Nref) * (I) < 0.0) ? (N) : -(N))", michael@0: "#error no emulation for faceforward(vec2, vec2, vec2)", michael@0: "#error no emulation for faceforward(vec3, vec3, vec3)", michael@0: "#error no emulation for faceforward(vec4, vec4, vec4)", michael@0: michael@0: "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))", michael@0: "#error no emulation for length(vec2)", michael@0: "#error no emulation for length(vec3)", michael@0: "#error no emulation for length(vec4)", michael@0: michael@0: "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))", michael@0: "#error no emulation for normalize(vec2)", michael@0: "#error no emulation for normalize(vec3)", michael@0: "#error no emulation for normalize(vec4)", michael@0: michael@0: "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))", michael@0: "#error no emulation for reflect(vec2, vec2)", michael@0: "#error no emulation for reflect(vec3, vec3)", michael@0: "#error no emulation for reflect(vec4, vec4)" michael@0: }; michael@0: michael@0: const bool kFunctionEmulationVertexMask[] = { michael@0: #if defined(__APPLE__) michael@0: // Work around ATI driver bugs in Mac. michael@0: false, // TFunctionCos1 michael@0: false, // TFunctionCos2 michael@0: false, // TFunctionCos3 michael@0: false, // TFunctionCos4 michael@0: true, // TFunctionDistance1_1 michael@0: false, // TFunctionDistance2_2 michael@0: false, // TFunctionDistance3_3 michael@0: false, // TFunctionDistance4_4 michael@0: true, // TFunctionDot1_1 michael@0: false, // TFunctionDot2_2 michael@0: false, // TFunctionDot3_3 michael@0: false, // TFunctionDot4_4 michael@0: true, // TFunctionFaceForward1_1_1 michael@0: false, // TFunctionFaceForward2_2_2 michael@0: false, // TFunctionFaceForward3_3_3 michael@0: false, // TFunctionFaceForward4_4_4 michael@0: true, // TFunctionLength1 michael@0: false, // TFunctionLength2 michael@0: false, // TFunctionLength3 michael@0: false, // TFunctionLength4 michael@0: true, // TFunctionNormalize1 michael@0: false, // TFunctionNormalize2 michael@0: false, // TFunctionNormalize3 michael@0: false, // TFunctionNormalize4 michael@0: true, // TFunctionReflect1_1 michael@0: false, // TFunctionReflect2_2 michael@0: false, // TFunctionReflect3_3 michael@0: false, // TFunctionReflect4_4 michael@0: #else michael@0: // Work around D3D driver bug in Win. michael@0: false, // TFunctionCos1 michael@0: false, // TFunctionCos2 michael@0: false, // TFunctionCos3 michael@0: false, // TFunctionCos4 michael@0: false, // TFunctionDistance1_1 michael@0: false, // TFunctionDistance2_2 michael@0: false, // TFunctionDistance3_3 michael@0: false, // TFunctionDistance4_4 michael@0: false, // TFunctionDot1_1 michael@0: false, // TFunctionDot2_2 michael@0: false, // TFunctionDot3_3 michael@0: false, // TFunctionDot4_4 michael@0: false, // TFunctionFaceForward1_1_1 michael@0: false, // TFunctionFaceForward2_2_2 michael@0: false, // TFunctionFaceForward3_3_3 michael@0: false, // TFunctionFaceForward4_4_4 michael@0: false, // TFunctionLength1 michael@0: false, // TFunctionLength2 michael@0: false, // TFunctionLength3 michael@0: false, // TFunctionLength4 michael@0: false, // TFunctionNormalize1 michael@0: false, // TFunctionNormalize2 michael@0: false, // TFunctionNormalize3 michael@0: false, // TFunctionNormalize4 michael@0: false, // TFunctionReflect1_1 michael@0: false, // TFunctionReflect2_2 michael@0: false, // TFunctionReflect3_3 michael@0: false, // TFunctionReflect4_4 michael@0: #endif michael@0: false // TFunctionUnknown michael@0: }; michael@0: michael@0: const bool kFunctionEmulationFragmentMask[] = { michael@0: #if defined(__APPLE__) michael@0: // Work around ATI driver bugs in Mac. michael@0: true, // TFunctionCos1 michael@0: true, // TFunctionCos2 michael@0: true, // TFunctionCos3 michael@0: true, // TFunctionCos4 michael@0: true, // TFunctionDistance1_1 michael@0: false, // TFunctionDistance2_2 michael@0: false, // TFunctionDistance3_3 michael@0: false, // TFunctionDistance4_4 michael@0: true, // TFunctionDot1_1 michael@0: false, // TFunctionDot2_2 michael@0: false, // TFunctionDot3_3 michael@0: false, // TFunctionDot4_4 michael@0: true, // TFunctionFaceForward1_1_1 michael@0: false, // TFunctionFaceForward2_2_2 michael@0: false, // TFunctionFaceForward3_3_3 michael@0: false, // TFunctionFaceForward4_4_4 michael@0: true, // TFunctionLength1 michael@0: false, // TFunctionLength2 michael@0: false, // TFunctionLength3 michael@0: false, // TFunctionLength4 michael@0: true, // TFunctionNormalize1 michael@0: false, // TFunctionNormalize2 michael@0: false, // TFunctionNormalize3 michael@0: false, // TFunctionNormalize4 michael@0: true, // TFunctionReflect1_1 michael@0: false, // TFunctionReflect2_2 michael@0: false, // TFunctionReflect3_3 michael@0: false, // TFunctionReflect4_4 michael@0: #else michael@0: // Work around D3D driver bug in Win. michael@0: false, // TFunctionCos1 michael@0: false, // TFunctionCos2 michael@0: false, // TFunctionCos3 michael@0: false, // TFunctionCos4 michael@0: false, // TFunctionDistance1_1 michael@0: false, // TFunctionDistance2_2 michael@0: false, // TFunctionDistance3_3 michael@0: false, // TFunctionDistance4_4 michael@0: false, // TFunctionDot1_1 michael@0: false, // TFunctionDot2_2 michael@0: false, // TFunctionDot3_3 michael@0: false, // TFunctionDot4_4 michael@0: false, // TFunctionFaceForward1_1_1 michael@0: false, // TFunctionFaceForward2_2_2 michael@0: false, // TFunctionFaceForward3_3_3 michael@0: false, // TFunctionFaceForward4_4_4 michael@0: false, // TFunctionLength1 michael@0: false, // TFunctionLength2 michael@0: false, // TFunctionLength3 michael@0: false, // TFunctionLength4 michael@0: false, // TFunctionNormalize1 michael@0: false, // TFunctionNormalize2 michael@0: false, // TFunctionNormalize3 michael@0: false, // TFunctionNormalize4 michael@0: false, // TFunctionReflect1_1 michael@0: false, // TFunctionReflect2_2 michael@0: false, // TFunctionReflect3_3 michael@0: false, // TFunctionReflect4_4 michael@0: #endif michael@0: false // TFunctionUnknown michael@0: }; michael@0: michael@0: class BuiltInFunctionEmulationMarker : public TIntermTraverser { michael@0: public: michael@0: BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator) michael@0: : mEmulator(emulator) michael@0: { michael@0: } michael@0: michael@0: virtual bool visitUnary(Visit visit, TIntermUnary* node) michael@0: { michael@0: if (visit == PreVisit) { michael@0: bool needToEmulate = mEmulator.SetFunctionCalled( michael@0: node->getOp(), node->getOperand()->getType()); michael@0: if (needToEmulate) michael@0: node->setUseEmulatedFunction(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: virtual bool visitAggregate(Visit visit, TIntermAggregate* node) michael@0: { michael@0: if (visit == PreVisit) { michael@0: // Here we handle all the built-in functions instead of the ones we michael@0: // currently identified as problematic. michael@0: switch (node->getOp()) { michael@0: case EOpLessThan: michael@0: case EOpGreaterThan: michael@0: case EOpLessThanEqual: michael@0: case EOpGreaterThanEqual: michael@0: case EOpVectorEqual: michael@0: case EOpVectorNotEqual: michael@0: case EOpMod: michael@0: case EOpPow: michael@0: case EOpAtan: michael@0: case EOpMin: michael@0: case EOpMax: michael@0: case EOpClamp: michael@0: case EOpMix: michael@0: case EOpStep: michael@0: case EOpSmoothStep: michael@0: case EOpDistance: michael@0: case EOpDot: michael@0: case EOpCross: michael@0: case EOpFaceForward: michael@0: case EOpReflect: michael@0: case EOpRefract: michael@0: case EOpMul: michael@0: break; michael@0: default: michael@0: return true; michael@0: }; michael@0: const TIntermSequence& sequence = node->getSequence(); michael@0: bool needToEmulate = false; michael@0: michael@0: if (sequence.size() == 2) { michael@0: TIntermTyped* param1 = sequence[0]->getAsTyped(); michael@0: TIntermTyped* param2 = sequence[1]->getAsTyped(); michael@0: if (!param1 || !param2) michael@0: return true; michael@0: needToEmulate = mEmulator.SetFunctionCalled( michael@0: node->getOp(), param1->getType(), param2->getType()); michael@0: } else if (sequence.size() == 3) { michael@0: TIntermTyped* param1 = sequence[0]->getAsTyped(); michael@0: TIntermTyped* param2 = sequence[1]->getAsTyped(); michael@0: TIntermTyped* param3 = sequence[2]->getAsTyped(); michael@0: if (!param1 || !param2 || !param3) michael@0: return true; michael@0: needToEmulate = mEmulator.SetFunctionCalled( michael@0: node->getOp(), param1->getType(), param2->getType(), param3->getType()); michael@0: } else { michael@0: return true; michael@0: } michael@0: michael@0: if (needToEmulate) michael@0: node->setUseEmulatedFunction(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: private: michael@0: BuiltInFunctionEmulator& mEmulator; michael@0: }; michael@0: michael@0: } // anonymous namepsace michael@0: michael@0: BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType) michael@0: { michael@0: if (shaderType == SH_FRAGMENT_SHADER) { michael@0: mFunctionMask = kFunctionEmulationFragmentMask; michael@0: mFunctionSource = kFunctionEmulationFragmentSource; michael@0: } else { michael@0: mFunctionMask = kFunctionEmulationVertexMask; michael@0: mFunctionSource = kFunctionEmulationVertexSource; michael@0: } michael@0: } michael@0: michael@0: bool BuiltInFunctionEmulator::SetFunctionCalled( michael@0: TOperator op, const TType& param) michael@0: { michael@0: TBuiltInFunction function = IdentifyFunction(op, param); michael@0: return SetFunctionCalled(function); michael@0: } michael@0: michael@0: bool BuiltInFunctionEmulator::SetFunctionCalled( michael@0: TOperator op, const TType& param1, const TType& param2) michael@0: { michael@0: TBuiltInFunction function = IdentifyFunction(op, param1, param2); michael@0: return SetFunctionCalled(function); michael@0: } michael@0: michael@0: bool BuiltInFunctionEmulator::SetFunctionCalled( michael@0: TOperator op, const TType& param1, const TType& param2, const TType& param3) michael@0: { michael@0: TBuiltInFunction function = IdentifyFunction(op, param1, param2, param3); michael@0: return SetFunctionCalled(function); michael@0: } michael@0: michael@0: bool BuiltInFunctionEmulator::SetFunctionCalled( michael@0: BuiltInFunctionEmulator::TBuiltInFunction function) { michael@0: if (function == TFunctionUnknown || mFunctionMask[function] == false) michael@0: return false; michael@0: for (size_t i = 0; i < mFunctions.size(); ++i) { michael@0: if (mFunctions[i] == function) michael@0: return true; michael@0: } michael@0: mFunctions.push_back(function); michael@0: return true; michael@0: } michael@0: michael@0: void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition( michael@0: TInfoSinkBase& out, bool withPrecision) const michael@0: { michael@0: if (mFunctions.size() == 0) michael@0: return; michael@0: out << "// BEGIN: Generated code for built-in function emulation\n\n"; michael@0: if (withPrecision) { michael@0: out << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n" michael@0: << "#define webgl_emu_precision highp\n" michael@0: << "#else\n" michael@0: << "#define webgl_emu_precision mediump\n" michael@0: << "#endif\n\n"; michael@0: } else { michael@0: out << "#define webgl_emu_precision\n\n"; michael@0: } michael@0: for (size_t i = 0; i < mFunctions.size(); ++i) { michael@0: out << mFunctionSource[mFunctions[i]] << "\n\n"; michael@0: } michael@0: out << "// END: Generated code for built-in function emulation\n\n"; michael@0: } michael@0: michael@0: BuiltInFunctionEmulator::TBuiltInFunction michael@0: BuiltInFunctionEmulator::IdentifyFunction( michael@0: TOperator op, const TType& param) michael@0: { michael@0: if (param.getNominalSize() > 4) michael@0: return TFunctionUnknown; michael@0: unsigned int function = TFunctionUnknown; michael@0: switch (op) { michael@0: case EOpCos: michael@0: function = TFunctionCos1; michael@0: break; michael@0: case EOpLength: michael@0: function = TFunctionLength1; michael@0: break; michael@0: case EOpNormalize: michael@0: function = TFunctionNormalize1; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: if (function == TFunctionUnknown) michael@0: return TFunctionUnknown; michael@0: if (param.isVector()) michael@0: function += param.getNominalSize() - 1; michael@0: return static_cast(function); michael@0: } michael@0: michael@0: BuiltInFunctionEmulator::TBuiltInFunction michael@0: BuiltInFunctionEmulator::IdentifyFunction( michael@0: TOperator op, const TType& param1, const TType& param2) michael@0: { michael@0: // Right now for all the emulated functions with two parameters, the two michael@0: // parameters have the same type. michael@0: if (param1.isVector() != param2.isVector() || michael@0: param1.getNominalSize() != param2.getNominalSize() || michael@0: param1.getNominalSize() > 4) michael@0: return TFunctionUnknown; michael@0: michael@0: unsigned int function = TFunctionUnknown; michael@0: switch (op) { michael@0: case EOpDistance: michael@0: function = TFunctionDistance1_1; michael@0: break; michael@0: case EOpDot: michael@0: function = TFunctionDot1_1; michael@0: break; michael@0: case EOpReflect: michael@0: function = TFunctionReflect1_1; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: if (function == TFunctionUnknown) michael@0: return TFunctionUnknown; michael@0: if (param1.isVector()) michael@0: function += param1.getNominalSize() - 1; michael@0: return static_cast(function); michael@0: } michael@0: michael@0: BuiltInFunctionEmulator::TBuiltInFunction michael@0: BuiltInFunctionEmulator::IdentifyFunction( michael@0: TOperator op, const TType& param1, const TType& param2, const TType& param3) michael@0: { michael@0: // Check that all params have the same type, length, michael@0: // and that they're not too large. michael@0: if (param1.isVector() != param2.isVector() || michael@0: param2.isVector() != param3.isVector() || michael@0: param1.getNominalSize() != param2.getNominalSize() || michael@0: param2.getNominalSize() != param3.getNominalSize() || michael@0: param1.getNominalSize() > 4) michael@0: return TFunctionUnknown; michael@0: michael@0: unsigned int function = TFunctionUnknown; michael@0: switch (op) { michael@0: case EOpFaceForward: michael@0: function = TFunctionFaceForward1_1_1; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: if (function == TFunctionUnknown) michael@0: return TFunctionUnknown; michael@0: if (param1.isVector()) michael@0: function += param1.getNominalSize() - 1; michael@0: return static_cast(function); michael@0: } michael@0: michael@0: void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation( michael@0: TIntermNode* root) michael@0: { michael@0: ASSERT(root); michael@0: michael@0: BuiltInFunctionEmulationMarker marker(*this); michael@0: root->traverse(&marker); michael@0: } michael@0: michael@0: void BuiltInFunctionEmulator::Cleanup() michael@0: { michael@0: mFunctions.clear(); michael@0: } michael@0: michael@0: //static michael@0: TString BuiltInFunctionEmulator::GetEmulatedFunctionName( michael@0: const TString& name) michael@0: { michael@0: ASSERT(name[name.length() - 1] == '('); michael@0: return "webgl_" + name.substr(0, name.length() - 1) + "_emu("; michael@0: } michael@0: