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 @@ ...@@ -16,6 +16,17 @@
#include "compiler/translator/ValidateGlobalInitializer.h" #include "compiler/translator/ValidateGlobalInitializer.h"
#include "compiler/translator/util.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 // Sub- vector and matrix fields
...@@ -934,7 +945,8 @@ void TParseContext::checkIsParameterQualifierValid( ...@@ -934,7 +945,8 @@ void TParseContext::checkIsParameterQualifierValid(
const TTypeQualifierBuilder &typeQualifierBuilder, const TTypeQualifierBuilder &typeQualifierBuilder,
TType *type) TType *type)
{ {
TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(&mDiagnostics); TTypeQualifier typeQualifier = typeQualifierBuilder.getParameterTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut) if (typeQualifier.qualifier == EvqOut || typeQualifier.qualifier == EvqInOut)
{ {
...@@ -1402,7 +1414,8 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, ...@@ -1402,7 +1414,8 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder, TPublicType TParseContext::addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
const TPublicType &typeSpecifier) const TPublicType &typeSpecifier)
{ {
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
TPublicType returnType = typeSpecifier; TPublicType returnType = typeSpecifier;
returnType.qualifier = typeQualifier.qualifier; returnType.qualifier = typeQualifier.qualifier;
...@@ -1704,7 +1717,8 @@ TIntermAggregate *TParseContext::parseInvariantDeclaration( ...@@ -1704,7 +1717,8 @@ TIntermAggregate *TParseContext::parseInvariantDeclaration(
const TString *identifier, const TString *identifier,
const TSymbol *symbol) const TSymbol *symbol)
{ {
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
if (!typeQualifier.invariant) if (!typeQualifier.invariant)
{ {
...@@ -1912,7 +1926,8 @@ TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &pub ...@@ -1912,7 +1926,8 @@ TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &pub
void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder) void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
{ {
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier; const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier, checkInvariantVariableQualifier(typeQualifier.invariant, typeQualifier.qualifier,
...@@ -2492,7 +2507,8 @@ TIntermAggregate *TParseContext::addInterfaceBlock( ...@@ -2492,7 +2507,8 @@ TIntermAggregate *TParseContext::addInterfaceBlock(
{ {
checkIsNotReserved(nameLine, blockName); checkIsNotReserved(nameLine, blockName);
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
if (typeQualifier.qualifier != EvqUniform) if (typeQualifier.qualifier != EvqUniform)
{ {
...@@ -3179,37 +3195,8 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif ...@@ -3179,37 +3195,8 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif
TLayoutQualifier rightQualifier, TLayoutQualifier rightQualifier,
const TSourceLoc &rightQualifierLocation) const TSourceLoc &rightQualifierLocation)
{ {
TLayoutQualifier joinedQualifier = leftQualifier; return sh::JoinLayoutQualifiers(leftQualifier, rightQualifier, rightQualifierLocation,
&mDiagnostics);
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;
} }
TFieldList *TParseContext::addStructDeclaratorListWithQualifiers( TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
...@@ -3217,7 +3204,8 @@ TFieldList *TParseContext::addStructDeclaratorListWithQualifiers( ...@@ -3217,7 +3204,8 @@ TFieldList *TParseContext::addStructDeclaratorListWithQualifiers(
TPublicType *typeSpecifier, TPublicType *typeSpecifier,
TFieldList *fieldList) TFieldList *fieldList)
{ {
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(&mDiagnostics); TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(
&mDiagnostics, AreTypeQualifierChecksRelaxed(mShaderVersion));
typeSpecifier->qualifier = typeQualifier.qualifier; typeSpecifier->qualifier = typeQualifier.qualifier;
typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier; typeSpecifier->layoutQualifier = typeQualifier.layoutQualifier;
......
...@@ -8,6 +8,49 @@ ...@@ -8,6 +8,49 @@
#include "Diagnostics.h" #include "Diagnostics.h"
#include <algorithm>
namespace sh
{
TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
TLayoutQualifier rightQualifier,
const TSourceLoc &rightQualifierLocation,
TDiagnostics *diagnostics)
{
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])
{
diagnostics->error(rightQualifierLocation,
"Cannot have multiple different work group size specifiers",
getWorkGroupSizeString(i), "");
}
joinedQualifier.localSize[i] = rightQualifier.localSize[i];
}
}
return joinedQualifier;
}
} // namespace sh
namespace namespace
{ {
...@@ -27,7 +70,7 @@ bool IsScopeQualifierWrapper(const TQualifierWrapperBase *qualifier) ...@@ -27,7 +70,7 @@ bool IsScopeQualifierWrapper(const TQualifierWrapperBase *qualifier)
} }
// Returns true if the invariant for the qualifier sequence holds // Returns true if the invariant for the qualifier sequence holds
bool IsInvariantCorrect(const std::vector<const TQualifierWrapperBase *> &qualifiers) bool IsInvariantCorrect(const TTypeQualifierBuilder::QualifierSequence &qualifiers)
{ {
// We should have at least one qualifier. // We should have at least one qualifier.
// The first qualifier always tells the scope. // The first qualifier always tells the scope.
...@@ -35,7 +78,9 @@ bool IsInvariantCorrect(const std::vector<const TQualifierWrapperBase *> &qualif ...@@ -35,7 +78,9 @@ bool IsInvariantCorrect(const std::vector<const TQualifierWrapperBase *> &qualif
} }
// Returns true if there are qualifiers which have been specified multiple times // Returns true if there are qualifiers which have been specified multiple times
bool HasRepeatingQualifiers(const std::vector<const TQualifierWrapperBase *> &qualifiers, // If areQualifierChecksRelaxed is set to true, then layout qualifier repetition is allowed.
bool HasRepeatingQualifiers(const TTypeQualifierBuilder::QualifierSequence &qualifiers,
bool areQualifierChecksRelaxed,
std::string *errorMessage) std::string *errorMessage)
{ {
bool invariantFound = false; bool invariantFound = false;
...@@ -71,7 +116,7 @@ bool HasRepeatingQualifiers(const std::vector<const TQualifierWrapperBase *> &qu ...@@ -71,7 +116,7 @@ bool HasRepeatingQualifiers(const std::vector<const TQualifierWrapperBase *> &qu
} }
case QtLayout: case QtLayout:
{ {
if (layoutFound) if (layoutFound && !areQualifierChecksRelaxed)
{ {
*errorMessage = "The layout qualifier specified multiple times."; *errorMessage = "The layout qualifier specified multiple times.";
return true; return true;
...@@ -127,7 +172,7 @@ bool HasRepeatingQualifiers(const std::vector<const TQualifierWrapperBase *> &qu ...@@ -127,7 +172,7 @@ bool HasRepeatingQualifiers(const std::vector<const TQualifierWrapperBase *> &qu
// The correct order of qualifiers is: // The correct order of qualifiers is:
// invariant-qualifier interpolation-qualifier storage-qualifier precision-qualifier // invariant-qualifier interpolation-qualifier storage-qualifier precision-qualifier
// layout-qualifier has to be before storage-qualifier. // layout-qualifier has to be before storage-qualifier.
bool AreQualifiersInOrder(const std::vector<const TQualifierWrapperBase *> &qualifiers, bool AreQualifiersInOrder(const TTypeQualifierBuilder::QualifierSequence &qualifiers,
std::string *errorMessage) std::string *errorMessage)
{ {
bool foundInterpolation = false; bool foundInterpolation = false;
...@@ -188,186 +233,24 @@ bool AreQualifiersInOrder(const std::vector<const TQualifierWrapperBase *> &qual ...@@ -188,186 +233,24 @@ bool AreQualifiersInOrder(const std::vector<const TQualifierWrapperBase *> &qual
return true; return true;
} }
} // namespace struct QualifierComparator
TTypeQualifier::TTypeQualifier(TQualifier scope, const TSourceLoc &loc)
: layoutQualifier(TLayoutQualifier::create()),
precision(EbpUndefined),
qualifier(scope),
invariant(false),
line(loc)
{
ASSERT(IsScopeQualifier(qualifier));
}
TTypeQualifierBuilder::TTypeQualifierBuilder(const TStorageQualifierWrapper *scope)
{
ASSERT(IsScopeQualifier(scope->getQualifier()));
mQualifiers.push_back(scope);
}
void TTypeQualifierBuilder::appendQualifier(const TQualifierWrapperBase *qualifier)
{
mQualifiers.push_back(qualifier);
}
bool TTypeQualifierBuilder::checkOrderIsValid(TDiagnostics *diagnostics) const
{
std::string errorMessage;
if (HasRepeatingQualifiers(mQualifiers, &errorMessage))
{
diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
"");
return false;
}
if (!AreQualifiersInOrder(mQualifiers, &errorMessage))
{
diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
"");
return false;
}
return true;
}
TTypeQualifier TTypeQualifierBuilder::getParameterTypeQualifier(TDiagnostics *diagnostics) const
{ {
ASSERT(IsInvariantCorrect(mQualifiers)); bool operator()(const TQualifierWrapperBase *q1, const TQualifierWrapperBase *q2)
ASSERT(static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier() ==
EvqTemporary);
TTypeQualifier typeQualifier(EvqTemporary, mQualifiers[0]->getLine());
if (!checkOrderIsValid(diagnostics))
{
return typeQualifier;
}
for (size_t i = 1; i < mQualifiers.size(); ++i)
{
const TQualifierWrapperBase *qualifier = mQualifiers[i];
bool isQualifierValid = false;
switch (qualifier->getType())
{
case QtInvariant:
case QtInterpolation:
case QtLayout:
break;
case QtStorage:
isQualifierValid = joinParameterStorageQualifier(
&typeQualifier.qualifier,
static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
break;
case QtPrecision:
isQualifierValid = true;
typeQualifier.precision =
static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
ASSERT(typeQualifier.precision != EbpUndefined);
break;
default:
UNREACHABLE();
}
if (!isQualifierValid)
{
const TString &qualifierString = qualifier->getQualifierString();
diagnostics->error(qualifier->getLine(), "invalid parameter qualifier",
qualifierString.c_str(), "");
break;
}
}
switch (typeQualifier.qualifier)
{ {
case EvqIn: return q1->getRank() < q2->getRank();
case EvqConstReadOnly: // const in
case EvqOut:
case EvqInOut:
break;
case EvqConst:
typeQualifier.qualifier = EvqConstReadOnly;
break;
case EvqTemporary:
// no qualifier has been specified, set it to EvqIn which is the default
typeQualifier.qualifier = EvqIn;
break;
default:
diagnostics->error(mQualifiers[0]->getLine(), "Invalid parameter qualifier ",
getQualifierString(typeQualifier.qualifier), "");
} }
return typeQualifier; };
}
TTypeQualifier TTypeQualifierBuilder::getVariableTypeQualifier(TDiagnostics *diagnostics) const void SortSequence(TTypeQualifierBuilder::QualifierSequence &qualifiers)
{ {
ASSERT(IsInvariantCorrect(mQualifiers)); // We need a stable sorting algorithm since the order of layout-qualifier declarations matter.
// The sorting starts from index 1, instead of 0, since the element at index 0 tells the scope
TQualifier scope = // and we always want it to be first.
static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier(); std::stable_sort(qualifiers.begin() + 1, qualifiers.end(), QualifierComparator());
TTypeQualifier typeQualifier = TTypeQualifier(scope, mQualifiers[0]->getLine());
if (!checkOrderIsValid(diagnostics))
{
return typeQualifier;
}
for (size_t i = 1; i < mQualifiers.size(); ++i)
{
const TQualifierWrapperBase *qualifier = mQualifiers[i];
bool isQualifierValid = false;
switch (qualifier->getType())
{
case QtInvariant:
isQualifierValid = true;
typeQualifier.invariant = true;
break;
case QtInterpolation:
{
switch (typeQualifier.qualifier)
{
case EvqGlobal:
isQualifierValid = true;
typeQualifier.qualifier =
static_cast<const TInterpolationQualifierWrapper *>(qualifier)
->getQualifier();
break;
default:
isQualifierValid = false;
}
break;
}
case QtLayout:
isQualifierValid = true;
typeQualifier.layoutQualifier =
static_cast<const TLayoutQualifierWrapper *>(qualifier)->getQualifier();
break;
case QtStorage:
isQualifierValid = joinVariableStorageQualifier(
&typeQualifier.qualifier,
static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
break;
case QtPrecision:
isQualifierValid = true;
typeQualifier.precision =
static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
ASSERT(typeQualifier.precision != EbpUndefined);
break;
default:
UNREACHABLE();
}
if (!isQualifierValid)
{
const TString &qualifierString = qualifier->getQualifierString();
diagnostics->error(qualifier->getLine(), "invalid qualifier combination",
qualifierString.c_str(), "");
break;
}
}
return typeQualifier;
} }
bool TTypeQualifierBuilder::joinVariableStorageQualifier(TQualifier *joinedQualifier, // Handles the joining of storage qualifiers for variables.
TQualifier storageQualifier) const bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storageQualifier)
{ {
switch (*joinedQualifier) switch (*joinedQualifier)
{ {
...@@ -443,8 +326,8 @@ bool TTypeQualifierBuilder::joinVariableStorageQualifier(TQualifier *joinedQuali ...@@ -443,8 +326,8 @@ bool TTypeQualifierBuilder::joinVariableStorageQualifier(TQualifier *joinedQuali
return true; return true;
} }
bool TTypeQualifierBuilder::joinParameterStorageQualifier(TQualifier *joinedQualifier, // Handles the joining of storage qualifiers for a parameter in a function.
TQualifier storageQualifier) const bool JoinParameterStorageQualifier(TQualifier *joinedQualifier, TQualifier storageQualifier)
{ {
switch (*joinedQualifier) switch (*joinedQualifier)
{ {
...@@ -468,3 +351,257 @@ bool TTypeQualifierBuilder::joinParameterStorageQualifier(TQualifier *joinedQual ...@@ -468,3 +351,257 @@ bool TTypeQualifierBuilder::joinParameterStorageQualifier(TQualifier *joinedQual
} }
return true; return true;
} }
TTypeQualifier GetVariableTypeQualifierFromSortedSequence(
const TTypeQualifierBuilder::QualifierSequence &sortedSequence,
TDiagnostics *diagnostics)
{
TTypeQualifier typeQualifier(
static_cast<const TStorageQualifierWrapper *>(sortedSequence[0])->getQualifier(),
sortedSequence[0]->getLine());
for (size_t i = 1; i < sortedSequence.size(); ++i)
{
const TQualifierWrapperBase *qualifier = sortedSequence[i];
bool isQualifierValid = false;
switch (qualifier->getType())
{
case QtInvariant:
isQualifierValid = true;
typeQualifier.invariant = true;
break;
case QtInterpolation:
{
switch (typeQualifier.qualifier)
{
case EvqGlobal:
isQualifierValid = true;
typeQualifier.qualifier =
static_cast<const TInterpolationQualifierWrapper *>(qualifier)
->getQualifier();
break;
default:
isQualifierValid = false;
}
break;
}
case QtLayout:
{
const TLayoutQualifierWrapper *layoutQualifierWrapper =
static_cast<const TLayoutQualifierWrapper *>(qualifier);
isQualifierValid = true;
typeQualifier.layoutQualifier = sh::JoinLayoutQualifiers(
typeQualifier.layoutQualifier, layoutQualifierWrapper->getQualifier(),
layoutQualifierWrapper->getLine(), diagnostics);
break;
}
case QtStorage:
isQualifierValid = JoinVariableStorageQualifier(
&typeQualifier.qualifier,
static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
break;
case QtPrecision:
isQualifierValid = true;
typeQualifier.precision =
static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
ASSERT(typeQualifier.precision != EbpUndefined);
break;
default:
UNREACHABLE();
}
if (!isQualifierValid)
{
const TString &qualifierString = qualifier->getQualifierString();
diagnostics->error(qualifier->getLine(), "invalid qualifier combination",
qualifierString.c_str(), "");
break;
}
}
return typeQualifier;
}
TTypeQualifier GetParameterTypeQualifierFromSortedSequence(
const TTypeQualifierBuilder::QualifierSequence &sortedSequence,
TDiagnostics *diagnostics)
{
TTypeQualifier typeQualifier(EvqTemporary, sortedSequence[0]->getLine());
for (size_t i = 1; i < sortedSequence.size(); ++i)
{
const TQualifierWrapperBase *qualifier = sortedSequence[i];
bool isQualifierValid = false;
switch (qualifier->getType())
{
case QtInvariant:
case QtInterpolation:
case QtLayout:
break;
case QtStorage:
isQualifierValid = JoinParameterStorageQualifier(
&typeQualifier.qualifier,
static_cast<const TStorageQualifierWrapper *>(qualifier)->getQualifier());
break;
case QtPrecision:
isQualifierValid = true;
typeQualifier.precision =
static_cast<const TPrecisionQualifierWrapper *>(qualifier)->getQualifier();
ASSERT(typeQualifier.precision != EbpUndefined);
break;
default:
UNREACHABLE();
}
if (!isQualifierValid)
{
const TString &qualifierString = qualifier->getQualifierString();
diagnostics->error(qualifier->getLine(), "invalid parameter qualifier",
qualifierString.c_str(), "");
break;
}
}
switch (typeQualifier.qualifier)
{
case EvqIn:
case EvqConstReadOnly: // const in
case EvqOut:
case EvqInOut:
break;
case EvqConst:
typeQualifier.qualifier = EvqConstReadOnly;
break;
case EvqTemporary:
// no qualifier has been specified, set it to EvqIn which is the default
typeQualifier.qualifier = EvqIn;
break;
default:
diagnostics->error(sortedSequence[0]->getLine(), "Invalid parameter qualifier ",
getQualifierString(typeQualifier.qualifier), "");
}
return typeQualifier;
}
} // namespace
unsigned int TInvariantQualifierWrapper::getRank() const
{
return 0u;
}
unsigned int TInterpolationQualifierWrapper::getRank() const
{
return 1u;
}
unsigned int TLayoutQualifierWrapper::getRank() const
{
return 2u;
}
unsigned int TStorageQualifierWrapper::getRank() const
{
// Force the 'centroid' auxilary storage qualifier to be always first among all storage
// qualifiers.
if (mStorageQualifier == EvqCentroid)
{
return 3u;
}
else
{
return 4u;
}
}
unsigned int TPrecisionQualifierWrapper::getRank() const
{
return 5u;
}
TTypeQualifier::TTypeQualifier(TQualifier scope, const TSourceLoc &loc)
: layoutQualifier(TLayoutQualifier::create()),
precision(EbpUndefined),
qualifier(scope),
invariant(false),
line(loc)
{
ASSERT(IsScopeQualifier(qualifier));
}
TTypeQualifierBuilder::TTypeQualifierBuilder(const TStorageQualifierWrapper *scope)
{
ASSERT(IsScopeQualifier(scope->getQualifier()));
mQualifiers.push_back(scope);
}
void TTypeQualifierBuilder::appendQualifier(const TQualifierWrapperBase *qualifier)
{
mQualifiers.push_back(qualifier);
}
bool TTypeQualifierBuilder::checkSequenceIsValid(TDiagnostics *diagnostics,
bool areQualifierChecksRelaxed) const
{
std::string errorMessage;
if (HasRepeatingQualifiers(mQualifiers, areQualifierChecksRelaxed, &errorMessage))
{
diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
"");
return false;
}
if (!areQualifierChecksRelaxed && !AreQualifiersInOrder(mQualifiers, &errorMessage))
{
diagnostics->error(mQualifiers[0]->getLine(), "qualifier sequence", errorMessage.c_str(),
"");
return false;
}
return true;
}
TTypeQualifier TTypeQualifierBuilder::getParameterTypeQualifier(
TDiagnostics *diagnostics,
bool areQualifierChecksRelaxed) const
{
ASSERT(IsInvariantCorrect(mQualifiers));
ASSERT(static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier() ==
EvqTemporary);
if (!checkSequenceIsValid(diagnostics, areQualifierChecksRelaxed))
{
return TTypeQualifier(EvqTemporary, mQualifiers[0]->getLine());
}
// If the qualifier checks are relaxed, then it is easier to sort the qualifiers so
// that the order imposed by the GLSL ES 3.00 spec is kept. Then we can use the same code to
// combine the qualifiers.
if (areQualifierChecksRelaxed)
{
// Copy the qualifier sequence so that we can sort them.
QualifierSequence sortedQualifierSequence = mQualifiers;
SortSequence(sortedQualifierSequence);
return GetParameterTypeQualifierFromSortedSequence(sortedQualifierSequence, diagnostics);
}
return GetParameterTypeQualifierFromSortedSequence(mQualifiers, diagnostics);
}
TTypeQualifier TTypeQualifierBuilder::getVariableTypeQualifier(TDiagnostics *diagnostics,
bool areQualifierChecksRelaxed) const
{
ASSERT(IsInvariantCorrect(mQualifiers));
if (!checkSequenceIsValid(diagnostics, areQualifierChecksRelaxed))
{
return TTypeQualifier(
static_cast<const TStorageQualifierWrapper *>(mQualifiers[0])->getQualifier(),
mQualifiers[0]->getLine());
}
// If the qualifier checks are relaxed, then it is easier to sort the qualifiers so
// that the order imposed by the GLSL ES 3.00 spec is kept. Then we can use the same code to
// combine the qualifiers.
if (areQualifierChecksRelaxed)
{
// Copy the qualifier sequence so that we can sort them.
QualifierSequence sortedQualifierSequence = mQualifiers;
SortSequence(sortedQualifierSequence);
return GetVariableTypeQualifierFromSortedSequence(sortedQualifierSequence, diagnostics);
}
return GetVariableTypeQualifierFromSortedSequence(mQualifiers, diagnostics);
}
...@@ -15,6 +15,14 @@ ...@@ -15,6 +15,14 @@
class TDiagnostics; class TDiagnostics;
namespace sh
{
TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
TLayoutQualifier rightQualifier,
const TSourceLoc &rightQualifierLocation,
TDiagnostics *diagnostics);
} // namespace sh
enum TQualifierType enum TQualifierType
{ {
QtInvariant, QtInvariant,
...@@ -32,6 +40,7 @@ class TQualifierWrapperBase : angle::NonCopyable ...@@ -32,6 +40,7 @@ class TQualifierWrapperBase : angle::NonCopyable
virtual ~TQualifierWrapperBase(){}; virtual ~TQualifierWrapperBase(){};
virtual TQualifierType getType() const = 0; virtual TQualifierType getType() const = 0;
virtual TString getQualifierString() const = 0; virtual TString getQualifierString() const = 0;
virtual unsigned int getRank() const = 0;
const TSourceLoc &getLine() const { return mLine; } const TSourceLoc &getLine() const { return mLine; }
private: private:
TSourceLoc mLine; TSourceLoc mLine;
...@@ -45,6 +54,7 @@ class TInvariantQualifierWrapper final : public TQualifierWrapperBase ...@@ -45,6 +54,7 @@ class TInvariantQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtInvariant; } TQualifierType getType() const { return QtInvariant; }
TString getQualifierString() const { return "invariant"; } TString getQualifierString() const { return "invariant"; }
unsigned int getRank() const;
}; };
class TInterpolationQualifierWrapper final : public TQualifierWrapperBase class TInterpolationQualifierWrapper final : public TQualifierWrapperBase
...@@ -59,6 +69,7 @@ class TInterpolationQualifierWrapper final : public TQualifierWrapperBase ...@@ -59,6 +69,7 @@ class TInterpolationQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtInterpolation; } TQualifierType getType() const { return QtInterpolation; }
TString getQualifierString() const { return ::getQualifierString(mInterpolationQualifier); } TString getQualifierString() const { return ::getQualifierString(mInterpolationQualifier); }
TQualifier getQualifier() const { return mInterpolationQualifier; } TQualifier getQualifier() const { return mInterpolationQualifier; }
unsigned int getRank() const;
private: private:
TQualifier mInterpolationQualifier; TQualifier mInterpolationQualifier;
...@@ -76,6 +87,8 @@ class TLayoutQualifierWrapper final : public TQualifierWrapperBase ...@@ -76,6 +87,8 @@ class TLayoutQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtLayout; } TQualifierType getType() const { return QtLayout; }
TString getQualifierString() const { return "layout"; } TString getQualifierString() const { return "layout"; }
const TLayoutQualifier &getQualifier() const { return mLayoutQualifier; } const TLayoutQualifier &getQualifier() const { return mLayoutQualifier; }
unsigned int getRank() const;
private: private:
TLayoutQualifier mLayoutQualifier; TLayoutQualifier mLayoutQualifier;
}; };
...@@ -92,6 +105,8 @@ class TStorageQualifierWrapper final : public TQualifierWrapperBase ...@@ -92,6 +105,8 @@ class TStorageQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtStorage; } TQualifierType getType() const { return QtStorage; }
TString getQualifierString() const { return ::getQualifierString(mStorageQualifier); } TString getQualifierString() const { return ::getQualifierString(mStorageQualifier); }
TQualifier getQualifier() const { return mStorageQualifier; } TQualifier getQualifier() const { return mStorageQualifier; }
unsigned int getRank() const;
private: private:
TQualifier mStorageQualifier; TQualifier mStorageQualifier;
}; };
...@@ -108,6 +123,8 @@ class TPrecisionQualifierWrapper final : public TQualifierWrapperBase ...@@ -108,6 +123,8 @@ class TPrecisionQualifierWrapper final : public TQualifierWrapperBase
TQualifierType getType() const { return QtPrecision; } TQualifierType getType() const { return QtPrecision; }
TString getQualifierString() const { return ::getPrecisionString(mPrecisionQualifier); } TString getQualifierString() const { return ::getPrecisionString(mPrecisionQualifier); }
TPrecision getQualifier() const { return mPrecisionQualifier; } TPrecision getQualifier() const { return mPrecisionQualifier; }
unsigned int getRank() const;
private: private:
TPrecision mPrecisionQualifier; TPrecision mPrecisionQualifier;
}; };
...@@ -130,28 +147,26 @@ struct TTypeQualifier ...@@ -130,28 +147,26 @@ struct TTypeQualifier
class TTypeQualifierBuilder : angle::NonCopyable class TTypeQualifierBuilder : angle::NonCopyable
{ {
public: public:
using QualifierSequence = std::vector<const TQualifierWrapperBase *>;
public:
TTypeQualifierBuilder(const TStorageQualifierWrapper *scope); TTypeQualifierBuilder(const TStorageQualifierWrapper *scope);
// Adds the passed qualifier to the end of the sequence. // Adds the passed qualifier to the end of the sequence.
void appendQualifier(const TQualifierWrapperBase *qualifier); void appendQualifier(const TQualifierWrapperBase *qualifier);
// Checks for the order of qualification and repeating qualifiers. // 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 // Goes over the qualifier sequence and parses it to form a type qualifier for a function
// parameter. // parameter.
// The returned object is initialized even if the parsing fails. // 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. // 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. // The returned object is initialized even if the parsing fails.
TTypeQualifier getVariableTypeQualifier(TDiagnostics *diagnostics) const; TTypeQualifier getVariableTypeQualifier(TDiagnostics *diagnostics,
bool areQualifierChecksRelaxed) const;
private: private:
// Handles the joining of storage qualifiers for a parameter in a function. QualifierSequence mQualifiers;
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;
}; };
#endif // COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_ #endif // COMPILER_TRANSLATOR_QUALIFIER_TYPES_H_
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
'<(angle_path)/src/tests/compiler_tests/Pack_Unpack_test.cpp', '<(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/PruneEmptyDeclarations_test.cpp',
'<(angle_path)/src/tests/compiler_tests/PruneUnusedFunctions_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/QualificationOrder_test.cpp',
'<(angle_path)/src/tests/compiler_tests/RecordConstantPrecision_test.cpp', '<(angle_path)/src/tests/compiler_tests/RecordConstantPrecision_test.cpp',
'<(angle_path)/src/tests/compiler_tests/RemovePow_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 @@ ...@@ -11,8 +11,10 @@
#include <map> #include <map>
#include "angle_gl.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "angle_gl.h"
#include "compiler/translator/TranslatorESSL.h"
#include "GLSLANG/ShaderLang.h" #include "GLSLANG/ShaderLang.h"
bool compileTestShader(GLenum type, bool compileTestShader(GLenum type,
...@@ -85,4 +87,32 @@ class MatchOutputCodeTest : public testing::Test ...@@ -85,4 +87,32 @@ class MatchOutputCodeTest : public testing::Test
std::map<ShShaderOutput, std::string> mOutputCode; 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_ #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