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 +