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
int EXT_draw_buffers;
int EXT_frag_depth;
int EXT_shader_texture_lod;
int WEBGL_debug_shader_precision;
// 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
......
......@@ -52,6 +52,8 @@
'compiler/translator/Diagnostics.h',
'compiler/translator/DirectiveHandler.cpp',
'compiler/translator/DirectiveHandler.h',
'compiler/translator/EmulatePrecision.cpp',
'compiler/translator/EmulatePrecision.h',
'compiler/translator/ExtensionBehavior.h',
'compiler/translator/FlagStd140Structs.cpp',
'compiler/translator/FlagStd140Structs.h',
......
......@@ -182,10 +182,12 @@ bool TCompiler::compile(const char* const shaderStrings[],
++firstSource;
}
bool debugShaderPrecision = getResources().WEBGL_debug_shader_precision == 1;
TIntermediate intermediate(infoSink);
TParseContext parseContext(symbolTable, extensionBehavior, intermediate,
shaderType, shaderSpec, compileOptions, true,
sourcePath, infoSink);
sourcePath, infoSink, debugShaderPrecision);
parseContext.fragmentPrecisionHigh = fragmentPrecisionHigh;
SetGlobalParseContext(&parseContext);
......@@ -394,7 +396,8 @@ void TCompiler::setResourceString()
<< ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
<< ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
<< ":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();
}
......
......@@ -27,10 +27,12 @@ static TBehavior getBehavior(const std::string& str)
TDirectiveHandler::TDirectiveHandler(TExtensionBehavior& extBehavior,
TDiagnostics& diagnostics,
int& shaderVersion)
int& shaderVersion,
bool debugShaderPrecisionSupported)
: mExtensionBehavior(extBehavior),
mDiagnostics(diagnostics),
mShaderVersion(shaderVersion)
mShaderVersion(shaderVersion),
mDebugShaderPrecisionSupported(debugShaderPrecisionSupported)
{
}
......@@ -65,6 +67,7 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
{
const char kOptimize[] = "optimize";
const char kDebug[] = "debug";
const char kDebugShaderPrecision[] = "webgl_debug_shader_precision";
const char kOn[] = "on";
const char kOff[] = "off";
......@@ -81,6 +84,12 @@ void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
else if (value == kOff) mPragma.debug = false;
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
{
mDiagnostics.report(pp::Diagnostics::PP_UNRECOGNIZED_PRAGMA, loc, name);
......
......@@ -18,7 +18,8 @@ class TDirectiveHandler : public pp::DirectiveHandler
public:
TDirectiveHandler(TExtensionBehavior& extBehavior,
TDiagnostics& diagnostics,
int& shaderVersion);
int& shaderVersion,
bool debugShaderPrecisionSupported);
virtual ~TDirectiveHandler();
const TPragma& pragma() const { return mPragma; }
......@@ -44,6 +45,7 @@ class TDirectiveHandler : public pp::DirectiveHandler
TExtensionBehavior& mExtensionBehavior;
TDiagnostics& mDiagnostics;
int& mShaderVersion;
bool mDebugShaderPrecisionSupported;
};
#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.
//
#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
TString hashedName = stream.str();
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
EOpNull, // if in a node, should only mean a node is still being built
EOpSequence, // denotes a list of statements, or parameters, etc.
EOpFunctionCall,
EOpInternalFunctionCall, // Call to an internal helper function
EOpFunction, // For function definition
EOpParameters, // an aggregate listing the parameters to a function
......@@ -741,12 +742,38 @@ class TIntermTraverser
const bool postVisit;
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:
int mDepth;
int mMaxDepth;
// All the nodes from root to the current node's parent during traversing.
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,
ShHashFunction64 hashFunction,
NameMap& nameMap,
TSymbolTable& symbolTable,
int shaderVersion)
: TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable, shaderVersion)
int shaderVersion,
bool forceHighp)
: TOutputGLSLBase(objSink, clampingStrategy, hashFunction, nameMap, symbolTable, shaderVersion),
mForceHighp(forceHighp)
{
}
......@@ -22,6 +24,9 @@ bool TOutputESSL::writeVariablePrecision(TPrecision precision)
return false;
TInfoSinkBase& out = objSink();
out << getPrecisionString(precision);
if (mForceHighp)
out << getPrecisionString(EbpHigh);
else
out << getPrecisionString(precision);
return true;
}
......@@ -17,10 +17,13 @@ public:
ShHashFunction64 hashFunction,
NameMap& nameMap,
TSymbolTable& symbolTable,
int shaderVersion);
int shaderVersion,
bool forceHighp);
protected:
virtual bool writeVariablePrecision(TPrecision precision);
private:
bool mForceHighp;
};
#endif // COMPILER_TRANSLATOR_OUTPUTESSL_H_
......@@ -622,6 +622,15 @@ bool TOutputGLSLBase::visitAggregate(Visit visit, TIntermAggregate *node)
else
out << ")";
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:
// Function parameters.
ASSERT(visit == PreVisit);
......
......@@ -25,7 +25,7 @@ struct TMatrixFields {
// they can be passed to the parser without needing a global.
//
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),
symbolTable(symt),
shaderType(type),
......@@ -42,7 +42,7 @@ struct TParseContext {
defaultBlockStorage(EbsShared),
diagnostics(is),
shaderVersion(100),
directiveHandler(ext, diagnostics, shaderVersion),
directiveHandler(ext, diagnostics, shaderVersion, debugShaderPrecisionSupported),
preprocessor(&diagnostics, &directiveHandler),
scanner(NULL) { }
TIntermediate& intermediate; // to hold and build a parse tree
......
......@@ -18,11 +18,14 @@ struct TPragma
// By default optimization is turned on and debug is turned off.
TPragma() : optimize(true), debug(false) { }
TPragma(bool o, bool d) : optimize(o), debug(d) { }
// Precision emulation is turned on by default, but has no effect unless
// 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 debug;
bool debugShaderPrecision;
STDGL stdgl;
};
......
......@@ -153,6 +153,7 @@ void ShInitBuiltInResources(ShBuiltInResources* resources)
resources->EXT_draw_buffers = 0;
resources->EXT_frag_depth = 0;
resources->EXT_shader_texture_lod = 0;
resources->WEBGL_debug_shader_precision = 0;
resources->NV_draw_buffers = 0;
......
......@@ -6,6 +6,7 @@
#include "compiler/translator/TranslatorESSL.h"
#include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/OutputESSL.h"
#include "angle_gl.h"
......@@ -21,6 +22,16 @@ void TranslatorESSL::translate(TIntermNode* root) {
// Write built-in extension behaviors.
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.
getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
sink, getShaderType() == GL_FRAGMENT_SHADER);
......@@ -29,7 +40,7 @@ void TranslatorESSL::translate(TIntermNode* root) {
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
// Write translated shader.
TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable(), getShaderVersion());
TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(), getSymbolTable(), getShaderVersion(), precisionEmulation);
root->traverse(&outputESSL);
}
......
......@@ -6,9 +6,11 @@
#include "compiler/translator/TranslatorGLSL.h"
#include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/OutputGLSL.h"
#include "compiler/translator/VersionGLSL.h"
TranslatorGLSL::TranslatorGLSL(sh::GLenum type, ShShaderSpec spec)
: TCompiler(type, spec, SH_GLSL_OUTPUT) {
}
......@@ -24,6 +26,16 @@ void TranslatorGLSL::translate(TIntermNode* root) {
// Write extension behaviour as needed
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.
getBuiltInFunctionEmulator().OutputEmulatedFunctionDefinition(
sink, false);
......
......@@ -325,6 +325,10 @@ class TType
{
return primarySize > 1 && secondarySize > 1;
}
bool isNonSquareMatrix() const
{
return isMatrix() && primarySize != secondarySize;
}
bool isArray() const
{
return array ? true : false;
......
......@@ -50,32 +50,8 @@ bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node)
}
if (replacement)
{
replacements.push_back(
NodeUpdateEntry(getParentNode(), node, replacement));
mReplacements.push_back(
NodeUpdateEntry(getParentNode(), node, replacement, false));
}
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
virtual bool visitBinary(Visit visit, TIntermBinary *);
void updateTree();
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);
};
......
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