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);
}