Commit c28888b3 by Martin Radev Committed by Commit Bot

Relax checks when parsing type qualifiers in GLSL ES 3.10

The grammar in GLSL ES 3.10 does not impose a strict order on the qualifiers and also allows multiple layout qualifiers. This patch relaxes the checks when parsing a type qualifier. BUG=angleproject:1442 TEST=angle_unittests Change-Id: Ib3653a1ed1bfced099a6b2cbf35a7cd480c9100a Reviewed-on: https://chromium-review.googlesource.com/379016 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 29639857
......@@ -16,6 +16,17 @@
#include "compiler/translator/ValidateGlobalInitializer.h"
#include "compiler/translator/util.h"
namespace
{
// GLSL ES 3.10 does not impose a strict order on type qualifiers and allows multiple layout
// declarations
// GLSL ES 3.10 Revision 4, 4.10 Order of Qualification
bool AreTypeQualifierChecksRelaxed(int shaderVersion)
{
return shaderVersion >= 310;
}
} // namespace
///////////////////////////////////////////////////////////////////////
//
// Sub- vector and matrix fields
......@@ -934,7 +945,8 @@ void TParseContext::checkIsParameterQualifierValid(
const TTypeQualifierBuilder &typeQualifierBuilder,
TType *type)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(&mDiagnostics);
TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut)
{
......@@ -1402,7 +1414,8 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
const TPublicType &typeSpecifier)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
TPublicType returnType = typeSpecifier;
returnType.qualifier = typeQualifier.qualifier;
......@@ -1704,7 +1717,8 @@ TIntermAggregate *TParseContext::parseInvariantDeclaration(
const TString *identifier,
const TSymbol *symbol)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
if (!typeQualifier.invariant)
{
......@@ -1912,7 +1926,8 @@ TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &pub
void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier,
......@@ -2492,7 +2507,8 @@ TIntermAggregate *TParseContext::addInterfaceBlock(
{
checkIsNotReserved(nameLine, blockName);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
if (typeQualifier.qualifier != EvqUniform)
{
......@@ -3179,37 +3195,8 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif
TLayoutQualifier rightQualifier,
const TSourceLoc &rightQualifierLocation)
{
TLayoutQualifier joinedQualifier = leftQualifier;
if (rightQualifier.location != -1)
{
joinedQualifier.location = rightQualifier.location;
}
if (rightQualifier.matrixPacking != EmpUnspecified)
{
joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
}
if (rightQualifier.blockStorage != EbsUnspecified)
{
joinedQualifier.blockStorage = rightQualifier.blockStorage;
}
for (size_t i = 0u; i < rightQualifier.localSize.size(); ++i)
{
if (rightQualifier.localSize[i] != -1)
{
if (joinedQualifier.localSize[i] != -1 &&
joinedQualifier.localSize[i] != rightQualifier.localSize[i])
{
error(rightQualifierLocation,
"Cannot have multiple different work group size specifiers",
getWorkGroupSizeString(i));
}
joinedQualifier.localSize[i] = rightQualifier.localSize[i];
}
}
return joinedQualifier;
return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation,
&mDiagnostics);
}
TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
......@@ -3217,7 +3204,8 @@ TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
TPublicType *typeSpecifier,
TFieldList *fieldList)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
typeSpecifier->qualifier = typeQualifier.qualifier;
typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier;
......
......@@ -15,6 +15,14 @@
class TDiagnostics;
namespace sh
{
TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
TLayoutQualifier rightQualifier,
const TSourceLoc &rightQualifierLocation,
TDiagnostics *diagnostics);
} // namespace sh
enum TQualifierType
{
QtInvariant,
......@@ -32,6 +40,7 @@ class TQualifierWrapperBase : angle::NonCopyable
virtual ~TQualifierWrapperBase(){};
virtual TQualifierType getType() const = 0;
virtual TString getQualifierString() const = 0;
virtual unsigned int getRank() const = 0;
const TSourceLoc &getLine() const { return mLine; }
private:
TSourceLoc mLine;
......@@ -45,6 +54,7 @@ class TInvariantQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtInvariant; }
TString getQualifierString() const { return "invariant"; }
unsigned int getRank() const;
};
class TInterpolationQualifierWrapper final : public TQualifierWrapperBase
......@@ -59,6 +69,7 @@ class TInterpolationQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtInterpolation; }
TString getQualifierString() const { return ::getQualifierString(mInterpolationQualifier); }
TQualifier getQualifier() const { return mInterpolationQualifier; }
unsigned int getRank() const;
private:
TQualifier mInterpolationQualifier;
......@@ -76,6 +87,8 @@ class TLayoutQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtLayout; }
TString getQualifierString() const { return "layout"; }
const TLayoutQualifier &getQualifier() const { return mLayoutQualifier; }
unsigned int getRank() const;
private:
TLayoutQualifier mLayoutQualifier;
};
......@@ -92,6 +105,8 @@ class TStorageQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtStorage; }
TString getQualifierString() const { return ::getQualifierString(mStorageQualifier); }
TQualifier getQualifier() const { return mStorageQualifier; }
unsigned int getRank() const;
private:
TQualifier mStorageQualifier;
};
......@@ -108,6 +123,8 @@ class TPrecisionQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtPrecision; }
TString getQualifierString() const { return ::getPrecisionString(mPrecisionQualifier); }
TPrecision getQualifier() const { return mPrecisionQualifier; }
unsigned int getRank() const;
private:
TPrecision mPrecisionQualifier;
};
......@@ -130,28 +147,26 @@ struct TTypeQualifier
class TTypeQualifierBuilder : angle::NonCopyable
{
public:
using QualifierSequence = std::vector<const TQualifierWrapperBase *>;
public:
TTypeQualifierBuilder(const TStorageQualifierWrapper *scope);
// Adds the passed qualifier to the end of the sequence.
void appendQualifier(const TQualifierWrapperBase *qualifier);
// Checks for the order of qualification and repeating qualifiers.
bool checkOrderIsValid(TDiagnostics *diagnostics) const;
bool checkSequenceIsValid(TDiagnostics *diagnostics, bool areQualifierChecksRelaxed) const;
// Goes over the qualifier sequence and parses it to form a type qualifier for a function
// parameter.
// The returned object is initialized even if the parsing fails.
TTypeQualifier getParameterTypeQualifier(TDiagnostics *diagnostics) const;
TTypeQualifier getParameterTypeQualifier(TDiagnostics *diagnostics,
bool areQualifierChecksRelaxed) const;
// Goes over the qualifier sequence and parses it to form a type qualifier for a variable.
// The returned object is initialized even if the parsing fails.
TTypeQualifier getVariableTypeQualifier(TDiagnostics *diagnostics) const;
TTypeQualifier getVariableTypeQualifier(TDiagnostics *diagnostics,
bool areQualifierChecksRelaxed) const;
private:
// Handles the joining of storage qualifiers for a parameter in a function.
bool joinParameterStorageQualifier(TQualifier *joinedQualifier,
TQualifier storageQualifier) const;
// Handles the joining of storage qualifiers for variables.
bool joinVariableStorageQualifier(TQualifier *joinedQualifier,
TQualifier storageQualifier) const;
std::vector<const TQualifierWrapperBase *> mQualifiers;
QualifierSequence mQualifiers;
};
#endif // COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
......@@ -56,6 +56,7 @@
'<(angle_path)/src/tests/compiler_tests/Pack_Unpack_test.cpp',
'<(angle_path)/src/tests/compiler_tests/PruneEmptyDeclarations_test.cpp',
'<(angle_path)/src/tests/compiler_tests/PruneUnusedFunctions_test.cpp',
'<(angle_path)/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp',
'<(angle_path)/src/tests/compiler_tests/QualificationOrder_test.cpp',
'<(angle_path)/src/tests/compiler_tests/RecordConstantPrecision_test.cpp',
'<(angle_path)/src/tests/compiler_tests/RemovePow_test.cpp',
......
//
// Copyright (c) 2016 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.
//
// QualificationOrderESSL31_test.cpp:
// OpenGL ES 3.1 removes the strict order of qualifiers imposed by the grammar.
// This file contains tests for invalid order and usage of qualifiers in GLSL ES 3.10.
#include "gtest/gtest.h"
#include "angle_gl.h"
#include "compiler/translator/TranslatorESSL.h"
#include "GLSLANG/ShaderLang.h"
#include "tests/test_utils/compiler_test.h"
class QualificationVertexShaderTestESSL31 : public testing::Test
{
public:
QualificationVertexShaderTestESSL31() {}
protected:
virtual void SetUp()
{
ShBuiltInResources resources;
ShInitBuiltInResources(&resources);
mTranslator = new TranslatorESSL(GL_VERTEX_SHADER, SH_GLES3_1_SPEC);
ASSERT_TRUE(mTranslator->Init(resources));
}
virtual void TearDown() { delete mTranslator; }
// Return true when compilation succeeds
bool compile(const std::string &shaderString)
{
const char *shaderStrings[] = {shaderString.c_str()};
mASTRoot = mTranslator->compileTreeForTesting(shaderStrings, 1,
SH_INTERMEDIATE_TREE | SH_VARIABLES);
TInfoSink &infoSink = mTranslator->getInfoSink();
mInfoLog = infoSink.info.c_str();
return mASTRoot != nullptr;
}
const TIntermSymbol *findSymbolInAST(const TString &stringToFind, TBasicType basicType)
{
ShaderVariableFinder finder(stringToFind, basicType);
mASTRoot->traverse(&finder);
return finder.getNode();
}
protected:
TranslatorESSL *mTranslator;
TIntermNode *mASTRoot;
std::string mInfoLog;
};
// GLSL ES 3.10 has relaxed checks on qualifier order. Any order is correct.
TEST_F(QualificationVertexShaderTestESSL31, CentroidOut)
{
const std::string &shaderString =
"#version 310 es\n"
"precision lowp float;\n"
"out centroid float something;\n"
"void main(){\n"
" something = 1.0;\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success" << mInfoLog;
}
else
{
const TIntermSymbol *node = findSymbolInAST("something", EbtFloat);
ASSERT_NE(nullptr, node);
const TType &type = node->getType();
EXPECT_EQ(EvqCentroidOut, type.getQualifier());
}
}
// GLSL ES 3.10 has relaxed checks on qualifier order. Any order is correct.
TEST_F(QualificationVertexShaderTestESSL31, AllQualifiersMixed)
{
const std::string &shaderString =
"#version 310 es\n"
"precision lowp float;\n"
"highp out invariant centroid flat vec4 something;\n"
"void main(){\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success" << mInfoLog;
}
else
{
const TIntermSymbol *node = findSymbolInAST("something", EbtFloat);
ASSERT_NE(nullptr, node);
const TType &type = node->getType();
EXPECT_TRUE(type.isInvariant());
EXPECT_EQ(EvqFlatOut, type.getQualifier());
EXPECT_EQ(EbpHigh, type.getPrecision());
}
}
// GLSL ES 3.10 allows multiple layout qualifiers to be specified.
TEST_F(QualificationVertexShaderTestESSL31, MultipleLayouts)
{
const std::string &shaderString =
"#version 310 es\n"
"precision lowp float;\n"
"in layout(location=1) layout(location=2) vec4 something;\n"
"void main(){\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success" << mInfoLog;
}
else
{
const TIntermSymbol *node = findSymbolInAST("something", EbtFloat);
ASSERT_NE(nullptr, node);
const TType &type = node->getType();
EXPECT_EQ(EvqVertexIn, type.getQualifier());
EXPECT_EQ(2, type.getLayoutQualifier().location);
}
}
// The test checks layout qualifier overriding when multiple layouts are specified.
TEST_F(QualificationVertexShaderTestESSL31, MultipleLayoutsInterfaceBlock)
{
const std::string &shaderString =
"#version 310 es\n"
"precision lowp float;\n"
"out float someValue;\n"
"layout(shared) layout(std140) layout(column_major) uniform MyInterface\n"
"{ vec4 something; } MyInterfaceName;\n"
"void main(){\n"
" someValue = MyInterfaceName.something.r;\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success" << mInfoLog;
}
else
{
const TIntermSymbol *node = findSymbolInAST("MyInterfaceName", EbtInterfaceBlock);
ASSERT_NE(nullptr, node);
const TType &type = node->getType();
TLayoutQualifier layoutQualifier = type.getLayoutQualifier();
EXPECT_EQ(EbsStd140, layoutQualifier.blockStorage);
EXPECT_EQ(EmpColumnMajor, layoutQualifier.matrixPacking);
}
}
// The test checks layout qualifier overriding when multiple layouts are specified.
TEST_F(QualificationVertexShaderTestESSL31, MultipleLayoutsInterfaceBlock2)
{
const std::string &shaderString =
"#version 310 es\n"
"precision lowp float;\n"
"out float someValue;\n"
"layout(row_major) layout(std140) layout(shared) uniform MyInterface\n"
"{ vec4 something; } MyInterfaceName;\n"
"void main(){\n"
" someValue = MyInterfaceName.something.r;\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success" << mInfoLog;
}
else
{
const TIntermSymbol *node = findSymbolInAST("MyInterfaceName", EbtInterfaceBlock);
ASSERT_NE(nullptr, node);
const TType &type = node->getType();
TLayoutQualifier layoutQualifier = type.getLayoutQualifier();
EXPECT_EQ(EbsShared, layoutQualifier.blockStorage);
EXPECT_EQ(EmpRowMajor, layoutQualifier.matrixPacking);
}
}
......@@ -11,8 +11,10 @@
#include <map>
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "angle_gl.h"
#include "compiler/translator/TranslatorESSL.h"
#include "GLSLANG/ShaderLang.h"
bool compileTestShader(GLenum type,
......@@ -85,4 +87,32 @@ class MatchOutputCodeTest : public testing::Test
std::map<ShShaderOutput, std::string> mOutputCode;
};
class ShaderVariableFinder : public TIntermTraverser
{
public:
ShaderVariableFinder(const TString &variableName, TBasicType basicType)
: TIntermTraverser(true, false, false),
mVariableName(variableName),
mNodeFound(nullptr),
mBasicType(basicType)
{
}
void visitSymbol(TIntermSymbol *node)
{
if (node->getBasicType() == mBasicType && node->getSymbol() == mVariableName)
{
mNodeFound = node;
}
}
bool isFound() const { return mNodeFound != nullptr; }
const TIntermSymbol *getNode() const { return mNodeFound; }
private:
TString mVariableName;
TIntermSymbol *mNodeFound;
TBasicType mBasicType;
};
#endif // TESTS_TEST_UTILS_COMPILER_TEST_H_
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