Commit 853dc1ab by Olli Etuaho Committed by Nicolas Capens

Add mediump and lowp precision emulation support for GLSL output

This implements the rounding as specified in WEBGL_debug_shader_precision extension proposal for desktop GLSL and ESSL output. The bulk of the new functionality is added in the form of a new EmulatePrecision AST traverser, which inserts calls to the rounding routines angle_frm and angle_frl in the appropriate places, and writes the rounding routines themselves to the shader. Compound assignments which are subject to emulation are transformed from "x op= y" to "angle_compound_op_frm(x, y)", a call to a function which does the appropriate rounding and places the result of the operation to x. The angle_ prefixed names should not clash with user-defined names if name hashing is on. If name hashing is not on, the precision emulation can only be used if the angle_ prefix is reserved for use by ANGLE. To support the rounding routines in output, a new operator type is added for internal helper function calls, which are not subject to name hashing. In ESSL output, all variables are forced to highp when precision emulation is on to ensure consistency with how precision emulation performs on desktop. Comprehensive tests for the added code generation are included. BUG=angle:787 Change-Id: I0d0ad9327888f803a32e79b64b08763c654c913b Reviewed-on: https://chromium-review.googlesource.com/229631Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent 1c9ecfd7
...@@ -223,6 +223,7 @@ typedef struct ...@@ -223,6 +223,7 @@ typedef struct
int EXT_draw_buffers; int EXT_draw_buffers;
int EXT_frag_depth; int EXT_frag_depth;
int EXT_shader_texture_lod; int EXT_shader_texture_lod;
int WEBGL_debug_shader_precision;
// Set to 1 to enable replacing GL_EXT_draw_buffers #extension directives // Set to 1 to enable replacing GL_EXT_draw_buffers #extension directives
// with GL_NV_draw_buffers in ESSL output. This flag can be used to emulate // with GL_NV_draw_buffers in ESSL output. This flag can be used to emulate
......
...@@ -52,6 +52,8 @@ ...@@ -52,6 +52,8 @@
'compiler/translator/Diagnostics.h', 'compiler/translator/Diagnostics.h',
'compiler/translator/DirectiveHandler.cpp', 'compiler/translator/DirectiveHandler.cpp',
'compiler/translator/DirectiveHandler.h', 'compiler/translator/DirectiveHandler.h',
'compiler/translator/EmulatePrecision.cpp',
'compiler/translator/EmulatePrecision.h',
'compiler/translator/ExtensionBehavior.h', 'compiler/translator/ExtensionBehavior.h',
'compiler/translator/FlagStd140Structs.cpp', 'compiler/translator/FlagStd140Structs.cpp',
'compiler/translator/FlagStd140Structs.h', 'compiler/translator/FlagStd140Structs.h',
......
...@@ -182,10 +182,12 @@ bool TCompiler::compile(const char* const shaderStrings[], ...@@ -182,10 +182,12 @@ bool TCompiler::compile(const char* const shaderStrings[],
++firstSource; ++firstSource;
} }
bool debugShaderPrecision = getResources().WEBGL_debug_shader_precision == 1;
TIntermediate intermediate(infoSink); TIntermediate intermediate(infoSink);
TParseContext parseContext(symbolTable, extensionBehavior, intermediate, TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
shaderType, shaderSpec, compileOptions, true, shaderType, shaderSpec, compileOptions, true,
sourcePath, infoSink); sourcePath, infoSink, debugShaderPrecision);
parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh; parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh;
SetGlobalParseContext(&parseContext); SetGlobalParseContext(&parseContext);
...@@ -394,7 +396,8 @@ void TCompiler::setResourceString() ...@@ -394,7 +396,8 @@ void TCompiler::setResourceString()
<< ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors << ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
<< ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset << ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
<< ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset << ":MaxProgramTexelOffset:" << compileResources.MaxProgramTexelOffset
<< ":NV_draw_buffers:" << compileResources.NV_draw_buffers; << ":NV_draw_buffers:" << compileResources.NV_draw_buffers
<< ":WEBGL_debug_shader_precision:" << compileResources.WEBGL_debug_shader_precision;
builtInResourcesString = strstream.str(); builtInResourcesString = strstream.str();
} }
......
...@@ -27,10 +27,12 @@ static TBehavior getBehavior(const std::string& str) ...@@ -27,10 +27,12 @@ static TBehavior getBehavior(const std::string& str)
TDirectiveHandler::TDirectiveHandler(TExtensionBehavior& extBehavior, TDirectiveHandler::TDirectiveHandler(TExtensionBehavior& extBehavior,
TDiagnostics& diagnostics, TDiagnostics& diagnostics,
int& shaderVersion) int& shaderVersion,
bool debugShaderPrecisionSupported)
: mExtensionBehavior(extBehavior), : mExtensionBehavior(extBehavior),
mDiagnostics(diagnostics), mDiagnostics(diagnostics),
mShaderVersion(shaderVersion) mShaderVersion(shaderVersion),
mDebugShaderPrecisionSupported(debugShaderPrecisionSupported)
{ {
} }
...@@ -65,6 +67,7 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc, ...@@ -65,6 +67,7 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
{ {
const char kOptimize[] = "optimize"; const char kOptimize[] = "optimize";
const char kDebug[] = "debug"; const char kDebug[] = "debug";
const char kDebugShaderPrecision[] = "webgl_debug_shader_precision";
const char kOn[] = "on"; const char kOn[] = "on";
const char kOff[] = "off"; const char kOff[] = "off";
...@@ -81,6 +84,12 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc, ...@@ -81,6 +84,12 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
else if (value == kOff) mPragma.debug = false; else if (value == kOff) mPragma.debug = false;
else invalidValue = true; else invalidValue = true;
} }
else if (name == kDebugShaderPrecision && mDebugShaderPrecisionSupported)
{
if (value == kOn) mPragma.debugShaderPrecision = true;
else if (value == kOff) mPragma.debugShaderPrecision = false;
else invalidValue = true;
}
else else
{ {
mDiagnostics.report(pp::Diagnostics::PP_UNRECOGNIZED_PRAGMA, loc, name); mDiagnostics.report(pp::Diagnostics::PP_UNRECOGNIZED_PRAGMA, loc, name);
......
...@@ -18,7 +18,8 @@ class TDirectiveHandler : public pp::DirectiveHandler ...@@ -18,7 +18,8 @@ class TDirectiveHandler : public pp::DirectiveHandler
public: public:
TDirectiveHandler(TExtensionBehavior& extBehavior, TDirectiveHandler(TExtensionBehavior& extBehavior,
TDiagnostics& diagnostics, TDiagnostics& diagnostics,
int& shaderVersion); int& shaderVersion,
bool debugShaderPrecisionSupported);
virtual ~TDirectiveHandler(); virtual ~TDirectiveHandler();
const TPragma& pragma() const { return mPragma; } const TPragma& pragma() const { return mPragma; }
...@@ -44,6 +45,7 @@ class TDirectiveHandler : public pp::DirectiveHandler ...@@ -44,6 +45,7 @@ class TDirectiveHandler : public pp::DirectiveHandler
TExtensionBehavior& mExtensionBehavior; TExtensionBehavior& mExtensionBehavior;
TDiagnostics& mDiagnostics; TDiagnostics& mDiagnostics;
int& mShaderVersion; int& mShaderVersion;
bool mDebugShaderPrecisionSupported;
}; };
#endif // COMPILER_TRANSLATOR_DIRECTIVEHANDLER_H_ #endif // COMPILER_TRANSLATOR_DIRECTIVEHANDLER_H_
//
// Copyright (c) 2002-2014 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/translator/EmulatePrecision.h"
namespace
{
static void writeVectorPrecisionEmulationHelpers(
TInfoSinkBase& sink, ShShaderOutput outputLanguage, unsigned int size)
{
std::stringstream vecTypeStrStr;
if (outputLanguage == SH_ESSL_OUTPUT)
vecTypeStrStr << "highp ";
vecTypeStrStr << "vec" << size;
std::string vecType = vecTypeStrStr.str();
sink <<
vecType << " angle_frm(in " << vecType << " v) {\n"
" v = clamp(v, -65504.0, 65504.0);\n"
" " << vecType << " exponent = floor(log2(abs(v) + 1e-30)) - 10.0;\n"
" bvec" << size << " isNonZero = greaterThanEqual(exponent, vec" << size << "(-25.0));\n"
" v = v * exp2(-exponent);\n"
" v = sign(v) * floor(abs(v));\n"
" return v * exp2(exponent) * vec" << size << "(isNonZero);\n"
"}\n";
sink <<
vecType << " angle_frl(in " << vecType << " v) {\n"
" v = clamp(v, -2.0, 2.0);\n"
" v = v * 256.0;\n"
" v = sign(v) * floor(abs(v));\n"
" return v * 0.00390625;\n"
"}\n";
}
static void writeMatrixPrecisionEmulationHelper(
TInfoSinkBase& sink, ShShaderOutput outputLanguage, unsigned int size, const char *functionName)
{
std::stringstream matTypeStrStr;
if (outputLanguage == SH_ESSL_OUTPUT)
matTypeStrStr << "highp ";
matTypeStrStr << "mat" << size;
std::string matType = matTypeStrStr.str();
sink << matType << " " << functionName << "(in " << matType << " m) {\n"
" " << matType << " rounded;\n";
for (unsigned int i = 0; i < size; ++i)
{
sink << " rounded[" << i << "] = " << functionName << "(m[" << i << "]);\n";
}
sink << " return rounded;\n"
"}\n";
}
static void writeCommonPrecisionEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage)
{
// Write the angle_frm functions that round floating point numbers to
// half precision, and angle_frl functions that round them to minimum lowp
// precision.
// Unoptimized version of angle_frm for single floats:
//
// int webgl_maxNormalExponent(in int exponentBits) {
// int possibleExponents = int(exp2(float(exponentBits)));
// int exponentBias = possibleExponents / 2 - 1;
// int allExponentBitsOne = possibleExponents - 1;
// return (allExponentBitsOne - 1) - exponentBias;
// }
//
// float angle_frm(in float x) {
// int mantissaBits = 10;
// int exponentBits = 5;
// float possibleMantissas = exp2(float(mantissaBits));
// float mantissaMax = 2.0 - 1.0 / possibleMantissas;
// int maxNE = webgl_maxNormalExponent(exponentBits);
// float max = exp2(float(maxNE)) * mantissaMax;
// if (x > max) {
// return max;
// }
// if (x < -max) {
// return -max;
// }
// float exponent = floor(log2(abs(x)));
// if (abs(x) == 0.0 || exponent < -float(maxNE)) {
// return 0.0 * sign(x)
// }
// x = x * exp2(-(exponent - float(mantissaBits)));
// x = sign(x) * floor(abs(x));
// return x * exp2(exponent - float(mantissaBits));
// }
// All numbers with a magnitude less than 2^-15 are subnormal, and are
// flushed to zero.
// Note the constant numbers below:
// a) 65504 is the maximum possible mantissa (1.1111111111 in binary) times
// 2^15, the maximum normal exponent.
// b) 10.0 is the number of mantissa bits.
// c) -25.0 is the minimum normal half-float exponent -15.0 minus the number
// of mantissa bits.
// d) + 1e-30 is to make sure the argument of log2() won't be zero. It can
// only affect the result of log2 on x where abs(x) < 1e-22. Since these
// numbers will be flushed to zero either way (2^-15 is the smallest
// normal positive number), this does not introduce any error.
std::string floatType = "float";
if (outputLanguage == SH_ESSL_OUTPUT)
floatType = "highp float";
sink <<
floatType << " angle_frm(in " << floatType << " x) {\n"
" x = clamp(x, -65504.0, 65504.0);\n"
" " << floatType << " exponent = floor(log2(abs(x) + 1e-30)) - 10.0;\n"
" bool isNonZero = (exponent >= -25.0);\n"
" x = x * exp2(-exponent);\n"
" x = sign(x) * floor(abs(x));\n"
" return x * exp2(exponent) * float(isNonZero);\n"
"}\n";
sink <<
floatType << " angle_frl(in " << floatType << " x) {\n"
" x = clamp(x, -2.0, 2.0);\n"
" x = x * 256.0;\n"
" x = sign(x) * floor(abs(x));\n"
" return x * 0.00390625;\n"
"}\n";
writeVectorPrecisionEmulationHelpers(sink, outputLanguage, 2);
writeVectorPrecisionEmulationHelpers(sink, outputLanguage, 3);
writeVectorPrecisionEmulationHelpers(sink, outputLanguage, 4);
for (unsigned int size = 2; size <= 4; ++size)
{
writeMatrixPrecisionEmulationHelper(sink, outputLanguage, size, "angle_frm");
writeMatrixPrecisionEmulationHelper(sink, outputLanguage, size, "angle_frl");
}
}
static void writeCompoundAssignmentPrecisionEmulation(
TInfoSinkBase& sink, ShShaderOutput outputLanguage,
const char *lType, const char *rType, const char *opStr, const char *opNameStr)
{
std::string lTypeStr = lType;
std::string rTypeStr = rType;
if (outputLanguage == SH_ESSL_OUTPUT)
{
std::stringstream lTypeStrStr;
lTypeStrStr << "highp " << lType;
lTypeStr = lTypeStrStr.str();
std::stringstream rTypeStrStr;
rTypeStrStr << "highp " << rType;
rTypeStr = rTypeStrStr.str();
}
// Note that y should be passed through angle_frm at the function call site,
// but x can't be passed through angle_frm there since it is an inout parameter.
// So only pass x and the result through angle_frm here.
sink <<
lTypeStr << " angle_compound_" << opNameStr << "_frm(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
" x = angle_frm(angle_frm(x) " << opStr << " y);\n"
" return x;\n"
"}\n";
sink <<
lTypeStr << " angle_compound_" << opNameStr << "_frl(inout " << lTypeStr << " x, in " << rTypeStr << " y) {\n"
" x = angle_frl(angle_frm(x) " << opStr << " y);\n"
" return x;\n"
"}\n";
}
const char *getFloatTypeStr(const TType& type)
{
switch (type.getNominalSize())
{
case 1:
return "float";
case 2:
return type.getSecondarySize() > 1 ? "mat2" : "vec2";
case 3:
return type.getSecondarySize() > 1 ? "mat3" : "vec3";
case 4:
return type.getSecondarySize() > 1 ? "mat4" : "vec4";
default:
UNREACHABLE();
return NULL;
}
}
bool canRoundFloat(const TType &type)
{
return type.getBasicType() == EbtFloat && !type.isNonSquareMatrix() && !type.isArray() &&
(type.getPrecision() == EbpLow || type.getPrecision() == EbpMedium);
}
TIntermAggregate *createInternalFunctionCallNode(TString name, TIntermNode *child)
{
TIntermAggregate *callNode = new TIntermAggregate();
callNode->setOp(EOpInternalFunctionCall);
callNode->setName(name);
callNode->getSequence()->push_back(child);
return callNode;
}
TIntermAggregate *createRoundingFunctionCallNode(TIntermTyped *roundedChild)
{
TString roundFunctionName;
if (roundedChild->getPrecision() == EbpMedium)
roundFunctionName = "angle_frm";
else
roundFunctionName = "angle_frl";
return createInternalFunctionCallNode(roundFunctionName, roundedChild);
}
TIntermAggregate *createCompoundAssignmentFunctionCallNode(TIntermTyped *left, TIntermTyped *right, const char *opNameStr)
{
std::stringstream strstr;
if (left->getPrecision() == EbpMedium)
strstr << "angle_compound_" << opNameStr << "_frm";
else
strstr << "angle_compound_" << opNameStr << "_frl";
TString functionName = strstr.str().c_str();
TIntermAggregate *callNode = createInternalFunctionCallNode(functionName, left);
callNode->getSequence()->push_back(right);
return callNode;
}
} // namespace anonymous
EmulatePrecision::EmulatePrecision()
: TIntermTraverser(true, true, true),
mDeclaringVariables(false),
mInLValue(false),
mInFunctionCallOutParameter(false)
{}
void EmulatePrecision::visitSymbol(TIntermSymbol *node)
{
if (canRoundFloat(node->getType()) &&
!mDeclaringVariables && !mInLValue && !mInFunctionCallOutParameter)
{
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createRoundingFunctionCallNode(node);
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
}
}
bool EmulatePrecision::visitBinary(Visit visit, TIntermBinary *node)
{
bool visitChildren = true;
if (node->isAssignment())
{
if (visit == PreVisit)
mInLValue = true;
else if (visit == InVisit)
mInLValue = false;
}
TOperator op = node->getOp();
// RHS of initialize is not being declared.
if (op == EOpInitialize && visit == InVisit)
mDeclaringVariables = false;
if ((op == EOpIndexDirectStruct || op == EOpVectorSwizzle) && visit == InVisit)
visitChildren = false;
if (visit != PreVisit)
return visitChildren;
const TType& type = node->getType();
bool roundFloat = canRoundFloat(type);
if (roundFloat) {
switch (op) {
// Math operators that can result in a float may need to apply rounding to the return
// value. Note that in the case of assignment, the rounding is applied to its return
// value here, not the value being assigned.
case EOpAssign:
case EOpAdd:
case EOpSub:
case EOpMul:
case EOpDiv:
case EOpVectorTimesScalar:
case EOpVectorTimesMatrix:
case EOpMatrixTimesVector:
case EOpMatrixTimesScalar:
case EOpMatrixTimesMatrix:
{
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createRoundingFunctionCallNode(node);
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
break;
}
// Compound assignment cases need to replace the operator with a function call.
case EOpAddAssign:
{
mEmulateCompoundAdd.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "add");
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
break;
}
case EOpSubAssign:
{
mEmulateCompoundSub.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "sub");
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
break;
}
case EOpMulAssign:
case EOpVectorTimesMatrixAssign:
case EOpVectorTimesScalarAssign:
case EOpMatrixTimesScalarAssign:
case EOpMatrixTimesMatrixAssign:
{
mEmulateCompoundMul.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "mul");
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
break;
}
case EOpDivAssign:
{
mEmulateCompoundDiv.insert(TypePair(getFloatTypeStr(type), getFloatTypeStr(node->getRight()->getType())));
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createCompoundAssignmentFunctionCallNode(node->getLeft(), node->getRight(), "div");
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, false));
break;
}
default:
// The rest of the binary operations should not need precision emulation.
break;
}
}
return visitChildren;
}
bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
{
bool visitChildren = true;
switch (node->getOp())
{
case EOpSequence:
case EOpConstructStruct:
// No special handling
break;
case EOpFunction:
if (visit == PreVisit)
{
const TIntermSequence &sequence = *(node->getSequence());
TIntermSequence::const_iterator seqIter = sequence.begin();
TIntermAggregate *params = (*seqIter)->getAsAggregate();
ASSERT(params != NULL);
ASSERT(params->getOp() == EOpParameters);
mFunctionMap[node->getName()] = params->getSequence();
}
break;
case EOpPrototype:
if (visit == PreVisit)
mFunctionMap[node->getName()] = node->getSequence();
visitChildren = false;
break;
case EOpParameters:
visitChildren = false;
break;
case EOpInvariantDeclaration:
visitChildren = false;
break;
case EOpDeclaration:
// Variable declaration.
if (visit == PreVisit)
{
mDeclaringVariables = true;
}
else if (visit == InVisit)
{
mDeclaringVariables = true;
}
else
{
mDeclaringVariables = false;
}
break;
case EOpFunctionCall:
{
// Function call.
bool inFunctionMap = (mFunctionMap.find(node->getName()) != mFunctionMap.end());
if (visit == PreVisit)
{
if (canRoundFloat(node->getType()) && !inFunctionMap) {
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createRoundingFunctionCallNode(node);
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
}
if (inFunctionMap)
{
mSeqIterStack.push_back(mFunctionMap[node->getName()]->begin());
if (mSeqIterStack.back() != mFunctionMap[node->getName()]->end())
{
TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier();
mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut);
}
}
else
{
// The function is not user-defined - it is likely built-in texture function.
// Assume that those do not have out parameters.
mInFunctionCallOutParameter = false;
}
}
else if (visit == InVisit)
{
if (inFunctionMap)
{
++mSeqIterStack.back();
TQualifier qualifier = (*mSeqIterStack.back())->getAsTyped()->getQualifier();
mInFunctionCallOutParameter = (qualifier == EvqOut || qualifier == EvqInOut);
}
}
else
{
if (inFunctionMap)
{
mSeqIterStack.pop_back();
mInFunctionCallOutParameter = false;
}
}
break;
}
default:
if (canRoundFloat(node->getType()) && visit == PreVisit)
{
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createRoundingFunctionCallNode(node);
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
}
break;
}
return visitChildren;
}
bool EmulatePrecision::visitUnary(Visit visit, TIntermUnary *node)
{
switch (node->getOp())
{
case EOpNegative:
case EOpVectorLogicalNot:
case EOpLogicalNot:
break;
case EOpPostIncrement:
case EOpPostDecrement:
case EOpPreIncrement:
case EOpPreDecrement:
if (visit == PreVisit)
mInLValue = true;
else if (visit == PostVisit)
mInLValue = false;
break;
default:
if (canRoundFloat(node->getType()) && visit == PreVisit)
{
TIntermNode *parent = getParentNode();
TIntermNode *replacement = createRoundingFunctionCallNode(node);
mReplacements.push_back(NodeUpdateEntry(parent, node, replacement, true));
}
break;
}
return true;
}
void EmulatePrecision::writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage)
{
// Other languages not yet supported
ASSERT(outputLanguage == SH_GLSL_OUTPUT || outputLanguage == SH_ESSL_OUTPUT);
writeCommonPrecisionEmulationHelpers(sink, outputLanguage);
EmulationSet::const_iterator it;
for (it = mEmulateCompoundAdd.begin(); it != mEmulateCompoundAdd.end(); it++)
writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "+", "add");
for (it = mEmulateCompoundSub.begin(); it != mEmulateCompoundSub.end(); it++)
writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "-", "sub");
for (it = mEmulateCompoundDiv.begin(); it != mEmulateCompoundDiv.end(); it++)
writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "/", "div");
for (it = mEmulateCompoundMul.begin(); it != mEmulateCompoundMul.end(); it++)
writeCompoundAssignmentPrecisionEmulation(sink, outputLanguage, it->lType, it->rType, "*", "mul");
}
//
// Copyright (c) 2002-2014 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 COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
#define COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
#include "common/angleutils.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/IntermNode.h"
#include "GLSLANG/ShaderLang.h"
// This class gathers all compound assignments from the AST and can then write
// the functions required for their precision emulation. This way there is no
// need to write a huge number of variations of the emulated compound assignment
// to every translated shader with emulation enabled.
class EmulatePrecision : public TIntermTraverser
{
public:
EmulatePrecision();
virtual void visitSymbol(TIntermSymbol *node);
virtual bool visitBinary(Visit visit, TIntermBinary *node);
virtual bool visitUnary(Visit visit, TIntermUnary *node);
virtual bool visitAggregate(Visit visit, TIntermAggregate *node);
void writeEmulationHelpers(TInfoSinkBase& sink, ShShaderOutput outputLanguage);
private:
DISALLOW_COPY_AND_ASSIGN(EmulatePrecision);
struct TypePair
{
TypePair(const char *l, const char *r)
: lType(l), rType(r) { }
const char *lType;
const char *rType;
};
struct TypePairComparator
{
bool operator() (const TypePair& l, const TypePair& r) const
{
if (l.lType == r.lType)
return l.rType < r.rType;
return l.lType < r.lType;
}
};
typedef std::set<TypePair, TypePairComparator> EmulationSet;
EmulationSet mEmulateCompoundAdd;
EmulationSet mEmulateCompoundSub;
EmulationSet mEmulateCompoundMul;
EmulationSet mEmulateCompoundDiv;
// Stack of function call parameter iterators
std::vector<TIntermSequence::const_iterator> mSeqIterStack;
bool mDeclaringVariables;
bool mInLValue;
bool mInFunctionCallOutParameter;
struct TStringComparator
{
bool operator() (const TString& a, const TString& b) const { return a.compare(b) < 0; }
};
// Map from function names to their parameter sequences
std::map<TString, TIntermSequence*, TStringComparator> mFunctionMap;
};
#endif // COMPILER_TRANSLATOR_EMULATE_PRECISION_H_
...@@ -1181,3 +1181,29 @@ TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunctio ...@@ -1181,3 +1181,29 @@ TString TIntermTraverser::hash(const TString &name, ShHashFunction64 hashFunctio
TString hashedName = stream.str(); TString hashedName = stream.str();
return hashedName; return hashedName;
} }
void TIntermTraverser::updateTree()
{
for (size_t ii = 0; ii < mReplacements.size(); ++ii)
{
const NodeUpdateEntry& entry = mReplacements[ii];
ASSERT(entry.parent);
bool replaced = entry.parent->replaceChildNode(
entry.original, entry.replacement);
ASSERT(replaced);
if (!entry.originalBecomesChildOfReplacement)
{
// In AST traversing, a parent is visited before its children.
// After we replace a node, if an immediate child is to
// be replaced, we need to make sure we don't update the replaced
// node; instead, we update the replacement node.
for (size_t jj = ii + 1; jj < mReplacements.size(); ++jj)
{
NodeUpdateEntry& entry2 = mReplacements[jj];
if (entry2.parent == entry.original)
entry2.parent = entry.replacement;
}
}
}
}
...@@ -33,6 +33,7 @@ enum TOperator ...@@ -33,6 +33,7 @@ enum TOperator
EOpNull, // if in a node, should only mean a node is still being built EOpNull, // if in a node, should only mean a node is still being built
EOpSequence, // denotes a list of statements, or parameters, etc. EOpSequence, // denotes a list of statements, or parameters, etc.
EOpFunctionCall, EOpFunctionCall,
EOpInternalFunctionCall, // Call to an internal helper function
EOpFunction, // For function definition EOpFunction, // For function definition
EOpParameters, // an aggregate listing the parameters to a function EOpParameters, // an aggregate listing the parameters to a function
...@@ -741,12 +742,38 @@ class TIntermTraverser ...@@ -741,12 +742,38 @@ class TIntermTraverser
const bool postVisit; const bool postVisit;
const bool rightToLeft; const bool rightToLeft;
// If traversers need to replace nodes, they can add the replacements in
// mReplacements during traversal and the user of the traverser should call
// this function after traversal to perform them.
void updateTree();
protected: protected:
int mDepth; int mDepth;
int mMaxDepth; int mMaxDepth;
// All the nodes from root to the current node's parent during traversing. // All the nodes from root to the current node's parent during traversing.
TVector<TIntermNode *> mPath; TVector<TIntermNode *> mPath;
struct NodeUpdateEntry
{
NodeUpdateEntry(TIntermNode *_parent,
TIntermNode *_original,
TIntermNode *_replacement,
bool _originalBecomesChildOfReplacement)
: parent(_parent),
original(_original),
replacement(_replacement),
originalBecomesChildOfReplacement(_originalBecomesChildOfReplacement) {}
TIntermNode *parent;
TIntermNode *original;
TIntermNode *replacement;
bool originalBecomesChildOfReplacement;
};
// During traversing, save all the changes that need to happen into
// mReplacements, then do them by calling updateTree().
std::vector<NodeUpdateEntry> mReplacements;
}; };
// //
......
...@@ -11,8 +11,10 @@ TOutputESSL::TOutputESSL(TInfoSinkBase& objSink, ...@@ -11,8 +11,10 @@ TOutputESSL::TOutputESSL(TInfoSinkBase& objSink,
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
NameMap& nameMap, NameMap& nameMap,
TSymbolTable& symbolTable, TSymbolTable& symbolTable,
int shaderVersion) int shaderVersion,
: TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable, shaderVersion) bool forceHighp)
: TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable, shaderVersion),
mForceHighp(forceHighp)
{ {
} }
...@@ -22,6 +24,9 @@ bool TOutputESSL::writeVariablePrecision(TPrecision precision) ...@@ -22,6 +24,9 @@ bool TOutputESSL::writeVariablePrecision(TPrecision precision)
return false; return false;
TInfoSinkBase& out = objSink(); TInfoSinkBase& out = objSink();
if (mForceHighp)
out << getPrecisionString(EbpHigh);
else
out << getPrecisionString(precision); out << getPrecisionString(precision);
return true; return true;
} }
...@@ -17,10 +17,13 @@ public: ...@@ -17,10 +17,13 @@ public:
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
NameMap& nameMap, NameMap& nameMap,
TSymbolTable& symbolTable, TSymbolTable& symbolTable,
int shaderVersion); int shaderVersion,
bool forceHighp);
protected: protected:
virtual bool writeVariablePrecision(TPrecision precision); virtual bool writeVariablePrecision(TPrecision precision);
private:
bool mForceHighp;
}; };
#endif // COMPILER_TRANSLATOR_OUTPUTESSL_H_ #endif // COMPILER_TRANSLATOR_OUTPUTESSL_H_
...@@ -622,6 +622,15 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -622,6 +622,15 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
else else
out << ")"; out << ")";
break; break;
case EOpInternalFunctionCall:
// Function call to an internal helper function.
if (visit == PreVisit)
out << node->getName() << "(";
else if (visit == InVisit)
out << ", ";
else
out << ")";
break;
case EOpParameters: case EOpParameters:
// Function parameters. // Function parameters.
ASSERT(visit == PreVisit); ASSERT(visit == PreVisit);
......
...@@ -25,7 +25,7 @@ struct TMatrixFields { ...@@ -25,7 +25,7 @@ struct TMatrixFields {
// they can be passed to the parser without needing a global. // they can be passed to the parser without needing a global.
// //
struct TParseContext { struct TParseContext {
TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, sh::GLenum type, ShShaderSpec spec, int options, bool checksPrecErrors, const char* sourcePath, TInfoSink& is) : TParseContext(TSymbolTable& symt, TExtensionBehavior& ext, TIntermediate& interm, sh::GLenum type, ShShaderSpec spec, int options, bool checksPrecErrors, const char* sourcePath, TInfoSink& is, bool debugShaderPrecisionSupported) :
intermediate(interm), intermediate(interm),
symbolTable(symt), symbolTable(symt),
shaderType(type), shaderType(type),
...@@ -42,7 +42,7 @@ struct TParseContext { ...@@ -42,7 +42,7 @@ struct TParseContext {
defaultBlockStorage(EbsShared), defaultBlockStorage(EbsShared),
diagnostics(is), diagnostics(is),
shaderVersion(100), shaderVersion(100),
directiveHandler(ext, diagnostics, shaderVersion), directiveHandler(ext, diagnostics, shaderVersion, debugShaderPrecisionSupported),
preprocessor(&diagnostics, &directiveHandler), preprocessor(&diagnostics, &directiveHandler),
scanner(NULL) { } scanner(NULL) { }
TIntermediate& intermediate; // to hold and build a parse tree TIntermediate& intermediate; // to hold and build a parse tree
......
...@@ -18,11 +18,14 @@ struct TPragma ...@@ -18,11 +18,14 @@ struct TPragma
// By default optimization is turned on and debug is turned off. // By default optimization is turned on and debug is turned off.
TPragma() : optimize(true), debug(false) { } // Precision emulation is turned on by default, but has no effect unless
TPragma(bool o, bool d) : optimize(o), debug(d) { } // the extension is enabled.
TPragma() : optimize(true), debug(false), debugShaderPrecision(true) { }
TPragma(bool o, bool d) : optimize(o), debug(d), debugShaderPrecision(true) { }
bool optimize; bool optimize;
bool debug; bool debug;
bool debugShaderPrecision;
STDGL stdgl; STDGL stdgl;
}; };
......
...@@ -153,6 +153,7 @@ void ShInitBuiltInResources(ShBuiltInResources* resources) ...@@ -153,6 +153,7 @@ void ShInitBuiltInResources(ShBuiltInResources* resources)
resources->EXT_draw_buffers = 0; resources->EXT_draw_buffers = 0;
resources->EXT_frag_depth = 0; resources->EXT_frag_depth = 0;
resources->EXT_shader_texture_lod = 0; resources->EXT_shader_texture_lod = 0;
resources->WEBGL_debug_shader_precision = 0;
resources->NV_draw_buffers = 0; resources->NV_draw_buffers = 0;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "compiler/translator/TranslatorESSL.h" #include "compiler/translator/TranslatorESSL.h"
#include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/OutputESSL.h" #include "compiler/translator/OutputESSL.h"
#include "angle_gl.h" #include "angle_gl.h"
...@@ -21,6 +22,16 @@ void TranslatorESSL::translate(TIntermNode* root) { ...@@ -21,6 +22,16 @@ void TranslatorESSL::translate(TIntermNode* root) {
// Write built-in extension behaviors. // Write built-in extension behaviors.
writeExtensionBehavior(); writeExtensionBehavior();
bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
if (precisionEmulation)
{
EmulatePrecision emulatePrecision;
root->traverse(&emulatePrecision);
emulatePrecision.updateTree();
emulatePrecision.writeEmulationHelpers(sink, SH_ESSL_OUTPUT);
}
// Write emulated built-in functions if needed. // Write emulated built-in functions if needed.
getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition( getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
sink, getShaderType() == GL_FRAGMENT_SHADER); sink, getShaderType() == GL_FRAGMENT_SHADER);
...@@ -29,7 +40,7 @@ void TranslatorESSL::translate(TIntermNode* root) { ...@@ -29,7 +40,7 @@ void TranslatorESSL::translate(TIntermNode* root) {
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink); getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
// Write translated shader. // Write translated shader.
TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable(), getShaderVersion()); TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable(), getShaderVersion(), precisionEmulation);
root->traverse(&outputESSL); root->traverse(&outputESSL);
} }
......
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
#include "compiler/translator/TranslatorGLSL.h" #include "compiler/translator/TranslatorGLSL.h"
#include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/OutputGLSL.h" #include "compiler/translator/OutputGLSL.h"
#include "compiler/translator/VersionGLSL.h" #include "compiler/translator/VersionGLSL.h"
TranslatorGLSL::TranslatorGLSL(sh::GLenum type, ShShaderSpec spec) TranslatorGLSL::TranslatorGLSL(sh::GLenum type, ShShaderSpec spec)
: TCompiler(type, spec, SH_GLSL_OUTPUT) { : TCompiler(type, spec, SH_GLSL_OUTPUT) {
} }
...@@ -24,6 +26,16 @@ void TranslatorGLSL::translate(TIntermNode* root) { ...@@ -24,6 +26,16 @@ void TranslatorGLSL::translate(TIntermNode* root) {
// Write extension behaviour as needed // Write extension behaviour as needed
writeExtensionBehavior(); writeExtensionBehavior();
bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
if (precisionEmulation)
{
EmulatePrecision emulatePrecision;
root->traverse(&emulatePrecision);
emulatePrecision.updateTree();
emulatePrecision.writeEmulationHelpers(sink, SH_GLSL_OUTPUT);
}
// Write emulated built-in functions if needed. // Write emulated built-in functions if needed.
getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition( getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
sink, false); sink, false);
......
...@@ -325,6 +325,10 @@ class TType ...@@ -325,6 +325,10 @@ class TType
{ {
return primarySize > 1 && secondarySize > 1; return primarySize > 1 && secondarySize > 1;
} }
bool isNonSquareMatrix() const
{
return isMatrix() && primarySize != secondarySize;
}
bool isArray() const bool isArray() const
{ {
return array ? true : false; return array ? true : false;
......
...@@ -50,32 +50,8 @@ bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node) ...@@ -50,32 +50,8 @@ bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node)
} }
if (replacement) if (replacement)
{ {
replacements.push_back( mReplacements.push_back(
NodeUpdateEntry(getParentNode(), node, replacement)); NodeUpdateEntry(getParentNode(), node, replacement, false));
} }
return true; return true;
} }
void UnfoldShortCircuitAST::updateTree()
{
for (size_t ii = 0; ii < replacements.size(); ++ii)
{
const NodeUpdateEntry& entry = replacements[ii];
ASSERT(entry.parent);
bool replaced = entry.parent->replaceChildNode(
entry.original, entry.replacement);
ASSERT(replaced);
// In AST traversing, a parent is visited before its children.
// After we replace a node, if an immediate child is to
// be replaced, we need to make sure we don't update the replaced
// node; instead, we update the replacement node.
for (size_t jj = ii + 1; jj < replacements.size(); ++jj)
{
NodeUpdateEntry& entry2 = replacements[jj];
if (entry2.parent == entry.original)
entry2.parent = entry.replacement;
}
}
}
...@@ -24,27 +24,7 @@ class UnfoldShortCircuitAST : public TIntermTraverser ...@@ -24,27 +24,7 @@ class UnfoldShortCircuitAST : public TIntermTraverser
virtual bool visitBinary(Visit visit, TIntermBinary *); virtual bool visitBinary(Visit visit, TIntermBinary *);
void updateTree();
private: private:
struct NodeUpdateEntry
{
NodeUpdateEntry(TIntermNode *_parent,
TIntermNode *_original,
TIntermNode *_replacement)
: parent(_parent),
original(_original),
replacement(_replacement) {}
TIntermNode *parent;
TIntermNode *original;
TIntermNode *replacement;
};
// During traversing, save all the replacements that need to happen;
// then replace them by calling updateNodes().
std::vector<NodeUpdateEntry> replacements;
DISALLOW_COPY_AND_ASSIGN(UnfoldShortCircuitAST); DISALLOW_COPY_AND_ASSIGN(UnfoldShortCircuitAST);
}; };
......
//
// Copyright (c) 2014 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.
//
// DebugShaderPrecision_test.cpp:
// Tests for writing the code for shader precision emulation.
//
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "GLSLANG/ShaderLang.h"
#include "compiler/translator/TranslatorESSL.h"
#include "compiler/translator/TranslatorGLSL.h"
class DebugShaderPrecisionTest : public testing::Test
{
public:
DebugShaderPrecisionTest() {}
protected:
virtual void SetUp()
{
ShBuiltInResources resources;
ShInitBuiltInResources(&resources);
resources.WEBGL_debug_shader_precision = 1;
mTranslatorESSL = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_GLES2_SPEC);
ASSERT_TRUE(mTranslatorESSL->Init(resources));
mTranslatorGLSL = new TranslatorGLSL(GL_FRAGMENT_SHADER, SH_GLES2_SPEC);
ASSERT_TRUE(mTranslatorGLSL->Init(resources));
}
virtual void TearDown()
{
delete mTranslatorESSL;
delete mTranslatorGLSL;
}
void compile(const std::string& shaderString)
{
const char *shaderStrings[] = { shaderString.c_str() };
bool compilationSuccess = mTranslatorESSL->compile(shaderStrings, 1, SH_OBJECT_CODE);
TInfoSink &infoSink = mTranslatorESSL->getInfoSink();
mESSLCode = infoSink.obj.c_str();
if (!compilationSuccess)
FAIL() << "Shader compilation into ESSL failed " << infoSink.info.c_str();
compilationSuccess = mTranslatorGLSL->compile(shaderStrings, 1, SH_OBJECT_CODE);
infoSink = mTranslatorGLSL->getInfoSink();
mGLSLCode = infoSink.obj.c_str();
if (!compilationSuccess)
FAIL() << "Shader compilation into GLSL failed " << infoSink.info.c_str();
}
bool foundInESSLCode(const char* stringToFind)
{
return mESSLCode.find(stringToFind) != std::string::npos;
}
bool foundInGLSLCode(const char* stringToFind)
{
return mGLSLCode.find(stringToFind) != std::string::npos;
}
bool foundInCode(const char* stringToFind)
{
return foundInESSLCode(stringToFind) && foundInGLSLCode(stringToFind);
}
bool notFoundInCode(const char* stringToFind)
{
return !foundInESSLCode(stringToFind) && !foundInGLSLCode(stringToFind);
}
private:
TranslatorESSL *mTranslatorESSL;
TranslatorGLSL *mTranslatorGLSL;
std::string mESSLCode;
std::string mGLSLCode;
};
class NoDebugShaderPrecisionTest : public testing::Test
{
public:
NoDebugShaderPrecisionTest() {}
protected:
virtual void SetUp()
{
ShBuiltInResources resources;
ShInitBuiltInResources(&resources);
mTranslator = new TranslatorGLSL(GL_FRAGMENT_SHADER, SH_GLES2_SPEC);
ASSERT_TRUE(mTranslator->Init(resources));
}
virtual void TearDown()
{
delete mTranslator;
}
bool compile(const std::string& shaderString)
{
const char *shaderStrings[] = { shaderString.c_str() };
bool compilationSuccess = mTranslator->compile(shaderStrings, 1, SH_OBJECT_CODE);
TInfoSink &infoSink = mTranslator->getInfoSink();
mCode = infoSink.obj.c_str();
return compilationSuccess;
}
bool foundInCode(const char* stringToFind)
{
return mCode.find(stringToFind) != std::string::npos;
}
private:
TranslatorGLSL *mTranslator;
std::string mCode;
};
TEST_F(DebugShaderPrecisionTest, RoundingFunctionsDefined)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float u;\n"
"void main() {\n"
" gl_FragColor = vec4(u);\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode("highp float angle_frm(in highp float"));
ASSERT_TRUE(foundInESSLCode("highp vec2 angle_frm(in highp vec2"));
ASSERT_TRUE(foundInESSLCode("highp vec3 angle_frm(in highp vec3"));
ASSERT_TRUE(foundInESSLCode("highp vec4 angle_frm(in highp vec4"));
ASSERT_TRUE(foundInESSLCode("highp mat2 angle_frm(in highp mat2"));
ASSERT_TRUE(foundInESSLCode("highp mat3 angle_frm(in highp mat3"));
ASSERT_TRUE(foundInESSLCode("highp mat4 angle_frm(in highp mat4"));
ASSERT_TRUE(foundInESSLCode("highp float angle_frl(in highp float"));
ASSERT_TRUE(foundInESSLCode("highp vec2 angle_frl(in highp vec2"));
ASSERT_TRUE(foundInESSLCode("highp vec3 angle_frl(in highp vec3"));
ASSERT_TRUE(foundInESSLCode("highp vec4 angle_frl(in highp vec4"));
ASSERT_TRUE(foundInESSLCode("highp mat2 angle_frl(in highp mat2"));
ASSERT_TRUE(foundInESSLCode("highp mat3 angle_frl(in highp mat3"));
ASSERT_TRUE(foundInESSLCode("highp mat4 angle_frl(in highp mat4"));
ASSERT_TRUE(foundInGLSLCode("float angle_frm(in float"));
ASSERT_TRUE(foundInGLSLCode("vec2 angle_frm(in vec2"));
ASSERT_TRUE(foundInGLSLCode("vec3 angle_frm(in vec3"));
ASSERT_TRUE(foundInGLSLCode("vec4 angle_frm(in vec4"));
ASSERT_TRUE(foundInGLSLCode("mat2 angle_frm(in mat2"));
ASSERT_TRUE(foundInGLSLCode("mat3 angle_frm(in mat3"));
ASSERT_TRUE(foundInGLSLCode("mat4 angle_frm(in mat4"));
ASSERT_TRUE(foundInGLSLCode("float angle_frl(in float"));
ASSERT_TRUE(foundInGLSLCode("vec2 angle_frl(in vec2"));
ASSERT_TRUE(foundInGLSLCode("vec3 angle_frl(in vec3"));
ASSERT_TRUE(foundInGLSLCode("vec4 angle_frl(in vec4"));
ASSERT_TRUE(foundInGLSLCode("mat2 angle_frl(in mat2"));
ASSERT_TRUE(foundInGLSLCode("mat3 angle_frl(in mat3"));
ASSERT_TRUE(foundInGLSLCode("mat4 angle_frl(in mat4"));
};
TEST_F(DebugShaderPrecisionTest, PragmaDisablesEmulation)
{
const std::string &shaderString =
"#pragma webgl_debug_shader_precision(off)\n"
"precision mediump float;\n"
"uniform float u;\n"
"void main() {\n"
" gl_FragColor = vec4(u);\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(notFoundInCode("angle_frm"));
const std::string &shaderStringPragmaOn =
"#pragma webgl_debug_shader_precision(on)\n"
"precision mediump float;\n"
"uniform float u;\n"
"void main() {\n"
" gl_FragColor = vec4(u);\n"
"}\n";
compile(shaderStringPragmaOn);
ASSERT_TRUE(foundInCode("angle_frm"));
};
// Emulation can't be toggled on for only a part of a shader.
// Only the last pragma in the shader has an effect.
TEST_F(DebugShaderPrecisionTest, MultiplePragmas)
{
const std::string &shaderString =
"#pragma webgl_debug_shader_precision(off)\n"
"precision mediump float;\n"
"uniform float u;\n"
"void main() {\n"
" gl_FragColor = vec4(u);\n"
"}\n"
"#pragma webgl_debug_shader_precision(on)\n";
compile(shaderString);
ASSERT_TRUE(foundInCode("angle_frm"));
};
TEST_F(NoDebugShaderPrecisionTest, HelpersWrittenOnlyWithExtension)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float u;\n"
"void main() {\n"
" gl_FragColor = vec4(u);\n"
"}\n";
ASSERT_TRUE(compile(shaderString));
ASSERT_FALSE(foundInCode("angle_frm"));
};
TEST_F(NoDebugShaderPrecisionTest, PragmaHasEffectsOnlyWithExtension)
{
const std::string &shaderString =
"#pragma webgl_debug_shader_precision(on)\n"
"precision mediump float;\n"
"uniform float u;\n"
"void main() {\n"
" gl_FragColor = vec4(u);\n"
"}\n";
ASSERT_TRUE(compile(shaderString));
ASSERT_FALSE(foundInCode("angle_frm"));
};
TEST_F(DebugShaderPrecisionTest, DeclarationsAndConstants)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 f;\n"
"uniform float uu, uu2;\n"
"varying float vv, vv2;\n"
"float gg = 0.0, gg2;\n"
"void main() {\n"
" float aa = 0.0, aa2;\n"
" gl_FragColor = f;\n"
"}\n";
compile(shaderString);
// Declarations or constants should not have rounding inserted around them
ASSERT_TRUE(notFoundInCode("angle_frm(0"));
ASSERT_TRUE(notFoundInCode("angle_frm(uu"));
ASSERT_TRUE(notFoundInCode("angle_frm(vv"));
ASSERT_TRUE(notFoundInCode("angle_frm(gg"));
ASSERT_TRUE(notFoundInCode("angle_frm(aa"));
};
TEST_F(DebugShaderPrecisionTest, InitializerRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float u;\n"
"void main() {\n"
" float a = u;\n"
" gl_FragColor = vec4(a);\n"
"}\n";
compile(shaderString);
// An expression that's part of initialization should have rounding
ASSERT_TRUE(foundInCode("angle_frm(u)"));
};
TEST_F(DebugShaderPrecisionTest, CompoundAddFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"uniform vec4 u2;\n"
"void main() {\n"
" vec4 v = u;\n"
" v += u2;\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp vec4 angle_compound_add_frm(inout highp vec4 x, in highp vec4 y) {\n"
" x = angle_frm(angle_frm(x) + y);"
));
ASSERT_TRUE(foundInGLSLCode(
"vec4 angle_compound_add_frm(inout vec4 x, in vec4 y) {\n"
" x = angle_frm(angle_frm(x) + y);"
));
ASSERT_TRUE(foundInCode("angle_compound_add_frm(v, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("+="));
};
TEST_F(DebugShaderPrecisionTest, CompoundSubFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"uniform vec4 u2;\n"
"void main() {\n"
" vec4 v = u;\n"
" v -= u2;\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp vec4 angle_compound_sub_frm(inout highp vec4 x, in highp vec4 y) {\n"
" x = angle_frm(angle_frm(x) - y);"
));
ASSERT_TRUE(foundInGLSLCode(
"vec4 angle_compound_sub_frm(inout vec4 x, in vec4 y) {\n"
" x = angle_frm(angle_frm(x) - y);"
));
ASSERT_TRUE(foundInCode("angle_compound_sub_frm(v, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("-="));
};
TEST_F(DebugShaderPrecisionTest, CompoundDivFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"uniform vec4 u2;\n"
"void main() {\n"
" vec4 v = u;\n"
" v /= u2;\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp vec4 angle_compound_div_frm(inout highp vec4 x, in highp vec4 y) {\n"
" x = angle_frm(angle_frm(x) / y);"
));
ASSERT_TRUE(foundInGLSLCode(
"vec4 angle_compound_div_frm(inout vec4 x, in vec4 y) {\n"
" x = angle_frm(angle_frm(x) / y);"
));
ASSERT_TRUE(foundInCode("angle_compound_div_frm(v, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("/="));
};
TEST_F(DebugShaderPrecisionTest, CompoundMulFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"uniform vec4 u2;\n"
"void main() {\n"
" vec4 v = u;\n"
" v *= u2;\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp vec4 angle_compound_mul_frm(inout highp vec4 x, in highp vec4 y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInGLSLCode(
"vec4 angle_compound_mul_frm(inout vec4 x, in vec4 y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInCode("angle_compound_mul_frm(v, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("*="));
};
TEST_F(DebugShaderPrecisionTest, CompoundAddVectorPlusScalarFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"uniform float u2;\n"
"void main() {\n"
" vec4 v = u;\n"
" v += u2;\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp vec4 angle_compound_add_frm(inout highp vec4 x, in highp float y) {\n"
" x = angle_frm(angle_frm(x) + y);"
));
ASSERT_TRUE(foundInGLSLCode(
"vec4 angle_compound_add_frm(inout vec4 x, in float y) {\n"
" x = angle_frm(angle_frm(x) + y);"
));
ASSERT_TRUE(foundInCode("angle_compound_add_frm(v, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("+="));
};
TEST_F(DebugShaderPrecisionTest, CompoundMatrixTimesMatrixFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform mat4 u;\n"
"uniform mat4 u2;\n"
"void main() {\n"
" mat4 m = u;\n"
" m *= u2;\n"
" gl_FragColor = m[0];\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp mat4 angle_compound_mul_frm(inout highp mat4 x, in highp mat4 y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInGLSLCode(
"mat4 angle_compound_mul_frm(inout mat4 x, in mat4 y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInCode("angle_compound_mul_frm(m, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("*="));
};
TEST_F(DebugShaderPrecisionTest, CompoundMatrixTimesScalarFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform mat4 u;\n"
"uniform float u2;\n"
"void main() {\n"
" mat4 m = u;\n"
" m *= u2;\n"
" gl_FragColor = m[0];\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp mat4 angle_compound_mul_frm(inout highp mat4 x, in highp float y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInGLSLCode(
"mat4 angle_compound_mul_frm(inout mat4 x, in float y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInCode("angle_compound_mul_frm(m, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("*="));
};
TEST_F(DebugShaderPrecisionTest, CompoundVectorTimesMatrixFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"uniform mat4 u2;\n"
"void main() {\n"
" vec4 v = u;\n"
" v *= u2;\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp vec4 angle_compound_mul_frm(inout highp vec4 x, in highp mat4 y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInGLSLCode("vec4 angle_compound_mul_frm(inout vec4 x, in mat4 y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInCode("angle_compound_mul_frm(v, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("*="));
};
TEST_F(DebugShaderPrecisionTest, CompoundVectorTimesScalarFunction)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"uniform float u2;\n"
"void main() {\n"
" vec4 v = u;\n"
" v *= u2;\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInESSLCode(
"highp vec4 angle_compound_mul_frm(inout highp vec4 x, in highp float y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInGLSLCode(
"vec4 angle_compound_mul_frm(inout vec4 x, in float y) {\n"
" x = angle_frm(angle_frm(x) * y);"
));
ASSERT_TRUE(foundInCode("angle_compound_mul_frm(v, angle_frm(u2));"));
ASSERT_TRUE(notFoundInCode("*="));
};
TEST_F(DebugShaderPrecisionTest, BinaryMathRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u1;\n"
"uniform vec4 u2;\n"
"uniform vec4 u3;\n"
"uniform vec4 u4;\n"
"uniform vec4 u5;\n"
"void main() {\n"
" vec4 v1 = u1 + u2;\n"
" vec4 v2 = u2 - u3;\n"
" vec4 v3 = u3 * u4;\n"
" vec4 v4 = u4 / u5;\n"
" vec4 v5;\n"
" vec4 v6 = (v5 = u5);\n"
" gl_FragColor = v1 + v2 + v3 + v4;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInCode("v1 = angle_frm((angle_frm(u1) + angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v2 = angle_frm((angle_frm(u2) - angle_frm(u3)))"));
ASSERT_TRUE(foundInCode("v3 = angle_frm((angle_frm(u3) * angle_frm(u4)))"));
ASSERT_TRUE(foundInCode("v4 = angle_frm((angle_frm(u4) / angle_frm(u5)))"));
ASSERT_TRUE(foundInCode("v6 = angle_frm((v5 = angle_frm(u5)))"));
};
TEST_F(DebugShaderPrecisionTest, BuiltInMathFunctionRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u1;\n"
"uniform vec4 u2;\n"
"uniform vec4 u3;\n"
"uniform float uf;\n"
"uniform float uf2;\n"
"uniform vec3 uf31;\n"
"uniform vec3 uf32;\n"
"uniform mat4 um1;\n"
"uniform mat4 um2;\n"
"void main() {\n"
" vec4 v1 = radians(u1);\n"
" vec4 v2 = degrees(u1);\n"
" vec4 v3 = sin(u1);\n"
" vec4 v4 = cos(u1);\n"
" vec4 v5 = tan(u1);\n"
" vec4 v6 = asin(u1);\n"
" vec4 v7 = acos(u1);\n"
" vec4 v8 = atan(u1);\n"
" vec4 v9 = atan(u1, u2);\n"
" vec4 v10 = pow(u1, u2);\n"
" vec4 v11 = exp(u1);\n"
" vec4 v12 = log(u1);\n"
" vec4 v13 = exp2(u1);\n"
" vec4 v14 = log2(u1);\n"
" vec4 v15 = sqrt(u1);\n"
" vec4 v16 = inversesqrt(u1);\n"
" vec4 v17 = abs(u1);\n"
" vec4 v18 = sign(u1);\n"
" vec4 v19 = floor(u1);\n"
" vec4 v20 = ceil(u1);\n"
" vec4 v21 = fract(u1);\n"
" vec4 v22 = mod(u1, uf);\n"
" vec4 v23 = mod(u1, u2);\n"
" vec4 v24 = min(u1, uf);\n"
" vec4 v25 = min(u1, u2);\n"
" vec4 v26 = max(u1, uf);\n"
" vec4 v27 = max(u1, u2);\n"
" vec4 v28 = clamp(u1, u2, u3);\n"
" vec4 v29 = clamp(u1, uf, uf2);\n"
" vec4 v30 = mix(u1, u2, u3);\n"
" vec4 v31 = mix(u1, u2, uf);\n"
" vec4 v32 = step(u1, u2);\n"
" vec4 v33 = step(uf, u1);\n"
" vec4 v34 = smoothstep(u1, u2, u3);\n"
" vec4 v35 = smoothstep(uf, uf2, u1);\n"
" vec4 v36 = normalize(u1);\n"
" vec4 v37 = faceforward(u1, u2, u3);\n"
" vec4 v38 = reflect(u1, u2);\n"
" vec4 v39 = refract(u1, u2, uf);\n"
" float f1 = length(u1);\n"
" float f2 = distance(u1, u2);\n"
" float f3 = dot(u1, u2);\n"
" vec3 vf31 = cross(uf31, uf32);\n"
" mat4 m1 = matrixCompMult(um1, um2);\n"
" gl_FragColor = v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 +"
"v11 + v12 + v13 + v14 + v15 + v16 + v17 + v18 + v19 + v20 +"
"v21 + v22 + v23 + v24 + v25 + v26 + v27 + v28 + v29 + v30 +"
"v31 + v32 + v33 + v34 + v35 + v36 + v37 + v38 + v39 +"
"vec4(f1, f2, f3, 0.0) + vec4(vf31, 0.0) + m1[0];\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInCode("v1 = angle_frm(radians(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v2 = angle_frm(degrees(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v3 = angle_frm(sin(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v4 = angle_frm(cos(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v5 = angle_frm(tan(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v6 = angle_frm(asin(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v7 = angle_frm(acos(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v8 = angle_frm(atan(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v9 = angle_frm(atan(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v10 = angle_frm(pow(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v11 = angle_frm(exp(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v12 = angle_frm(log(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v13 = angle_frm(exp2(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v14 = angle_frm(log2(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v15 = angle_frm(sqrt(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v16 = angle_frm(inversesqrt(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v17 = angle_frm(abs(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v18 = angle_frm(sign(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v19 = angle_frm(floor(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v20 = angle_frm(ceil(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v21 = angle_frm(fract(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v22 = angle_frm(mod(angle_frm(u1), angle_frm(uf)))"));
ASSERT_TRUE(foundInCode("v23 = angle_frm(mod(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v24 = angle_frm(min(angle_frm(u1), angle_frm(uf)))"));
ASSERT_TRUE(foundInCode("v25 = angle_frm(min(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v26 = angle_frm(max(angle_frm(u1), angle_frm(uf)))"));
ASSERT_TRUE(foundInCode("v27 = angle_frm(max(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v28 = angle_frm(clamp(angle_frm(u1), angle_frm(u2), angle_frm(u3)))"));
ASSERT_TRUE(foundInCode("v29 = angle_frm(clamp(angle_frm(u1), angle_frm(uf), angle_frm(uf2)))"));
ASSERT_TRUE(foundInCode("v30 = angle_frm(mix(angle_frm(u1), angle_frm(u2), angle_frm(u3)))"));
ASSERT_TRUE(foundInCode("v31 = angle_frm(mix(angle_frm(u1), angle_frm(u2), angle_frm(uf)))"));
ASSERT_TRUE(foundInCode("v32 = angle_frm(step(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v33 = angle_frm(step(angle_frm(uf), angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v34 = angle_frm(smoothstep(angle_frm(u1), angle_frm(u2), angle_frm(u3)))"));
ASSERT_TRUE(foundInCode("v35 = angle_frm(smoothstep(angle_frm(uf), angle_frm(uf2), angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v36 = angle_frm(normalize(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("v37 = angle_frm(faceforward(angle_frm(u1), angle_frm(u2), angle_frm(u3)))"));
ASSERT_TRUE(foundInCode("v38 = angle_frm(reflect(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("v39 = angle_frm(refract(angle_frm(u1), angle_frm(u2), angle_frm(uf)))"));
ASSERT_TRUE(foundInCode("f1 = angle_frm(length(angle_frm(u1)))"));
ASSERT_TRUE(foundInCode("f2 = angle_frm(distance(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("f3 = angle_frm(dot(angle_frm(u1), angle_frm(u2)))"));
ASSERT_TRUE(foundInCode("vf31 = angle_frm(cross(angle_frm(uf31), angle_frm(uf32)))"));
ASSERT_TRUE(foundInCode("m1 = angle_frm(matrixCompMult(angle_frm(um1), angle_frm(um2)))"));
};
TEST_F(DebugShaderPrecisionTest, BuiltInRelationalFunctionRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u1;\n"
"uniform vec4 u2;\n"
"void main() {\n"
" bvec4 bv1 = lessThan(u1, u2);\n"
" bvec4 bv2 = lessThanEqual(u1, u2);\n"
" bvec4 bv3 = greaterThan(u1, u2);\n"
" bvec4 bv4 = greaterThanEqual(u1, u2);\n"
" bvec4 bv5 = equal(u1, u2);\n"
" bvec4 bv6 = notEqual(u1, u2);\n"
" gl_FragColor = vec4(bv1) + vec4(bv2) + vec4(bv3) + vec4(bv4) + vec4(bv5) + vec4(bv6);\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInCode("bv1 = lessThan(angle_frm(u1), angle_frm(u2))"));
ASSERT_TRUE(foundInCode("bv2 = lessThanEqual(angle_frm(u1), angle_frm(u2))"));
ASSERT_TRUE(foundInCode("bv3 = greaterThan(angle_frm(u1), angle_frm(u2))"));
ASSERT_TRUE(foundInCode("bv4 = greaterThanEqual(angle_frm(u1), angle_frm(u2))"));
ASSERT_TRUE(foundInCode("bv5 = equal(angle_frm(u1), angle_frm(u2))"));
ASSERT_TRUE(foundInCode("bv6 = notEqual(angle_frm(u1), angle_frm(u2))"));
};
TEST_F(DebugShaderPrecisionTest, ConstructorRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"precision mediump int;\n"
"uniform float u1;\n"
"uniform float u2;\n"
"uniform float u3;\n"
"uniform float u4;\n"
"uniform ivec4 uiv;\n"
"void main() {\n"
" vec4 v1 = vec4(u1, u2, u3, u4);\n"
" vec4 v2 = vec4(uiv);\n"
" gl_FragColor = v1 + v2;\n"
"}\n";
compile(shaderString);
// Note: this is suboptimal for the case taking four floats, but optimizing would be tricky.
ASSERT_TRUE(foundInCode("v1 = angle_frm(vec4(angle_frm(u1), angle_frm(u2), angle_frm(u3), angle_frm(u4)))"));
ASSERT_TRUE(foundInCode("v2 = angle_frm(vec4(uiv))"));
};
TEST_F(DebugShaderPrecisionTest, StructConstructorNoRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"struct S { mediump vec4 a; };\n"
"uniform vec4 u;\n"
"void main() {\n"
" S s = S(u);\n"
" gl_FragColor = s.a;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInCode("s = S(angle_frm(u))"));
ASSERT_TRUE(notFoundInCode("angle_frm(S"));
};
TEST_F(DebugShaderPrecisionTest, SwizzleRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u;\n"
"void main() {\n"
" vec4 v = u.xyxy;"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInCode("v = angle_frm(u).xyxy"));
};
TEST_F(DebugShaderPrecisionTest, BuiltInTexFunctionRounding)
{
const std::string &shaderString =
"precision mediump float;\n"
"precision lowp sampler2D;\n"
"uniform vec2 u;\n"
"uniform sampler2D s;\n"
"void main() {\n"
" lowp vec4 v = texture2D(s, u);\n"
" gl_FragColor = v;\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(foundInCode("v = angle_frl(texture2D(s, angle_frm(u)))"));
};
TEST_F(DebugShaderPrecisionTest, FunctionCallParameterQualifiersFromDefinition)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u1;\n"
"uniform vec4 u2;\n"
"uniform vec4 u3;\n"
"uniform vec4 u4;\n"
"uniform vec4 u5;\n"
"vec4 add(in vec4 x, in vec4 y) {\n"
" return x + y;\n"
"}\n"
"void compound_add(inout vec4 x, in vec4 y) {\n"
" x = x + y;\n"
"}\n"
"void add_to_last(in vec4 x, in vec4 y, out vec4 z) {\n"
" z = x + y;\n"
"}\n"
"void main() {\n"
" vec4 v = add(u1, u2);\n"
" compound_add(v, u3);\n"
" vec4 v2;\n"
" add_to_last(u4, u5, v2);\n"
" gl_FragColor = v + v2;\n"
"}\n";
compile(shaderString);
// Note that this is not optimal code, there are redundant frm calls.
// However, getting the implementation working when other operations
// are nested within function calls would be tricky if to get right
// otherwise.
// Test in parameters
ASSERT_TRUE(foundInCode("v = add(angle_frm(u1), angle_frm(u2))"));
// Test inout parameter
ASSERT_TRUE(foundInCode("compound_add(v, angle_frm(u3))"));
// Test out parameter
ASSERT_TRUE(foundInCode("add_to_last(angle_frm(u4), angle_frm(u5), v2)"));
};
TEST_F(DebugShaderPrecisionTest, FunctionCallParameterQualifiersFromPrototype)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u1;\n"
"uniform vec4 u2;\n"
"uniform vec4 u3;\n"
"uniform vec4 u4;\n"
"uniform vec4 u5;\n"
"vec4 add(in vec4 x, in vec4 y);\n"
"void compound_add(inout vec4 x, in vec4 y);\n"
"void add_to_last(in vec4 x, in vec4 y, out vec4 z);\n"
"void main() {\n"
" vec4 v = add(u1, u2);\n"
" compound_add(v, u3);\n"
" vec4 v2;\n"
" add_to_last(u4, u5, v2);\n"
" gl_FragColor = v + v2;\n"
"}\n"
"vec4 add(in vec4 x, in vec4 y) {\n"
" return x + y;\n"
"}\n"
"void compound_add(inout vec4 x, in vec4 y) {\n"
" x = x + y;\n"
"}\n"
"void add_to_last(in vec4 x, in vec4 y, out vec4 z) {\n"
" z = x + y;\n"
"}\n";
compile(shaderString);
// Test in parameters
ASSERT_TRUE(foundInCode("v = add(angle_frm(u1), angle_frm(u2))"));
// Test inout parameter
ASSERT_TRUE(foundInCode("compound_add(v, angle_frm(u3))"));
// Test out parameter
ASSERT_TRUE(foundInCode("add_to_last(angle_frm(u4), angle_frm(u5), v2)"));
};
TEST_F(DebugShaderPrecisionTest, NestedFunctionCalls)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform vec4 u1;\n"
"uniform vec4 u2;\n"
"uniform vec4 u3;\n"
"vec4 add(in vec4 x, in vec4 y) {\n"
" return x + y;\n"
"}\n"
"vec4 compound_add(inout vec4 x, in vec4 y) {\n"
" x = x + y;\n"
" return x;\n"
"}\n"
"void main() {\n"
" vec4 v = u1;\n"
" vec4 v2 = add(compound_add(v, u2), fract(u3));\n"
" gl_FragColor = v + v2;\n"
"}\n";
compile(shaderString);
// Test nested calls
ASSERT_TRUE(foundInCode("v2 = add(compound_add(v, angle_frm(u2)), angle_frm(fract(angle_frm(u3))))"));
};
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