Commit bccc65d3 by Kenneth Russell Committed by Commit Bot

Flatten "#pragma STDGL invariant(all)" into varying variables.

This is implemented as a compiler option which is enabled by default when outputting to desktop GLSL version 130 and greater, which does not support this #pragma in fragment shaders. As a workaround, and for better compatibility on desktop OpenGL drivers, this pragma is also flattened into the outputs of vertex shaders, and the inputs of ESSL 1.00 fragment shaders. TEST=conformance/glsl/misc/shaders-with-invariance.html with --enable-unsafe-es3-apis BUG=629622, angleproject:1293 Change-Id: Ib040230915e639971505ed496d26e804c9d64e68 Reviewed-on: https://chromium-review.googlesource.com/361792Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org> Commit-Queue: Kenneth Russell <kbr@chromium.org>
parent d2f59bb6
......@@ -217,6 +217,11 @@ typedef enum {
// constant pow expressions incorrectly. Only applies to the HLSL back-end. It works
// by expanding the integer pow expressions into a series of multiplies.
SH_EXPAND_SELECT_HLSL_INTEGER_POW_EXPRESSIONS = 0x800000,
// Flatten "#pragma STDGL invariant(all)" into the declarations of
// varying variables and built-in GLSL variables. This compiler
// option is enabled automatically when needed.
SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL = 0x1000000,
} ShCompileOptions;
// Defines alternate strategies for implementing array index clamping.
......
......@@ -140,7 +140,8 @@ TShHandleBase::~TShHandleBase()
}
TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
: shaderType(type),
: variablesCollected(false),
shaderType(type),
shaderSpec(spec),
outputType(output),
maxUniformVectors(0),
......@@ -408,12 +409,20 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
return NULL;
}
bool TCompiler::compile(const char* const shaderStrings[],
size_t numStrings, int compileOptions)
bool TCompiler::compile(const char *const shaderStrings[], size_t numStrings, int compileOptionsIn)
{
if (numStrings == 0)
return true;
int compileOptions = compileOptionsIn;
// Apply key workarounds.
if (shouldFlattenPragmaStdglInvariantAll())
{
// This should be harmless to do in all cases, but for the moment, do it only conditionally.
compileOptions |= SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL;
}
TScopedPoolAllocator scopedAlloc(&allocator);
TIntermNode *root = compileTreeImpl(shaderStrings, numStrings, compileOptions);
......@@ -577,6 +586,7 @@ void TCompiler::clearResults()
expandedUniforms.clear();
varyings.clear();
interfaceBlocks.clear();
variablesCollected = false;
builtInFunctionEmulator.Cleanup();
......@@ -833,12 +843,16 @@ bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
void TCompiler::collectVariables(TIntermNode* root)
{
sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings,
&interfaceBlocks, hashFunction, symbolTable, extensionBehavior);
root->traverse(&collect);
if (!variablesCollected)
{
sh::CollectVariables collect(&attributes, &outputVariables, &uniforms, &varyings,
&interfaceBlocks, hashFunction, symbolTable, extensionBehavior);
root->traverse(&collect);
// This is for enforcePackingRestriction().
sh::ExpandUniforms(uniforms, &expandedUniforms);
// This is for enforcePackingRestriction().
sh::ExpandUniforms(uniforms, &expandedUniforms);
variablesCollected = true;
}
}
bool TCompiler::enforcePackingRestrictions()
......@@ -907,9 +921,26 @@ const BuiltInFunctionEmulator& TCompiler::getBuiltInFunctionEmulator() const
return builtInFunctionEmulator;
}
void TCompiler::writePragma()
void TCompiler::writePragma(int compileOptions)
{
TInfoSinkBase &sink = infoSink.obj;
if (mPragma.stdgl.invariantAll)
sink << "#pragma STDGL invariant(all)\n";
if (!(compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL))
{
TInfoSinkBase &sink = infoSink.obj;
if (mPragma.stdgl.invariantAll)
sink << "#pragma STDGL invariant(all)\n";
}
}
bool TCompiler::isVaryingDefined(const char *varyingName)
{
ASSERT(variablesCollected);
for (size_t ii = 0; ii < varyings.size(); ++ii)
{
if (varyings[ii].name == varyingName)
{
return true;
}
}
return false;
}
......@@ -154,24 +154,29 @@ class TCompiler : public TShHandleBase
const TExtensionBehavior& getExtensionBehavior() const;
const char *getSourcePath() const;
const TPragma& getPragma() const { return mPragma; }
void writePragma();
void writePragma(int compileOptions);
unsigned int *getTemporaryIndex() { return &mTemporaryIndex; }
// Relies on collectVariables having been called.
bool isVaryingDefined(const char *varyingName);
const ArrayBoundsClamper& getArrayBoundsClamper() const;
ShArrayIndexClampingStrategy getArrayIndexClampingStrategy() const;
const BuiltInFunctionEmulator& getBuiltInFunctionEmulator() const;
virtual bool shouldCollectVariables(int compileOptions)
{
return (compileOptions & SH_VARIABLES) != 0;
}
virtual bool shouldFlattenPragmaStdglInvariantAll() = 0;
std::vector<sh::Attribute> attributes;
std::vector<sh::OutputVariable> outputVariables;
std::vector<sh::Uniform> uniforms;
std::vector<sh::ShaderVariable> expandedUniforms;
std::vector<sh::Varying> varyings;
std::vector<sh::InterfaceBlock> interfaceBlocks;
virtual bool shouldCollectVariables(int compileOptions)
{
return (compileOptions & SH_VARIABLES) != 0;
}
bool variablesCollected;
private:
// Creates the function call DAG for further analysis, returning false if there is a recursion
......
......@@ -1517,8 +1517,35 @@ TIntermAggregate *TParseContext::parseSingleDeclaration(TPublicType &publicType,
const TSourceLoc &identifierOrTypeLocation,
const TString &identifier)
{
TIntermSymbol *symbol =
intermediate.addSymbol(0, identifier, TType(publicType), identifierOrTypeLocation);
TType type(publicType);
if ((mCompileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) &&
mDirectiveHandler.pragma().stdgl.invariantAll)
{
TQualifier qualifier = type.getQualifier();
// The directive handler has already taken care of rejecting invalid uses of this pragma
// (for example, in ESSL 3.00 fragment shaders), so at this point, flatten it into all
// affected variable declarations:
//
// 1. Built-in special variables which are inputs to the fragment shader. (These are handled
// elsewhere, in TranslatorGLSL.)
//
// 2. Outputs from vertex shaders in ESSL 1.00 and 3.00 (EvqVaryingOut and EvqVertexOut). It
// is actually less likely that there will be bugs in the handling of ESSL 3.00 shaders, but
// the way this is currently implemented we have to enable this compiler option before
// parsing the shader and determining the shading language version it uses. If this were
// implemented as a post-pass, the workaround could be more targeted.
//
// 3. Inputs in ESSL 1.00 fragment shaders (EvqVaryingIn). This is somewhat in violation of
// the specification, but there are desktop OpenGL drivers that expect that this is the
// behavior of the #pragma when specified in ESSL 1.00 fragment shaders.
if (qualifier == EvqVaryingOut || qualifier == EvqVertexOut || qualifier == EvqVaryingIn)
{
type.setInvariant(true);
}
}
TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, type, identifierOrTypeLocation);
bool emptyDeclaration = (identifier == "");
......@@ -1541,7 +1568,7 @@ TIntermAggregate *TParseContext::parseSingleDeclaration(TPublicType &publicType,
checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &publicType);
TVariable *variable = nullptr;
declareVariable(identifierOrTypeLocation, identifier, TType(publicType), &variable);
declareVariable(identifierOrTypeLocation, identifier, type, &variable);
if (variable && symbol)
symbol->setId(variable->getUniqueId());
......
......@@ -42,6 +42,7 @@ class TParseContext : angle::NonCopyable
mDeferredSingleDeclarationErrorCheck(false),
mShaderType(type),
mShaderSpec(spec),
mCompileOptions(options),
mShaderVersion(100),
mTreeRoot(nullptr),
mLoopNestingLevel(0),
......@@ -406,6 +407,7 @@ class TParseContext : angle::NonCopyable
sh::GLenum mShaderType; // vertex or fragment language (future: pack or unpack)
ShShaderSpec mShaderSpec; // The language specification compiler conforms to - GLES2 or WebGL.
int mCompileOptions; // Options passed to TCompiler
int mShaderVersion;
TIntermNode *mTreeRoot; // root of parse tree being created
int mLoopNestingLevel; // 0 if outside all loops
......
......@@ -25,7 +25,8 @@ void TranslatorESSL::initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, i
}
}
void TranslatorESSL::translate(TIntermNode *root, int) {
void TranslatorESSL::translate(TIntermNode *root, int compileOptions)
{
TInfoSinkBase& sink = getInfoSink().obj;
int shaderVer = getShaderVersion();
......@@ -39,7 +40,7 @@ void TranslatorESSL::translate(TIntermNode *root, int) {
// Write pragmas after extensions because some drivers consider pragmas
// like non-preprocessor tokens.
writePragma();
writePragma(compileOptions);
bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
......@@ -90,6 +91,12 @@ void TranslatorESSL::translate(TIntermNode *root, int) {
root->traverse(&outputESSL);
}
bool TranslatorESSL::shouldFlattenPragmaStdglInvariantAll()
{
// Not necessary when translating to ESSL.
return false;
}
void TranslatorESSL::writeExtensionBehavior() {
TInfoSinkBase& sink = getInfoSink().obj;
const TExtensionBehavior& extBehavior = getExtensionBehavior();
......
......@@ -18,6 +18,7 @@ class TranslatorESSL : public TCompiler
void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) override;
void translate(TIntermNode *root, int compileOptions) override;
bool shouldFlattenPragmaStdglInvariantAll() override;
private:
void writeExtensionBehavior();
......
......@@ -42,7 +42,38 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions)
// Write pragmas after extensions because some drivers consider pragmas
// like non-preprocessor tokens.
writePragma();
writePragma(compileOptions);
// If flattening the global invariant pragma, write invariant declarations for built-in
// variables. It should be harmless to do this twice in the case that the shader also explicitly
// did this. However, it's important to emit invariant qualifiers only for those built-in
// variables that are actually used, to avoid affecting the behavior of the shader.
if ((compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) && getPragma().stdgl.invariantAll)
{
collectVariables(root);
switch (getShaderType())
{
case GL_VERTEX_SHADER:
sink << "invariant gl_Position;\n";
// gl_PointSize should be declared invariant in both ESSL 1.00 and 3.00 fragment
// shaders if it's statically referenced.
conditionallyOutputInvariantDeclaration("gl_PointSize");
break;
case GL_FRAGMENT_SHADER:
// The preprocessor will reject this pragma if it's used in ESSL 3.00 fragment
// shaders, so we can use simple logic to determine whether to declare these
// variables invariant.
conditionallyOutputInvariantDeclaration("gl_FragCoord");
conditionallyOutputInvariantDeclaration("gl_PointCoord");
break;
default:
// Currently not reached, but leave this in for future expansion.
ASSERT(false);
break;
}
}
bool precisionEmulation = getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision;
......@@ -152,6 +183,13 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions)
root->traverse(&outputGLSL);
}
bool TranslatorGLSL::shouldFlattenPragmaStdglInvariantAll()
{
// Required when outputting to any GLSL version greater than 1.20, but since ANGLE doesn't
// translate to that version, return true for the next higher version.
return IsGLSL130OrNewer(getOutputType());
}
void TranslatorGLSL::writeVersion(TIntermNode *root)
{
TVersionGLSL versionGLSL(getShaderType(), getPragma(), getOutputType());
......@@ -230,3 +268,12 @@ void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root)
sink << "#extension " << ext << " : require\n";
}
}
void TranslatorGLSL::conditionallyOutputInvariantDeclaration(const char *builtinVaryingName)
{
if (isVaryingDefined(builtinVaryingName))
{
TInfoSinkBase &sink = getInfoSink().obj;
sink << "invariant " << builtinVaryingName << ";\n";
}
}
......@@ -18,10 +18,12 @@ class TranslatorGLSL : public TCompiler
void initBuiltInFunctionEmulator(BuiltInFunctionEmulator *emu, int compileOptions) override;
void translate(TIntermNode *root, int compileOptions) override;
bool shouldFlattenPragmaStdglInvariantAll() override;
private:
void writeVersion(TIntermNode *root);
void writeExtensionBehavior(TIntermNode *root);
void conditionallyOutputInvariantDeclaration(const char *builtinVaryingName);
};
#endif // COMPILER_TRANSLATOR_TRANSLATORGLSL_H_
......@@ -99,6 +99,12 @@ void TranslatorHLSL::translate(TIntermNode *root, int compileOptions)
mUniformRegisterMap = outputHLSL.getUniformRegisterMap();
}
bool TranslatorHLSL::shouldFlattenPragmaStdglInvariantAll()
{
// Not necessary when translating to HLSL.
return false;
}
bool TranslatorHLSL::hasInterfaceBlock(const std::string &interfaceBlockName) const
{
return (mInterfaceBlockRegisterMap.count(interfaceBlockName) > 0);
......@@ -113,4 +119,4 @@ unsigned int TranslatorHLSL::getInterfaceBlockRegister(const std::string &interf
const std::map<std::string, unsigned int> *TranslatorHLSL::getUniformRegisterMap() const
{
return &mUniformRegisterMap;
}
\ No newline at end of file
}
......@@ -22,6 +22,7 @@ class TranslatorHLSL : public TCompiler
protected:
void translate(TIntermNode *root, int compileOptions) override;
bool shouldFlattenPragmaStdglInvariantAll() override;
// collectVariables needs to be run always so registers can be assigned.
bool shouldCollectVariables(int compileOptions) override { return true; }
......
......@@ -326,6 +326,8 @@ class TType
return invariant;
}
void setInvariant(bool i) { invariant = i; }
TLayoutQualifier getLayoutQualifier() const
{
return layoutQualifier;
......
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