Commit a3b4ab4c by zmo@google.com

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
parent 74a46a5c
#define MAJOR_VERSION 0
#define MINOR_VERSION 0
#define BUILD_VERSION 0
#define BUILD_REVISION 753
#define BUILD_REVISION 754
#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
......
......@@ -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; }",
"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; }",
// 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])); }",
"#error no emulation for cos(float)",
"#error no emulation for cos(vec2)",
"#error no emulation for cos(vec3)",
"#error no emulation for cos(vec4)",
"#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)",
"#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)",
"#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
#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
true, // 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
#endif
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
......@@ -101,17 +189,30 @@ const bool kFunctionEmulationFragmentMask[] = {
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 @@ private:
} // 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 @@ bool BuiltInFunctionEmulator::SetFunctionCalled(
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 @@ void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
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::TBuiltInFunction
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 @@ BuiltInFunctionEmulator::IdentifyFunction(
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 @@ void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
root->traverse(&marker);
}
void BuiltInFunctionEmulator::Cleanup()
{
mFunctions.clear();
}
//static
TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
const TString& name)
......
......@@ -13,23 +13,6 @@
#include "compiler/intermediate.h"
//
// Built-in function groups. We only list the ones that might need to be
// emulated in certain os/drivers, assuming they are no more than 32.
//
enum TBuiltInFunctionGroup {
TFunctionGroupAbs = 1 << 0, // NVIDIA Win/Mac
TFunctionGroupAtan = 1 << 1, // NVIDIA Win/Mac
TFunctionGroupCos = 1 << 2, // ATI Mac
TFunctionGroupMod = 1 << 3, // NVIDIA Win/Mac
TFunctionGroupSign = 1 << 4, // NVIDIA Win/Mac
TFunctionGroupAll = TFunctionGroupAbs |
TFunctionGroupAtan |
TFunctionGroupCos |
TFunctionGroupMod |
TFunctionGroupSign
};
//
// This class decides which built-in functions need to be replaced with the
// emulated ones.
// It's only a workaround for OpenGL driver bugs, and isn't needed in general.
......@@ -37,13 +20,6 @@ enum TBuiltInFunctionGroup {
class BuiltInFunctionEmulator {
public:
BuiltInFunctionEmulator(ShShaderType shaderType);
// functionGroupMask is a bitmap of TBuiltInFunctionGroup.
// We only emulate functions that are marked by this mask and are actually
// called in a given shader.
// By default the value is TFunctionGroupAll.
void SetFunctionGroupMask(unsigned int functionGroupMask);
// Records that a function is called by the shader and might needs to be
// emulated. If the function's group is not in mFunctionGroupFilter, this
// becomes an no-op.
......@@ -59,6 +35,8 @@ public:
void MarkBuiltInFunctionsForEmulation(TIntermNode* root);
void Cleanup();
// "name(" becomes "webgl_name_emu(".
static TString GetEmulatedFunctionName(const TString& name);
......@@ -67,16 +45,7 @@ private:
// Built-in functions.
//
enum TBuiltInFunction {
TFunctionAbs1 = 0, // float abs(float);
TFunctionAbs2, // vec2 abs(vec2);
TFunctionAbs3, // vec3 abs(vec3);
TFunctionAbs4, // vec4 abs(vec4);
TFunctionAtan1, // float atan(float);
TFunctionAtan2, // vec2 atan(vec2);
TFunctionAtan3, // vec3 atan(vec3);
TFunctionAtan4, // vec4 atan(vec4);
TFunctionAtan1_1, // float atan(float, float);
TFunctionAtan1_1 = 0, // float atan(float, float);
TFunctionAtan2_2, // vec2 atan(vec2, vec2);
TFunctionAtan3_3, // vec3 atan(vec3, vec2);
TFunctionAtan4_4, // vec4 atan(vec4, vec2);
......@@ -86,15 +55,36 @@ private:
TFunctionCos3, // vec3 cos(vec3);
TFunctionCos4, // vec4 cos(vec4);
TFunctionDistance1_1, // float distance(float, float);
TFunctionDistance2_2, // vec2 distance(vec2, vec2);
TFunctionDistance3_3, // vec3 distance(vec3, vec3);
TFunctionDistance4_4, // vec4 distance(vec4, vec4);
TFunctionDot1_1, // float dot(float, float);
TFunctionDot2_2, // vec2 dot(vec2, vec2);
TFunctionDot3_3, // vec3 dot(vec3, vec3);
TFunctionDot4_4, // vec4 dot(vec4, vec4);
TFunctionLength1, // float length(float);
TFunctionLength2, // float length(vec2);
TFunctionLength3, // float length(vec3);
TFunctionLength4, // float length(vec4);
TFunctionMod1_1, // float mod(float, float);
TFunctionMod2_2, // vec2 mod(vec2, vec2);
TFunctionMod3_3, // vec3 mod(vec3, vec3);
TFunctionMod4_4, // vec4 mod(vec4, vec4);
TFunctionSign1, // float sign(float);
TFunctionSign2, // vec2 sign(vec2);
TFunctionSign3, // vec3 sign(vec3);
TFunctionSign4, // vec4 sign(vec4);
TFunctionNormalize1, // float normalize(float);
TFunctionNormalize2, // vec2 normalize(vec2);
TFunctionNormalize3, // vec3 normalize(vec3);
TFunctionNormalize4, // vec4 normalize(vec4);
TFunctionReflect1_1, // float reflect(float, float);
TFunctionReflect2_2, // vec2 reflect(vec2, vec2);
TFunctionReflect3_3, // vec3 reflect(vec3, vec3);
TFunctionReflect4_4, // vec4 reflect(vec4, vec4);
TFunctionUnknown
};
......@@ -105,9 +95,9 @@ private:
bool SetFunctionCalled(TBuiltInFunction function);
TVector<TBuiltInFunction> mFunctions;
unsigned int mFunctionGroupMask; // a bitmap of TBuiltInFunctionGroup.
const bool* mFunctionMask; // a boolean flag for each function.
const char** mFunctionSource;
};
#endif // COMPILIER_BUILT_IN_FUNCTION_EMULATOR_H_
......@@ -209,6 +209,8 @@ void TCompiler::clearResults()
attribs.clear();
uniforms.clear();
builtInFunctionEmulator.Cleanup();
}
bool TCompiler::detectRecursion(TIntermNode* root)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment