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
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)
{
ASSERT(mVariablesCollected);
......
......@@ -141,6 +141,8 @@ class TCompiler : public TShHandleBase
// Get the resources set by InitBuiltInSymbolTable
const ShBuiltInResources &getResources() const;
const TPragma &getPragma() const { return mPragma; }
int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
int getGeometryShaderInvocations() const { return mGeometryShaderInvocations; }
TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
......@@ -190,8 +192,6 @@ class TCompiler : public TShHandleBase
// Get built-in extensions with default behavior.
const TExtensionBehavior &getExtensionBehavior() const;
const char *getSourcePath() const;
const TPragma &getPragma() const { return mPragma; }
void writePragma(ShCompileOptions compileOptions);
// Relies on collectVariables having been called.
bool isVaryingDefined(const char *varyingName);
......
......@@ -1337,23 +1337,39 @@ void TOutputGLSLBase::declareInterfaceBlock(const TType &type)
{
writeFieldLayoutQualifier(field);
}
out << getMemoryQualifiers(*field->type());
if (writeVariablePrecision(field->type()->getPrecision()))
const TType &fieldType = *field->type();
out << getMemoryQualifiers(fieldType);
if (writeVariablePrecision(fieldType.getPrecision()))
out << " ";
if (fieldType.isInvariant())
{
writeInvariantQualifier(fieldType);
}
const char *qualifier = getVariableInterpolation(field->type()->getQualifier());
const char *qualifier = getVariableInterpolation(fieldType.getQualifier());
if (qualifier != nullptr)
out << qualifier;
out << getTypeName(*field->type()) << " " << hashFieldName(field);
out << getTypeName(fieldType) << " " << hashFieldName(field);
if (field->type()->isArray())
out << ArrayString(*field->type());
if (fieldType.isArray())
out << ArrayString(fieldType);
out << ";\n";
}
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,
sh::TLayoutPrimitiveType inputPrimitive,
int invocations,
......
......@@ -12,6 +12,7 @@
#include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/HashNames.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/Pragma.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh
......@@ -117,6 +118,8 @@ class TOutputGLSLBase : public TIntermTraverser
ShCompileOptions mCompileOptions;
};
void WritePragma(TInfoSinkBase &out, ShCompileOptions compileOptions, const TPragma &pragma);
void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutPrimitiveType inputPrimitive,
int invocations,
......
......@@ -4270,7 +4270,7 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
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)
......@@ -4448,7 +4448,9 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
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");
}
......
......@@ -44,7 +44,7 @@ bool TranslatorESSL::translate(TIntermBlock *root,
// Write pragmas after extensions because some drivers consider pragmas
// like non-preprocessor tokens.
writePragma(compileOptions);
WritePragma(sink, compileOptions, getPragma());
bool precisionEmulation = false;
if (!emulatePrecisionIfNeeded(root, sink, &precisionEmulation, SH_ESSL_OUTPUT))
......
......@@ -58,7 +58,7 @@ bool TranslatorGLSL::translate(TIntermBlock *root,
// Write pragmas after extensions because some drivers consider pragmas
// like non-preprocessor tokens.
writePragma(compileOptions);
WritePragma(sink, compileOptions, getPragma());
// 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
......
......@@ -1351,6 +1351,38 @@ TEST_P(GLSLTest, InvariantAllBoth)
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
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