Commit 013613ea by Shahbaz Youssefi Committed by Angle LUCI CQ

Translator: Allow invariant on I/O block members

The GLSL ES 3.2 spec says: > Only variables output from a shader can be candidates for invariance. And: > All uses of invariant must be at global scope or on block members, and > before any use of the variables being declared as invariant. As a result, it's possible to specify `invariant` on a member of an output I/O block. Bug: angleproject:4889 Change-Id: I8822ed214813db8424d6a67dfef7f069a448b19d Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2946115Reviewed-by: 's avatarIan Elliott <ianelliott@google.com> Reviewed-by: 's avatarJonah Ryan-Davis <jonahr@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 4bafc503
...@@ -1567,16 +1567,6 @@ const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const ...@@ -1567,16 +1567,6 @@ const BuiltInFunctionEmulator &TCompiler::getBuiltInFunctionEmulator() const
return mBuiltInFunctionEmulator; return mBuiltInFunctionEmulator;
} }
void TCompiler::writePragma(ShCompileOptions compileOptions)
{
if ((compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) == 0)
{
TInfoSinkBase &sink = mInfoSink.obj;
if (mPragma.stdgl.invariantAll)
sink << "#pragma STDGL invariant(all)\n";
}
}
bool TCompiler::isVaryingDefined(const char *varyingName) bool TCompiler::isVaryingDefined(const char *varyingName)
{ {
ASSERT(mVariablesCollected); ASSERT(mVariablesCollected);
......
...@@ -141,6 +141,8 @@ class TCompiler : public TShHandleBase ...@@ -141,6 +141,8 @@ class TCompiler : public TShHandleBase
// Get the resources set by InitBuiltInSymbolTable // Get the resources set by InitBuiltInSymbolTable
const ShBuiltInResources &getResources() const; const ShBuiltInResources &getResources() const;
const TPragma &getPragma() const { return mPragma; }
int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; } int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
int getGeometryShaderInvocations() const { return mGeometryShaderInvocations; } int getGeometryShaderInvocations() const { return mGeometryShaderInvocations; }
TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
...@@ -190,8 +192,6 @@ class TCompiler : public TShHandleBase ...@@ -190,8 +192,6 @@ class TCompiler : public TShHandleBase
// Get built-in extensions with default behavior. // Get built-in extensions with default behavior.
const TExtensionBehavior &getExtensionBehavior() const; const TExtensionBehavior &getExtensionBehavior() const;
const char *getSourcePath() const; const char *getSourcePath() const;
const TPragma &getPragma() const { return mPragma; }
void writePragma(ShCompileOptions compileOptions);
// Relies on collectVariables having been called. // Relies on collectVariables having been called.
bool isVaryingDefined(const char *varyingName); bool isVaryingDefined(const char *varyingName);
......
...@@ -1337,23 +1337,39 @@ void TOutputGLSLBase::declareInterfaceBlock(const TType &type) ...@@ -1337,23 +1337,39 @@ void TOutputGLSLBase::declareInterfaceBlock(const TType &type)
{ {
writeFieldLayoutQualifier(field); writeFieldLayoutQualifier(field);
} }
out << getMemoryQualifiers(*field->type());
if (writeVariablePrecision(field->type()->getPrecision())) const TType &fieldType = *field->type();
out << getMemoryQualifiers(fieldType);
if (writeVariablePrecision(fieldType.getPrecision()))
out << " "; out << " ";
if (fieldType.isInvariant())
{
writeInvariantQualifier(fieldType);
}
const char *qualifier = getVariableInterpolation(field->type()->getQualifier()); const char *qualifier = getVariableInterpolation(fieldType.getQualifier());
if (qualifier != nullptr) if (qualifier != nullptr)
out << qualifier; out << qualifier;
out << getTypeName(*field->type()) << " " << hashFieldName(field); out << getTypeName(fieldType) << " " << hashFieldName(field);
if (field->type()->isArray()) if (fieldType.isArray())
out << ArrayString(*field->type()); out << ArrayString(fieldType);
out << ";\n"; out << ";\n";
} }
out << "}"; out << "}";
} }
void WritePragma(TInfoSinkBase &out, ShCompileOptions compileOptions, const TPragma &pragma)
{
if ((compileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) == 0)
{
if (pragma.stdgl.invariantAll)
out << "#pragma STDGL invariant(all)\n";
}
}
void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out, void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutPrimitiveType inputPrimitive, sh::TLayoutPrimitiveType inputPrimitive,
int invocations, int invocations,
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "compiler/translator/ExtensionBehavior.h" #include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/HashNames.h" #include "compiler/translator/HashNames.h"
#include "compiler/translator/InfoSink.h" #include "compiler/translator/InfoSink.h"
#include "compiler/translator/Pragma.h"
#include "compiler/translator/tree_util/IntermTraverse.h" #include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh namespace sh
...@@ -117,6 +118,8 @@ class TOutputGLSLBase : public TIntermTraverser ...@@ -117,6 +118,8 @@ class TOutputGLSLBase : public TIntermTraverser
ShCompileOptions mCompileOptions; ShCompileOptions mCompileOptions;
}; };
void WritePragma(TInfoSinkBase &out, ShCompileOptions compileOptions, const TPragma &pragma);
void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out, void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutPrimitiveType inputPrimitive, sh::TLayoutPrimitiveType inputPrimitive,
int invocations, int invocations,
......
...@@ -4270,7 +4270,7 @@ TIntermDeclaration *TParseContext::addInterfaceBlock( ...@@ -4270,7 +4270,7 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
if (typeQualifier.invariant) if (typeQualifier.invariant)
{ {
error(typeQualifier.line, "invalid qualifier on interface block member", "invariant"); error(typeQualifier.line, "invalid qualifier on interface block", "invariant");
} }
if (typeQualifier.qualifier != EvqBuffer) if (typeQualifier.qualifier != EvqBuffer)
...@@ -4448,7 +4448,9 @@ TIntermDeclaration *TParseContext::addInterfaceBlock( ...@@ -4448,7 +4448,9 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
break; break;
} }
if (fieldType->isInvariant()) // On interface block members, invariant is only applicable to output I/O blocks.
const bool isOutputShaderIoBlock = isShaderIoBlock && IsShaderOut(typeQualifier.qualifier);
if (fieldType->isInvariant() && !isOutputShaderIoBlock)
{ {
error(field->line(), "invalid qualifier on interface block member", "invariant"); error(field->line(), "invalid qualifier on interface block member", "invariant");
} }
......
...@@ -44,7 +44,7 @@ bool TranslatorESSL::translate(TIntermBlock *root, ...@@ -44,7 +44,7 @@ bool TranslatorESSL::translate(TIntermBlock *root,
// Write pragmas after extensions because some drivers consider pragmas // Write pragmas after extensions because some drivers consider pragmas
// like non-preprocessor tokens. // like non-preprocessor tokens.
writePragma(compileOptions); WritePragma(sink, compileOptions, getPragma());
bool precisionEmulation = false; bool precisionEmulation = false;
if (!emulatePrecisionIfNeeded(root, sink, &precisionEmulation, SH_ESSL_OUTPUT)) if (!emulatePrecisionIfNeeded(root, sink, &precisionEmulation, SH_ESSL_OUTPUT))
......
...@@ -58,7 +58,7 @@ bool TranslatorGLSL::translate(TIntermBlock *root, ...@@ -58,7 +58,7 @@ bool TranslatorGLSL::translate(TIntermBlock *root,
// Write pragmas after extensions because some drivers consider pragmas // Write pragmas after extensions because some drivers consider pragmas
// like non-preprocessor tokens. // like non-preprocessor tokens.
writePragma(compileOptions); WritePragma(sink, compileOptions, getPragma());
// If flattening the global invariant pragma, write invariant declarations for built-in // 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 // variables. It should be harmless to do this twice in the case that the shader also explicitly
......
...@@ -1351,6 +1351,38 @@ TEST_P(GLSLTest, InvariantAllBoth) ...@@ -1351,6 +1351,38 @@ TEST_P(GLSLTest, InvariantAllBoth)
EXPECT_EQ(0u, program); EXPECT_EQ(0u, program);
} }
// Verify that using a struct as both invariant and non-invariant output works.
TEST_P(GLSLTest_ES31, StructBothInvariantAndNot)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks"));
constexpr char kVS[] = R"(#version 310 es
#extension GL_EXT_shader_io_blocks : require
struct S
{
vec4 s;
};
out Output
{
vec4 x;
invariant S s;
};
out S s2;
void main(){
x = vec4(0);
s.s = vec4(1);
s2.s = vec4(2);
})";
GLuint shader = CompileShader(GL_VERTEX_SHADER, kVS);
EXPECT_NE(0u, shader);
glDeleteShader(shader);
}
// Verify that functions without return statements still compile // Verify that functions without return statements still compile
TEST_P(GLSLTest, MissingReturnFloat) TEST_P(GLSLTest, MissingReturnFloat)
{ {
......
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