Commit 230e14dd by Shahbaz Youssefi Committed by Commit Bot

Add support for layout(early_fragment_tests) in;

Bug: angleproject:4314 Change-Id: I37b228f37201cc4188834e68459cd7294727c3ac Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2014240Reviewed-by: 's avatarCody Northrop <cnorthrop@google.com> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 01e28144
......@@ -1122,16 +1122,16 @@ struct TLayoutQualifier
bool isEmpty() const
{
return location == -1 && binding == -1 && offset == -1 && numViews == -1 && yuv == false &&
matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified &&
!localSize.isAnyValueSet() && imageInternalFormat == EiifUnspecified &&
primitiveType == EptUndefined && invocations == 0 && maxVertices == -1 &&
index == -1;
earlyFragmentTests == false && matrixPacking == EmpUnspecified &&
blockStorage == EbsUnspecified && !localSize.isAnyValueSet() &&
imageInternalFormat == EiifUnspecified && primitiveType == EptUndefined &&
invocations == 0 && maxVertices == -1 && index == -1;
}
bool isCombinationValid() const
{
bool workSizeSpecified = localSize.isAnyValueSet();
bool numViewsSet = (numViews != -1);
bool workGroupSizeSpecified = localSize.isAnyValueSet();
bool numViewsSet = (numViews != -1);
bool geometryShaderSpecified =
(primitiveType != EptUndefined) || (invocations != 0) || (maxVertices != -1);
bool otherLayoutQualifiersSpecified =
......@@ -1139,9 +1139,11 @@ struct TLayoutQualifier
blockStorage != EbsUnspecified || imageInternalFormat != EiifUnspecified);
// we can have either the work group size specified, or number of views,
// or yuv layout qualifier, or the other layout qualifiers.
return (workSizeSpecified ? 1 : 0) + (numViewsSet ? 1 : 0) + (yuv ? 1 : 0) +
(otherLayoutQualifiersSpecified ? 1 : 0) + (geometryShaderSpecified ? 1 : 0) <=
// or yuv layout qualifier, or early_fragment_tests layout qualifier, or the other layout
// qualifiers.
return (workGroupSizeSpecified ? 1 : 0) + (numViewsSet ? 1 : 0) + (yuv ? 1 : 0) +
(earlyFragmentTests ? 1 : 0) + (otherLayoutQualifiersSpecified ? 1 : 0) +
(geometryShaderSpecified ? 1 : 0) <=
1;
}
......@@ -1170,6 +1172,9 @@ struct TLayoutQualifier
// EXT_YUV_target yuv layout qualifier.
bool yuv;
// early_fragment_tests qualifier.
bool earlyFragmentTests;
// OES_geometry_shader layout qualifiers.
TLayoutPrimitiveType primitiveType;
int invocations;
......@@ -1190,6 +1195,7 @@ struct TLayoutQualifier
imageInternalFormat(EiifUnspecified),
numViews(-1),
yuv(false),
earlyFragmentTests(false),
primitiveType(EptUndefined),
invocations(0),
maxVertices(-1),
......
......@@ -482,6 +482,8 @@ void TCompiler::setASTMetadata(const TParseContext &parseContext)
mPragma = parseContext.pragma();
mSymbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
mEarlyFragmentTestsSpecified = parseContext.isEarlyFragmentTestsSpecified();
mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize();
......@@ -1463,6 +1465,14 @@ bool TCompiler::isVaryingDefined(const char *varyingName)
return false;
}
void EmitEarlyFragmentTestsGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
{
if (compiler.isEarlyFragmentTestsSpecified())
{
sink << "layout (early_fragment_tests) in;\n";
}
}
void EmitWorkGroupSizeGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
{
if (compiler.isComputeShaderLocalSizeDeclared())
......
......@@ -98,6 +98,8 @@ class TCompiler : public TShHandleBase
int getShaderVersion() const { return mShaderVersion; }
TInfoSink &getInfoSink() { return mInfoSink; }
bool isEarlyFragmentTestsSpecified() const { return mEarlyFragmentTestsSpecified; }
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mComputeShaderLocalSize; }
int getNumViews() const { return mNumViews; }
......@@ -268,6 +270,9 @@ class TCompiler : public TShHandleBase
TDiagnostics mDiagnostics;
const char *mSourcePath; // Path of source file or NULL
// fragment shader early fragment tests
bool mEarlyFragmentTestsSpecified;
// compute shader local group size
bool mComputeShaderLocalSizeDeclared;
sh::WorkGroupSize mComputeShaderLocalSize;
......@@ -304,6 +309,7 @@ class TCompiler : public TShHandleBase
TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
void DeleteCompiler(TCompiler *);
void EmitEarlyFragmentTestsGLSL(const TCompiler &, TInfoSinkBase &sink);
void EmitWorkGroupSizeGLSL(const TCompiler &, TInfoSinkBase &sink);
void EmitMultiviewGLSL(const TCompiler &,
const ShCompileOptions &,
......
......@@ -185,6 +185,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mFunctionReturnsValue(false),
mChecksPrecisionErrors(checksPrecErrors),
mFragmentPrecisionHighOnESSL1(false),
mEarlyFragmentTestsSpecified(false),
mDefaultUniformMatrixPacking(EmpColumnMajor),
mDefaultUniformBlockStorage(sh::IsWebGLBasedSpec(spec) ? EbsStd140 : EbsShared),
mDefaultBufferMatrixPacking(EmpColumnMajor),
......@@ -1360,6 +1361,11 @@ void TParseContext::declarationQualifierErrorCheck(const sh::TQualifier qualifie
checkYuvIsNotSpecified(location, layoutQualifier.yuv);
}
if (qualifier != EvqFragmentIn)
{
checkEarlyFragmentTestsIsNotSpecified(location, layoutQualifier.earlyFragmentTests);
}
// If multiview extension is enabled, "in" qualifier is allowed in the vertex shader in previous
// parsing steps. So it needs to be checked here.
if (anyMultiviewExtensionAvailable() && mShaderVersion < 300 && qualifier == EvqVertexIn)
......@@ -1736,6 +1742,17 @@ void TParseContext::checkYuvIsNotSpecified(const TSourceLoc &location, bool yuv)
}
}
void TParseContext::checkEarlyFragmentTestsIsNotSpecified(const TSourceLoc &location,
bool earlyFragmentTests)
{
if (earlyFragmentTests != false)
{
error(location,
"invalid layout qualifier: only valid when used with 'in' in a fragment shader",
"early_fragment_tests");
}
}
void TParseContext::functionCallRValueLValueErrorCheck(const TFunction *fnCandidate,
TIntermAggregate *fnCall)
{
......@@ -2215,6 +2232,9 @@ TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &ty
checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), returnType.layoutQualifier);
checkEarlyFragmentTestsIsNotSpecified(typeSpecifier.getLine(),
returnType.layoutQualifier.earlyFragmentTests);
if (mShaderVersion < 300)
{
if (typeSpecifier.isArray())
......@@ -3067,6 +3087,12 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
checkStd430IsForShaderStorageBlock(typeQualifier.line, layoutQualifier.blockStorage,
typeQualifier.qualifier);
if (typeQualifier.qualifier != EvqFragmentIn)
{
checkEarlyFragmentTestsIsNotSpecified(typeQualifier.line,
layoutQualifier.earlyFragmentTests);
}
if (typeQualifier.qualifier == EvqComputeIn)
{
if (mComputeShaderLocalSizeDeclared &&
......@@ -3170,6 +3196,27 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
mNumViews = layoutQualifier.numViews;
}
else if (typeQualifier.qualifier == EvqFragmentIn)
{
if (mShaderVersion < 310)
{
error(typeQualifier.line,
"in type qualifier without variable declaration supported in GLSL ES 3.10 only",
"layout");
return;
}
if (!layoutQualifier.earlyFragmentTests)
{
error(typeQualifier.line,
"only early_fragment_tests is allowed as layout qualifier when not declaring a "
"variable",
"layout");
return;
}
mEarlyFragmentTestsSpecified = true;
}
else
{
if (!checkWorkGroupSizeIsNotSpecified(typeQualifier.line, layoutQualifier))
......@@ -3713,6 +3760,8 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
}
checkYuvIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.yuv);
checkEarlyFragmentTestsIsNotSpecified(typeQualifier.line,
typeQualifier.layoutQualifier.earlyFragmentTests);
TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier);
......@@ -4348,6 +4397,11 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const ImmutableString &qual
qualifier.yuv = true;
}
}
else if (qualifierType == "early_fragment_tests")
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.earlyFragmentTests = true;
}
else if (qualifierType == "rgba32f")
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
......@@ -4858,6 +4912,8 @@ TFieldList *TParseContext::addStructDeclaratorList(const TPublicType &typeSpecif
typeSpecifier.getBasicType());
checkWorkGroupSizeIsNotSpecified(typeSpecifier.getLine(), typeSpecifier.layoutQualifier);
checkEarlyFragmentTestsIsNotSpecified(typeSpecifier.getLine(),
typeSpecifier.layoutQualifier.earlyFragmentTests);
TFieldList *fieldList = new TFieldList();
......
......@@ -75,6 +75,8 @@ class TParseContext : angle::NonCopyable
mFragmentPrecisionHighOnESSL1 = fragmentPrecisionHigh;
}
bool isEarlyFragmentTestsSpecified() const { return mEarlyFragmentTestsSpecified; }
void setLoopNestingLevel(int loopNestintLevel) { mLoopNestingLevel = loopNestintLevel; }
void incrLoopNestingLevel() { ++mLoopNestingLevel; }
......@@ -543,6 +545,8 @@ class TParseContext : angle::NonCopyable
void checkYuvIsNotSpecified(const TSourceLoc &location, bool yuv);
void checkEarlyFragmentTestsIsNotSpecified(const TSourceLoc &location, bool earlyFragmentTests);
bool checkUnsizedArrayConstructorArgumentDimensionality(const TIntermSequence &arguments,
TType type,
const TSourceLoc &line);
......@@ -620,6 +624,7 @@ class TParseContext : angle::NonCopyable
// without precision, explicit or implicit.
bool mFragmentPrecisionHighOnESSL1; // true if highp precision is supported when compiling
// ESSL1.
bool mEarlyFragmentTestsSpecified; // true if layout(early_fragment_tests) in; is specified.
TLayoutMatrixPacking mDefaultUniformMatrixPacking;
TLayoutBlockStorage mDefaultUniformBlockStorage;
TLayoutMatrixPacking mDefaultBufferMatrixPacking;
......
......@@ -638,6 +638,10 @@ TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
{
joinedQualifier.yuv = rightQualifier.yuv;
}
if (rightQualifier.earlyFragmentTests != false)
{
joinedQualifier.earlyFragmentTests = rightQualifier.earlyFragmentTests;
}
if (rightQualifier.binding != -1)
{
joinedQualifier.binding = rightQualifier.binding;
......
......@@ -90,6 +90,11 @@ bool TranslatorESSL::translate(TIntermBlock *root,
// Write array bounds clamping emulation if needed.
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
if (getShaderType() == GL_FRAGMENT_SHADER)
{
EmitEarlyFragmentTestsGLSL(*this, sink);
}
if (getShaderType() == GL_COMPUTE_SHADER)
{
EmitWorkGroupSizeGLSL(*this, sink);
......
......@@ -201,6 +201,8 @@ bool TranslatorGLSL::translate(TIntermBlock *root,
sink << "out vec4 angle_SecondaryFragData[" << getResources().MaxDualSourceDrawBuffers
<< "];\n";
}
EmitEarlyFragmentTestsGLSL(*this, sink);
}
if (getShaderType() == GL_COMPUTE_SHADER)
......
......@@ -915,6 +915,8 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
return false;
}
}
EmitEarlyFragmentTestsGLSL(*this, sink);
}
else if (getShaderType() == GL_VERTEX_SHADER)
{
......
......@@ -1924,6 +1924,21 @@ TEST_F(ComputeShaderValidationTest, NoWorkGroupSizeSpecified)
}
}
// Test that workgroup size declaration doesn't accept variable declaration.
TEST_F(ComputeShaderValidationTest, NoVariableDeclrationAfterWorkGroupSize)
{
constexpr char kShaderString[] =
R"(#version 310 es
layout(local_size_x = 1) in vec4 x;
void main()
{
})";
if (compile(kShaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Work group size is less than 1. It should be at least 1.
// GLSL ES 3.10 Revision 4, 7.1.3 Compute Shader Special Variables
// The spec is not clear whether having a local size qualifier equal zero
......@@ -6185,3 +6200,155 @@ void main() {
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that layout(early_fragment_tests) in; is valid in fragment shader
TEST_F(FragmentShaderValidationTest, ValidEarlyFragmentTests)
{
constexpr char kShaderString[] =
R"(#version 310 es
precision mediump float;
layout(early_fragment_tests) in;
out vec4 color;
void main()
{
color = vec4(0.0);
})";
if (!compile(kShaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that layout(early_fragment_tests=x) in; is invalid
TEST_F(FragmentShaderValidationTest, InvalidValueForEarlyFragmentTests)
{
constexpr char kShaderString[] =
R"(#version 310 es
precision mediump float;
layout(early_fragment_tests=1) in;
out vec4 color;
void main()
{
color = vec4(0.0);
})";
if (compile(kShaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that layout(early_fragment_tests) in varying; is invalid
TEST_F(FragmentShaderValidationTest, InvalidEarlyFragmentTestsOnVariableDecl)
{
constexpr char kShaderString[] =
R"(#version 310 es
precision mediump float;
layout(early_fragment_tests) in vec4 v;
out vec4 color;
void main()
{
color = v;
})";
if (compile(kShaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that layout(early_fragment_tests) in; is invalid in vertex shader
TEST_F(VertexShaderValidationTest, InvalidEarlyFragmentTests)
{
constexpr char kShaderString[] =
R"(#version 310 es
layout(early_fragment_tests) in;
void main()
{
gl_Position = vec4(0.0);
})";
if (compile(kShaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that layout(early_fragment_tests) in; is invalid in compute shader
TEST_F(ComputeShaderValidationTest, InvalidEarlyFragmentTests)
{
constexpr char kShaderString[] =
R"(#version 310 es
layout(local_size_x = 1) in;
layout(early_fragment_tests) in;
void main() {})";
if (compile(kShaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that layout(x) in; only accepts x=early_fragment_tests.
TEST_F(FragmentShaderValidationTest, NothingButEarlyFragmentTestsWithInWithoutVariableDecl)
{
const char *noValueQualifiers[] = {
"shared", "packed",
"std140", "std430",
"row_major", "col_major",
"location", "yuv",
"rgba32f", "rgba16f",
"r32f", "rgba8",
"rgba8_snorm", "rgba32i",
"rgba16i", "rgba8i",
"r32i", "rgba32ui",
"rgba16ui", "rgba8ui",
"r32ui", "points",
"lines", "lines_adjacency",
"triangles", "triangles_adjacency",
"line_strip", "triangle_strip",
};
const char *withValueQualifiers[] = {
"location", "binding", "offset", "local_size_x", "local_size_y",
"local_size_z", "num_views", "invocations", "max_vertices", "index",
};
constexpr char kShaderStringPre[] =
R"(#version 310 es
precision mediump float;
layout()";
constexpr char kShaderStringPost[] =
R"() in;
out vec4 color;
void main()
{
color = vec4(0.0);
})";
// Make sure the method of constructing shaders is valid.
const std::string validShaderString =
kShaderStringPre + std::string("early_fragment_tests") + kShaderStringPost;
if (!compile(validShaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
for (size_t i = 0; i < ArraySize(noValueQualifiers); ++i)
{
const std::string shaderString =
kShaderStringPre + std::string(noValueQualifiers[i]) + kShaderStringPost;
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
for (size_t i = 0; i < ArraySize(withValueQualifiers); ++i)
{
const std::string shaderString =
kShaderStringPre + std::string(withValueQualifiers[i]) + "=1" + kShaderStringPost;
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
}
......@@ -60,7 +60,6 @@
// Image related failures
4314 : KHR-GLES31.core.shader_image_load_store.basic-glsl-earlyFragTests = FAIL
4316 : KHR-GLES31.core.shader_image_load_store.negative-linkErrors = FAIL
4312 VULKAN : KHR-GLES31.core.shader_image_load_store.basic-glsl-misc-fs = SKIP
4312 VULKAN : KHR-GLES31.core.shader_image_load_store.advanced-sync-imageAccess = SKIP
......
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