Further work on the function emulation.
This is to work around driver bugs. We added more functions, and removed some unnecessary ones. Remove the function group because we have flags for each function now. Use more macros instead of functions. Don't emit global precision because that will affect all later code.
ANGLEBUG=196
TEST=build and test on Mac ATI/NVIDIA, fixes the failing webgl glsl conformance tests.
Review URL: http://codereview.appspot.com/5011053
git-svn-id: https://angleproject.googlecode.com/svn/trunk@754 736b8ea6-26fd-11df-bfd4-992fa37f6226
diff --git a/src/compiler/BuiltInFunctionEmulator.cpp b/src/compiler/BuiltInFunctionEmulator.cpp
index f44a044..71c0aa7 100644
--- a/src/compiler/BuiltInFunctionEmulator.cpp
+++ b/src/compiler/BuiltInFunctionEmulator.cpp
@@ -10,85 +10,173 @@
namespace {
-const char* kFunctionEmulationSource[] = {
- "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; }",
+// we use macros here instead of function definitions to work around more GLSL
+// compiler bugs, in particular on NVIDIA hardware on Mac OSX. Macros are
+// problematic because if the argument has side-effects they will be repeatedly
+// evaluated. This is unlikely to show up in real shaders, but is something to
+// consider.
+const char* kFunctionEmulationVertexSource[] = {
+ "#error no emulation for atan(float, float)",
+ "vec2 webgl_atan_emu(vec2 y, vec2 x) { return vec2(atan(y[0], x[0]), atan(y[1], x[1])); }",
+ "vec3 webgl_atan_emu(vec3 y, vec3 x) { return vec3(atan(y[0], x[0]), atan(y[1], x[1]), atan(y[2], x[2])); }",
+ "vec4 webgl_atan_emu(vec4 y, vec4 x) { return vec4(atan(y[0], x[0]), atan(y[1], x[1]), atan(y[2], x[2]), atan(y[3], x[3])); }",
- "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; }",
+ "#error no emulation for cos(float)",
+ "#error no emulation for cos(vec2)",
+ "#error no emulation for cos(vec3)",
+ "#error no emulation for cos(vec4)",
- "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); }",
+ "#define webgl_distance_emu(x, y) ((x) >= (y) ? (x) - (y) : (y) - (x))",
+ "#error no emulation for distance(vec2, vec2)",
+ "#error no emulation for distance(vec3, vec3)",
+ "#error no emulation for distance(vec4, vec4)",
- "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; }",
+ "#define webgl_dot_emu(x, y) ((x) * (y))",
+ "#error no emulation for dot(vec2, vec2)",
+ "#error no emulation for dot(vec3, vec3)",
+ "#error no emulation for dot(vec4, vec4)",
- "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; }",
+ "#define webgl_length_emu(x) ((x) >= 0.0 ? (x) : -(x))",
+ "#error no emulation for length(vec2)",
+ "#error no emulation for length(vec3)",
+ "#error no emulation for length(vec4)",
+
+ "#error no emulation for mod(float, float)",
+ "vec2 webgl_mod_emu(vec2 x, vec2 y) { return vec2(mod(x[0], y[0]), mod(x[1], y[1])); }",
+ "vec3 webgl_mod_emu(vec3 x, vec3 y) { return vec3(mod(x[0], y[0]), mod(x[1], y[1]), mod(x[2], y[2])); }",
+ "vec4 webgl_mod_emu(vec4 x, vec4 y) { return vec4(mod(x[0], y[0]), mod(x[1], y[1]), mod(x[2], y[2]), mod(x[3], y[3])); }",
+
+ "#define webgl_normalize_emu(x) ((x) == 0.0 ? 0.0 : ((x) > 0.0 ? 1.0 : -1.0))",
+ "#error no emulation for normalize(vec2)",
+ "#error no emulation for normalize(vec3)",
+ "#error no emulation for normalize(vec4)",
+
+ "#define webgl_reflect_emu(I, N) ((I) - 2.0 * (N) * (I) * (N))",
+ "#error no emulation for reflect(vec2, vec2)",
+ "#error no emulation for reflect(vec3, vec3)",
+ "#error no emulation for reflect(vec4, vec4)"
+};
+
+const char* kFunctionEmulationFragmentSource[] = {
+ "#error no emulation for atan(float, float)",
+ "#error no emulation for atan(vec2, vec2)",
+ "#error no emulation for atan(vec3, vec3)",
+ "#error no emulation for atan(vec4, vec4)",
+
+ "webgl_emu_precision float webgl_cos_emu(webgl_emu_precision float a) { return cos(a); }",
+ "webgl_emu_precision vec2 webgl_cos_emu(webgl_emu_precision vec2 a) { return cos(a); }",
+ "webgl_emu_precision vec3 webgl_cos_emu(webgl_emu_precision vec3 a) { return cos(a); }",
+ "webgl_emu_precision vec4 webgl_cos_emu(webgl_emu_precision vec4 a) { return cos(a); }",
+
+ "#error no emulation for distance(float, float)",
+ "#error no emulation for distance(vec2, vec2)",
+ "#error no emulation for distance(vec3, vec3)",
+ "#error no emulation for distance(vec4, vec4)",
+
+ "#error no emulation for dot(float, float)",
+ "#error no emulation for dot(vec2, vec2)",
+ "#error no emulation for dot(vec3, vec3)",
+ "#error no emulation for dot(vec4, vec4)",
+
+ "#error no emulation for length(float)",
+ "#error no emulation for length(vec2)",
+ "#error no emulation for length(vec3)",
+ "#error no emulation for length(vec4)",
+
+ "#error no emulation for mod(float, float)",
+ "#error no emulation for mod(vec2, vec2)",
+ "#error no emulation for mod(vec3, vec3)",
+ "#error no emulation for mod(vec4, vec4)",
+
+ "#error no emulation for normalize(float)",
+ "#error no emulation for normalize(vec2)",
+ "#error no emulation for normalize(vec3)",
+ "#error no emulation for normalize(vec4)",
+
+ "#error no emulation for reflect(float, float)",
+ "#error no emulation for reflect(vec2, vec2)",
+ "#error no emulation for reflect(vec3, vec3)",
+ "#error no emulation for reflect(vec4, vec4)"
};
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
+#if defined(__APPLE__)
+ // Work around ATI driver bugs in Mac.
false, // TFunctionAtan1_1
false, // TFunctionAtan2_2
false, // TFunctionAtan3_3
false, // TFunctionAtan4_4
+ false, // TFunctionCos1
+ false, // TFunctionCos2
+ false, // TFunctionCos3
+ false, // TFunctionCos4
+ true, // TFunctionDistance1_1
+ false, // TFunctionDistance2_2
+ false, // TFunctionDistance3_3
+ false, // TFunctionDistance4_4
+ true, // TFunctionDot1_1
+ false, // TFunctionDot2_2
+ false, // TFunctionDot3_3
+ false, // TFunctionDot4_4
+ true, // TFunctionLength1
+ false, // TFunctionLength2
+ false, // TFunctionLength3
+ false, // TFunctionLength4
+ false, // TFunctionMod1_1
+ false, // TFunctionMod2_2
+ false, // TFunctionMod3_3
+ false, // TFunctionMod4_4
+ true, // TFunctionNormalize1
+ false, // TFunctionNormalize2
+ false, // TFunctionNormalize3
+ false, // TFunctionNormalize4
+ true, // TFunctionReflect1_1
+ false, // TFunctionReflect2_2
+ false, // TFunctionReflect3_3
+ false, // TFunctionReflect4_4
+#else
+ // Work around D3D driver bug in Win.
+ false, // TFunctionAtan1_1
+ true, // TFunctionAtan2_2
+ true, // TFunctionAtan3_3
+ true, // TFunctionAtan4_4
+ false, // TFunctionCos1
+ false, // TFunctionCos2
+ false, // TFunctionCos3
+ false, // TFunctionCos4
+ false, // TFunctionDistance1_1
+ false, // TFunctionDistance2_2
+ false, // TFunctionDistance3_3
+ false, // TFunctionDistance4_4
+ false, // TFunctionDot1_1
+ false, // TFunctionDot2_2
+ false, // TFunctionDot3_3
+ false, // TFunctionDot4_4
+ false, // TFunctionLength1
+ false, // TFunctionLength2
+ false, // TFunctionLength3
+ false, // TFunctionLength4
+ false, // TFunctionMod1_1
+ true, // TFunctionMod2_2
+ true, // TFunctionMod3_3
+ true, // TFunctionMod4_4
+ false, // TFunctionNormalize1
+ false, // TFunctionNormalize2
+ false, // TFunctionNormalize3
+ false, // TFunctionNormalize4
+ false, // TFunctionReflect1_1
+ false, // TFunctionReflect2_2
+ false, // TFunctionReflect3_3
+ false, // TFunctionReflect4_4
+#endif
+ false // TFunctionUnknown
+};
+const bool kFunctionEmulationFragmentMask[] = {
+ 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
@@ -101,17 +189,30 @@
false, // TFunctionCos3
false, // TFunctionCos4
#endif
-
+ false, // TFunctionDistance1_1
+ false, // TFunctionDistance2_2
+ false, // TFunctionDistance3_3
+ false, // TFunctionDistance4_4
+ false, // TFunctionDot1_1
+ false, // TFunctionDot2_2
+ false, // TFunctionDot3_3
+ false, // TFunctionDot4_4
+ false, // TFunctionLength1
+ false, // TFunctionLength2
+ false, // TFunctionLength3
+ false, // TFunctionLength4
false, // TFunctionMod1_1
false, // TFunctionMod2_2
false, // TFunctionMod3_3
false, // TFunctionMod4_4
-
- false, // TFunctionSign1
- false, // TFunctionSign2
- false, // TFunctionSign3
- false, // TFunctionSign4
-
+ false, // TFunctionNormalize1
+ false, // TFunctionNormalize2
+ false, // TFunctionNormalize3
+ false, // TFunctionNormalize4
+ false, // TFunctionReflect1_1
+ false, // TFunctionReflect2_2
+ false, // TFunctionReflect3_3
+ false, // TFunctionReflect4_4
false // TFunctionUnknown
};
@@ -188,18 +289,14 @@
} // anonymous namepsace
BuiltInFunctionEmulator::BuiltInFunctionEmulator(ShShaderType shaderType)
- : mFunctionGroupMask(TFunctionGroupAll)
{
- if (shaderType == SH_FRAGMENT_SHADER)
+ if (shaderType == SH_FRAGMENT_SHADER) {
mFunctionMask = kFunctionEmulationFragmentMask;
- else
+ mFunctionSource = kFunctionEmulationFragmentSource;
+ } else {
mFunctionMask = kFunctionEmulationVertexMask;
-}
-
-void BuiltInFunctionEmulator::SetFunctionGroupMask(
- unsigned int functionGroupMask)
-{
- mFunctionGroupMask = functionGroupMask;
+ mFunctionSource = kFunctionEmulationVertexSource;
+ }
}
bool BuiltInFunctionEmulator::SetFunctionCalled(
@@ -224,61 +321,8 @@
if (mFunctions[i] == function)
return true;
}
- switch (function) {
- case TFunctionAbs1:
- case TFunctionAbs2:
- case TFunctionAbs3:
- case TFunctionAbs4:
- if (mFunctionGroupMask & TFunctionGroupAbs) {
- mFunctions.push_back(function);
- 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:
- case TFunctionSign4:
- if (mFunctionGroupMask & TFunctionGroupSign) {
- mFunctions.push_back(function);
- return true;
- }
- break;
- default:
- UNREACHABLE();
- break;
- }
- return false;
+ mFunctions.push_back(function);
+ return true;
}
void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
@@ -288,14 +332,16 @@
return;
out << "// BEGIN: Generated code for built-in function emulation\n\n";
if (withPrecision) {
- out << "#if defined(GL_FRAGMENT_PRECISION_HIGH) && (GL_FRAGMENT_PRECISION_HIGH == 1)\n"
- << "precision highp float;\n"
+ out << "#if defined(GL_FRAGMENT_PRECISION_HIGH)\n"
+ << "#define webgl_emulation_precision highp\n"
<< "#else\n"
- << "precision mediump float;\n"
+ << "#define webgl_emulation_precision mediump\n"
<< "#endif\n\n";
+ } else {
+ out << "#define webgl_emulation_precision\n\n";
}
for (size_t i = 0; i < mFunctions.size(); ++i) {
- out << kFunctionEmulationSource[mFunctions[i]] << "\n\n";
+ out << mFunctionSource[mFunctions[i]] << "\n\n";
}
out << "// END: Generated code for built-in function emulation\n\n";
}
@@ -304,19 +350,18 @@
BuiltInFunctionEmulator::IdentifyFunction(
TOperator op, const TType& param)
{
+ if (param.getNominalSize() > 4)
+ return TFunctionUnknown;
unsigned int function = TFunctionUnknown;
switch (op) {
- case EOpAbs:
- function = TFunctionAbs1;
- break;
- case EOpAtan:
- function = TFunctionAtan1;
- break;
case EOpCos:
function = TFunctionCos1;
break;
- case EOpSign:
- function = TFunctionSign1;
+ case EOpLength:
+ function = TFunctionLength1;
+ break;
+ case EOpNormalize:
+ function = TFunctionNormalize1;
break;
default:
break;
@@ -344,11 +389,17 @@
case EOpAtan:
function = TFunctionAtan1_1;
break;
+ case EOpDistance:
+ function = TFunctionDistance1_1;
+ break;
+ case EOpDot:
+ function = TFunctionDot1_1;
+ break;
case EOpMod:
function = TFunctionMod1_1;
break;
- case EOpSign:
- function = TFunctionSign1;
+ case EOpReflect:
+ function = TFunctionReflect1_1;
break;
default:
break;
@@ -369,6 +420,11 @@
root->traverse(&marker);
}
+void BuiltInFunctionEmulator::Cleanup()
+{
+ mFunctions.clear();
+}
+
//static
TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
const TString& name)