Parse binding layout qualifier for opaque types
This patch adds binding layout qualifier support for opaque types.
Binding layout qualifier on blocks is not yet supported.
This includes support for GLSL output and some minor simplification of
related functionality in ParseContext.
TEST=angle_unittests, dEQP-GLES31.functional.layout_binding.*
BUG=angleproject:1442
Change-Id: I53fb505b5a539bccee70613f3969fba81965ae84
Reviewed-on: https://chromium-review.googlesource.com/441586
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index e4aa5a1..258af09 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -128,6 +128,8 @@
mComputeShaderLocalSizeDeclared(false),
mNumViews(-1),
mMaxNumViews(resources.MaxViewsOVR),
+ mMaxImageUnits(resources.MaxImageUnits),
+ mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
mDeclaringFunction(false)
{
mComputeShaderLocalSize.fill(-1);
@@ -1028,6 +1030,8 @@
{
ASSERT((*variable) == nullptr);
+ checkBindingIsValid(line, type);
+
bool needsReservedCheck = true;
// gl_LastFragData may be redeclared with a new precision qualifier
@@ -1081,7 +1085,7 @@
if (!IsImage(type->getBasicType()))
{
- checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, line);
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, line);
}
else
{
@@ -1270,17 +1274,28 @@
}
else
{
+ checkInternalFormatIsNotSpecified(identifierLocation, layoutQualifier.imageInternalFormat);
- if (!checkInternalFormatIsNotSpecified(identifierLocation,
- layoutQualifier.imageInternalFormat))
- {
- return;
- }
+ checkMemoryQualifierIsNotSpecified(publicType.memoryQualifier, identifierLocation);
+ }
+}
- if (!checkIsMemoryQualifierNotSpecified(publicType.memoryQualifier, identifierLocation))
- {
- return;
- }
+void TParseContext::checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type)
+{
+ TLayoutQualifier layoutQualifier = type.getLayoutQualifier();
+ int arraySize = type.isArray() ? type.getArraySize() : 1;
+ if (IsImage(type.getBasicType()))
+ {
+ checkImageBindingIsValid(identifierLocation, layoutQualifier.binding, arraySize);
+ }
+ else if (IsSampler(type.getBasicType()))
+ {
+ checkSamplerBindingIsValid(identifierLocation, layoutQualifier.binding, arraySize);
+ }
+ else
+ {
+ ASSERT(!IsOpaqueType(type.getBasicType()));
+ checkBindingIsNotSpecified(identifierLocation, layoutQualifier.binding);
}
}
@@ -1314,16 +1329,44 @@
return true;
}
-bool TParseContext::checkInternalFormatIsNotSpecified(const TSourceLoc &location,
+void TParseContext::checkInternalFormatIsNotSpecified(const TSourceLoc &location,
TLayoutImageInternalFormat internalFormat)
{
if (internalFormat != EiifUnspecified)
{
error(location, "invalid layout qualifier: only valid when used with images",
getImageInternalFormatString(internalFormat));
- return false;
}
- return true;
+}
+
+void TParseContext::checkBindingIsNotSpecified(const TSourceLoc &location, int binding)
+{
+ if (binding != -1)
+ {
+ error(location,
+ "invalid layout qualifier: only valid when used with opaque types or blocks",
+ "binding");
+ }
+}
+
+void TParseContext::checkImageBindingIsValid(const TSourceLoc &location, int binding, int arraySize)
+{
+ // Expects arraySize to be 1 when setting binding for only a single variable.
+ if (binding >= 0 && binding + arraySize > mMaxImageUnits)
+ {
+ error(location, "image binding greater than gl_MaxImageUnits", "binding");
+ }
+}
+
+void TParseContext::checkSamplerBindingIsValid(const TSourceLoc &location,
+ int binding,
+ int arraySize)
+{
+ // Expects arraySize to be 1 when setting binding for only a single variable.
+ if (binding >= 0 && binding + arraySize > mMaxCombinedTextureImageUnits)
+ {
+ error(location, "sampler binding greater than maximum texture units", "binding");
+ }
}
void TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
@@ -1845,35 +1888,29 @@
}
}
-bool TParseContext::checkIsMemoryQualifierNotSpecified(const TMemoryQualifier &memoryQualifier,
+void TParseContext::checkMemoryQualifierIsNotSpecified(const TMemoryQualifier &memoryQualifier,
const TSourceLoc &location)
{
if (memoryQualifier.readonly)
{
error(location, "Only allowed with images.", "readonly");
- return false;
}
if (memoryQualifier.writeonly)
{
error(location, "Only allowed with images.", "writeonly");
- return false;
}
if (memoryQualifier.coherent)
{
error(location, "Only allowed with images.", "coherent");
- return false;
}
if (memoryQualifier.restrictQualifier)
{
error(location, "Only allowed with images.", "restrict");
- return false;
}
if (memoryQualifier.volatileQualifier)
{
error(location, "Only allowed with images.", "volatile");
- return false;
}
- return true;
}
TIntermDeclaration *TParseContext::parseSingleDeclaration(
@@ -2092,7 +2129,7 @@
checkInvariantVariableQualifier(typeQualifier.invariant, type.getQualifier(),
typeQualifier.line);
- checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
symbolTable.addInvariantVarying(std::string(identifier->c_str()));
@@ -2120,10 +2157,10 @@
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &publicType);
TVariable *variable = nullptr;
- declareVariable(identifierLocation, identifier, TType(publicType), &variable);
+ TType type(publicType);
+ declareVariable(identifierLocation, identifier, type, &variable);
- TIntermSymbol *symbol =
- intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation);
+ TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, type, identifierLocation);
if (variable && symbol)
{
symbol->setId(variable->getUniqueId());
@@ -2260,11 +2297,13 @@
if (!layoutQualifier.isCombinationValid())
{
- error(typeQualifier.line, "invalid combination:", "layout");
+ error(typeQualifier.line, "invalid layout qualifier combination", "layout");
return;
}
- checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+ checkBindingIsNotSpecified(typeQualifier.line, layoutQualifier.binding);
+
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
checkInternalFormatIsNotSpecified(typeQualifier.line, layoutQualifier.imageInternalFormat);
@@ -2741,7 +2780,10 @@
error(typeQualifier.line, "invalid qualifier on interface block member", "invariant");
}
- checkIsMemoryQualifierNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+ checkMemoryQualifierIsNotSpecified(typeQualifier.memoryQualifier, typeQualifier.line);
+
+ // TODO(oetuaho): Remove this and support binding for blocks.
+ checkBindingIsNotSpecified(typeQualifier.line, typeQualifier.layoutQualifier.binding);
TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier);
@@ -3359,6 +3401,19 @@
qualifier.locationsSpecified = 1;
}
}
+ else if (qualifierType == "binding")
+ {
+ checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
+ if (intValue < 0)
+ {
+ error(intValueLine, "out of range: binding must be non-negative",
+ intValueString.c_str());
+ }
+ else
+ {
+ qualifier.binding = intValue;
+ }
+ }
else if (qualifierType == "local_size_x")
{
parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
@@ -3528,7 +3583,9 @@
error(field.line(), "disallowed type in struct", field.type()->getBasicString());
}
- checkIsMemoryQualifierNotSpecified(field.type()->getMemoryQualifier(), field.line());
+ checkMemoryQualifierIsNotSpecified(field.type()->getMemoryQualifier(), field.line());
+
+ checkBindingIsNotSpecified(field.line(), field.type()->getLayoutQualifier().binding);
checkLocationIsNotSpecified(field.line(), field.type()->getLayoutQualifier());
}