Commit 32e97315 by zmo@google.com

Emulate certain buil-in functions to work around driver bugs.

This is implemented by adding a new compile option SH_EMULATE_BUILT_IN_FUNCTIONS. The emulated functions are names as webgl_originalName_emu so there will never be naming conflicts. At the moment only three functions are emulated: normalize, abs, sign. Also, the compile option will emulate all three. However, the mechanism to emulate only a selected subset is also imeplemented. It can be turned on easily. ANGLEBUG=196 TEST=with this option, the failed test with abs.frag passes. Review URL: http://codereview.appspot.com/4916043 git-svn-id: https://angleproject.googlecode.com/svn/trunk@738 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent b81c401d
......@@ -82,7 +82,10 @@ typedef enum {
SH_LINE_DIRECTIVES = 0x0010,
SH_SOURCE_PATH = 0x0020,
SH_MAP_LONG_VARIABLE_NAMES = 0x0040,
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = 0x0080
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = 0x0080,
// This is needed only as a workaround for certain OpenGL driver bugs.
SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0100
} ShCompileOptions;
//
......
......@@ -84,6 +84,7 @@ int main(int argc, char* argv[])
case 'o': compileOptions |= SH_OBJECT_CODE; break;
case 'u': compileOptions |= SH_ATTRIBUTES_UNIFORMS; break;
case 'l': compileOptions |= SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; break;
case 'e': compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; break;
case 'b':
if (argv[0][2] == '=') {
switch (argv[0][3]) {
......@@ -177,13 +178,14 @@ int main(int argc, char* argv[])
//
void usage()
{
printf("Usage: translate [-i -m -o -u -l -b=e -b=g -b=h -a] file1 file2 ...\n"
printf("Usage: translate [-i -m -o -u -l -e -b=e -b=g -b=h -a] file1 file2 ...\n"
"Where: filename : filename ending in .frag or .vert\n"
" -i : print intermediate tree\n"
" -m : map long variable names\n"
" -o : print translated code\n"
" -u : print active attribs and uniforms\n"
" -l : unroll for-loops with integer indices\n"
" -e : emulate certain built-in functions (workaround for driver bugs)\n"
" -b=e : output GLSL ES code (this is by default)\n"
" -b=g : output GLSL code\n"
" -b=h : output HLSL code\n"
......
......@@ -19,6 +19,8 @@
],
'sources': [
'compiler/BaseTypes.h',
'compiler/BuiltInFunctionEmulator.cpp',
'compiler/BuiltInFunctionEmulator.h',
'compiler/Common.h',
'compiler/Compiler.cpp',
'compiler/ConstantUnion.h',
......
#define MAJOR_VERSION 0
#define MINOR_VERSION 0
#define BUILD_VERSION 0
#define BUILD_REVISION 736
#define BUILD_REVISION 738
#define STRINGIFY(x) #x
#define MACRO_STRINGIFY(x) STRINGIFY(x)
......
//
// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/BuiltInFunctionEmulator.h"
#include "compiler/SymbolTable.h"
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_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; }",
};
class BuiltInFunctionEmulationMarker : public TIntermTraverser {
public:
BuiltInFunctionEmulationMarker(BuiltInFunctionEmulator& emulator)
: mEmulator(emulator)
{
}
virtual bool visitUnary(Visit visit, TIntermUnary* node)
{
if (visit == PreVisit) {
bool needToEmulate = mEmulator.SetFunctionCalled(
node->getOp(), node->getOperand()->getType());
if (needToEmulate)
node->setUseEmulatedFunction();
}
return true;
}
private:
BuiltInFunctionEmulator& mEmulator;
};
} // anonymous namepsace
BuiltInFunctionEmulator::BuiltInFunctionEmulator()
: mFunctionGroupMask(TFunctionGroupAll)
{
}
void BuiltInFunctionEmulator::SetFunctionGroupMask(
unsigned int functionGroupMask)
{
mFunctionGroupMask = functionGroupMask;
}
bool BuiltInFunctionEmulator::SetFunctionCalled(
TOperator op, const TType& returnType)
{
TBuiltInFunction function = IdentifyFunction(op, returnType);
if (function == TFunctionUnknown)
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:
case TFunctionAbs4:
if (mFunctionGroupMask & TFunctionGroupAbs) {
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;
}
void BuiltInFunctionEmulator::OutputEmulatedFunctionDefinition(
TInfoSinkBase& out, bool withPrecision) const
{
if (mFunctions.size() == 0)
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"
<< "#else\n"
<< "precision mediump float;\n"
<< "#endif\n\n";
}
for (size_t i = 0; i < mFunctions.size(); ++i) {
out << kFunctionEmulationSource[mFunctions[i]] << "\n\n";
}
out << "// END: Generated code for built-in function emulation\n\n";
}
BuiltInFunctionEmulator::TBuiltInFunction
BuiltInFunctionEmulator::IdentifyFunction(TOperator op, const TType& returnType)
{
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);
if (returnType.isVector())
function += returnType.getNominalSize() - 1;
return static_cast<TBuiltInFunction>(function);
}
void BuiltInFunctionEmulator::MarkBuiltInFunctionsForEmulation(
TIntermNode* root)
{
ASSERT(root);
BuiltInFunctionEmulationMarker marker(*this);
root->traverse(&marker);
}
//static
TString BuiltInFunctionEmulator::GetEmulatedFunctionName(
const TString& name)
{
ASSERT(name[name.length() - 1] == '(');
return "webgl_" + name.substr(0, name.length() - 1) + "_emu(";
}
//
// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILIER_BUILT_IN_FUNCTION_EMULATOR_H_
#define COMPILIER_BUILT_IN_FUNCTION_EMULATOR_H_
#include "compiler/InfoSink.h"
#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 {
TFunctionGroupNormalize = 1 << 0,
TFunctionGroupAbs = 1 << 1,
TFunctionGroupSign = 1 << 2,
TFunctionGroupAll =
TFunctionGroupNormalize | TFunctionGroupAbs | 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.
//
class BuiltInFunctionEmulator {
public:
BuiltInFunctionEmulator();
// 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.
// Returns true if the function call needs to be replaced with an emulated
// one.
// TODO(zmo): for now, an operator and a return type is enough to identify
// the function we want to emulate. Should make this more flexible to
// handle any functions.
bool SetFunctionCalled(TOperator op, const TType& returnType);
// Output function emulation definition. This should be before any other
// shader source.
void OutputEmulatedFunctionDefinition(TInfoSinkBase& out, bool withPrecision) const;
void MarkBuiltInFunctionsForEmulation(TIntermNode* root);
// "name(" becomes "webgl_name_emu(".
static TString GetEmulatedFunctionName(const TString& name);
private:
//
// Built-in functions.
//
enum TBuiltInFunction {
TFunctionNormalize1 = 0, // float normalize(float);
TFunctionNormalize2, // vec2 normalize(vec2);
TFunctionNormalize3, // vec3 normalize(vec3);
TFunctionNormalize4, // fec4 normalize(vec4);
TFunctionAbs1, // float abs(float);
TFunctionAbs2, // vec2 abs(vec2);
TFunctionAbs3, // vec3 abs(vec3);
TFunctionAbs4, // vec4 abs(vec4);
TFunctionSign1, // float sign(float);
TFunctionSign2, // vec2 sign(vec2);
TFunctionSign3, // vec3 sign(vec3);
TFunctionSign4, // vec4 sign(vec4);
TFunctionUnknown
};
// Same TODO as SetFunctionCalled.
TBuiltInFunction IdentifyFunction(TOperator op, const TType& returnType);
TVector<TBuiltInFunction> mFunctions;
unsigned int mFunctionGroupMask; // a bitmap of TBuiltInFunctionGroup.
};
#endif // COMPILIER_BUILT_IN_FUNCTION_EMULATOR_H_
......@@ -4,6 +4,7 @@
// found in the LICENSE file.
//
#include "compiler/BuiltInFunctionEmulator.h"
#include "compiler/DetectRecursion.h"
#include "compiler/ForLoopUnroll.h"
#include "compiler/Initialize.h"
......@@ -160,6 +161,10 @@ bool TCompiler::compile(const char* const shaderStrings[],
if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
// Built-in function emulation needs to happen after validateLimitations pass.
if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
builtInFunctionEmulator.MarkBuiltInFunctionsForEmulation(root);
// Call mapLongVariableNames() before collectAttribsUniforms() so in
// collectAttribsUniforms() we already have the mapped symbol names and
// we could composite mapped and original variable names.
......@@ -251,3 +256,8 @@ const TExtensionBehavior& TCompiler::getExtensionBehavior() const
{
return extensionBehavior;
}
const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
{
return builtInFunctionEmulator;
}
......@@ -305,25 +305,28 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary* node)
bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node)
{
TString preString;
TString postString = ")";
switch (node->getOp())
{
case EOpNegative: writeTriplet(visit, "(-", NULL, ")"); break;
case EOpVectorLogicalNot: writeTriplet(visit, "not(", NULL, ")"); break;
case EOpLogicalNot: writeTriplet(visit, "(!", NULL, ")"); break;
case EOpNegative: preString = "(-"; break;
case EOpVectorLogicalNot: preString = "not("; break;
case EOpLogicalNot: preString = "(!"; break;
case EOpPostIncrement: writeTriplet(visit, "(", NULL, "++)"); break;
case EOpPostDecrement: writeTriplet(visit, "(", NULL, "--)"); break;
case EOpPreIncrement: writeTriplet(visit, "(++", NULL, ")"); break;
case EOpPreDecrement: writeTriplet(visit, "(--", NULL, ")"); break;
case EOpPostIncrement: preString = "("; postString = "++)"; break;
case EOpPostDecrement: preString = "("; postString = "--)"; break;
case EOpPreIncrement: preString = "(++"; break;
case EOpPreDecrement: preString = "(--"; break;
case EOpConvIntToBool:
case EOpConvFloatToBool:
switch (node->getOperand()->getType().getNominalSize())
{
case 1: writeTriplet(visit, "bool(", NULL, ")"); break;
case 2: writeTriplet(visit, "bvec2(", NULL, ")"); break;
case 3: writeTriplet(visit, "bvec3(", NULL, ")"); break;
case 4: writeTriplet(visit, "bvec4(", NULL, ")"); break;
case 1: preString = "bool("; break;
case 2: preString = "bvec2("; break;
case 3: preString = "bvec3("; break;
case 4: preString = "bvec4("; break;
default: UNREACHABLE();
}
break;
......@@ -331,10 +334,10 @@ bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node)
case EOpConvIntToFloat:
switch (node->getOperand()->getType().getNominalSize())
{
case 1: writeTriplet(visit, "float(", NULL, ")"); break;
case 2: writeTriplet(visit, "vec2(", NULL, ")"); break;
case 3: writeTriplet(visit, "vec3(", NULL, ")"); break;
case 4: writeTriplet(visit, "vec4(", NULL, ")"); break;
case 1: preString = "float("; break;
case 2: preString = "vec2("; break;
case 3: preString = "vec3("; break;
case 4: preString = "vec4("; break;
default: UNREACHABLE();
}
break;
......@@ -342,49 +345,53 @@ bool TOutputGLSLBase::visitUnary(Visit visit, TIntermUnary* node)
case EOpConvBoolToInt:
switch (node->getOperand()->getType().getNominalSize())
{
case 1: writeTriplet(visit, "int(", NULL, ")"); break;
case 2: writeTriplet(visit, "ivec2(", NULL, ")"); break;
case 3: writeTriplet(visit, "ivec3(", NULL, ")"); break;
case 4: writeTriplet(visit, "ivec4(", NULL, ")"); break;
case 1: preString = "int("; break;
case 2: preString = "ivec2("; break;
case 3: preString = "ivec3("; break;
case 4: preString = "ivec4("; break;
default: UNREACHABLE();
}
break;
case EOpRadians: writeTriplet(visit, "radians(", NULL, ")"); break;
case EOpDegrees: writeTriplet(visit, "degrees(", NULL, ")"); break;
case EOpSin: writeTriplet(visit, "sin(", NULL, ")"); break;
case EOpCos: writeTriplet(visit, "cos(", NULL, ")"); break;
case EOpTan: writeTriplet(visit, "tan(", NULL, ")"); break;
case EOpAsin: writeTriplet(visit, "asin(", NULL, ")"); break;
case EOpAcos: writeTriplet(visit, "acos(", NULL, ")"); break;
case EOpAtan: writeTriplet(visit, "atan(", NULL, ")"); break;
case EOpExp: writeTriplet(visit, "exp(", NULL, ")"); break;
case EOpLog: writeTriplet(visit, "log(", NULL, ")"); break;
case EOpExp2: writeTriplet(visit, "exp2(", NULL, ")"); break;
case EOpLog2: writeTriplet(visit, "log2(", NULL, ")"); break;
case EOpSqrt: writeTriplet(visit, "sqrt(", NULL, ")"); break;
case EOpInverseSqrt: writeTriplet(visit, "inversesqrt(", NULL, ")"); break;
case EOpAbs: writeTriplet(visit, "abs(", NULL, ")"); break;
case EOpSign: writeTriplet(visit, "sign(", NULL, ")"); break;
case EOpFloor: writeTriplet(visit, "floor(", NULL, ")"); break;
case EOpCeil: writeTriplet(visit, "ceil(", NULL, ")"); break;
case EOpFract: writeTriplet(visit, "fract(", NULL, ")"); break;
case EOpLength: writeTriplet(visit, "length(", NULL, ")"); break;
case EOpNormalize: writeTriplet(visit, "normalize(", NULL, ")"); break;
case EOpDFdx: writeTriplet(visit, "dFdx(", NULL, ")"); break;
case EOpDFdy: writeTriplet(visit, "dFdy(", NULL, ")"); break;
case EOpFwidth: writeTriplet(visit, "fwidth(", NULL, ")"); break;
case EOpAny: writeTriplet(visit, "any(", NULL, ")"); break;
case EOpAll: writeTriplet(visit, "all(", NULL, ")"); break;
case EOpRadians: preString = "radians("; break;
case EOpDegrees: preString = "degrees("; break;
case EOpSin: preString = "sin("; break;
case EOpCos: preString = "cos("; break;
case EOpTan: preString = "tan("; break;
case EOpAsin: preString = "asin("; break;
case EOpAcos: preString = "acos("; break;
case EOpAtan: preString = "atan("; break;
case EOpExp: preString = "exp("; break;
case EOpLog: preString = "log("; break;
case EOpExp2: preString = "exp2("; break;
case EOpLog2: preString = "log2("; break;
case EOpSqrt: preString = "sqrt("; break;
case EOpInverseSqrt: preString = "inversesqrt("; break;
case EOpAbs: preString = "abs("; break;
case EOpSign: preString = "sign("; break;
case EOpFloor: preString = "floor("; break;
case EOpCeil: preString = "ceil("; break;
case EOpFract: preString = "fract("; break;
case EOpLength: preString = "length("; break;
case EOpNormalize: preString = "normalize("; break;
case EOpDFdx: preString = "dFdx("; break;
case EOpDFdy: preString = "dFdy("; break;
case EOpFwidth: preString = "fwidth("; break;
case EOpAny: preString = "any("; break;
case EOpAll: preString = "all("; break;
default: UNREACHABLE(); break;
}
if (visit == PreVisit && node->getUseEmulatedFunction())
preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preString);
writeTriplet(visit, preString.c_str(), NULL, postString.c_str());
return true;
}
......
......@@ -16,6 +16,7 @@
#include "GLSLANG/ShaderLang.h"
#include "compiler/BuiltInFunctionEmulator.h"
#include "compiler/ExtensionBehavior.h"
#include "compiler/InfoSink.h"
#include "compiler/SymbolTable.h"
......@@ -80,6 +81,8 @@ protected:
// Get built-in extensions with default behavior.
const TExtensionBehavior& getExtensionBehavior() const;
const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
private:
ShShaderType shaderType;
ShShaderSpec shaderSpec;
......@@ -90,6 +93,8 @@ private:
// Built-in extensions with default behavior.
TExtensionBehavior extensionBehavior;
BuiltInFunctionEmulator builtInFunctionEmulator;
// Results of compilation.
TInfoSink infoSink; // Output sink.
TVariableInfoList attribs; // Active attributes in the compiled shader.
......
......@@ -18,6 +18,10 @@ void TranslatorESSL::translate(TIntermNode* root) {
// Write built-in extension behaviors.
writeExtensionBehavior();
// Write emulated built-in functions if needed.
getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
sink, getShaderType() == SH_FRAGMENT_SHADER);
// Write translated shader.
TOutputESSL outputESSL(sink);
root->traverse(&outputESSL);
......
......@@ -31,6 +31,10 @@ void TranslatorGLSL::translate(TIntermNode* root) {
// Write GLSL version.
writeVersion(getShaderType(), root, sink);
// Write emulated built-in functions if needed.
getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
sink, false);
// Write translated shader.
TOutputGLSL outputGLSL(sink);
root->traverse(&outputGLSL);
......
......@@ -410,7 +410,7 @@ protected:
//
class TIntermUnary : public TIntermOperator {
public:
TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0) {}
TIntermUnary(TOperator o, TType& t) : TIntermOperator(o, t), operand(0), useEmulatedFunction(false) {}
TIntermUnary(TOperator o) : TIntermOperator(o), operand(0) {}
virtual void traverse(TIntermTraverser*);
......@@ -420,8 +420,12 @@ public:
TIntermTyped* getOperand() { return operand; }
bool promote(TInfoSink&);
void setUseEmulatedFunction() { useEmulatedFunction = true; }
bool getUseEmulatedFunction() { return useEmulatedFunction; }
protected:
TIntermTyped* operand;
bool useEmulatedFunction; // if set to true, replace the function call by an emulated one.
};
typedef TVector<TIntermNode*> TIntermSequence;
......
......@@ -283,6 +283,10 @@
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\BuiltInFunctionEmulator.cpp"
>
</File>
<File
RelativePath=".\Compiler.cpp"
>
</File>
......@@ -517,6 +521,10 @@
>
</File>
<File
RelativePath=".\BuiltInFunctionEmulator.h"
>
</File>
<File
RelativePath=".\Common.h"
>
</File>
......
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