Add GLSL support for runtime-sized arrays in SSBOs
The GLSL parser now allows a runtime-sized array as the last member in
a shader storage block. Clamping indexing against the memory bounds is
done by determining the array length at runtime.
Runtime-sized arrays are used in dEQP tests for many compute shader
tests, so these now work on the OpenGL backend.
BUG=angleproject:1951
TEST=angle_unittests,
dEQP-GLES31.functional.shaders.linkage.shader_storage_block.*
dEQP-GLES31.functional.shaders.builtin_functions.*compute*
Change-Id: Ibecca24623ca8e4723af6f0e0421fe9711ea828d
Reviewed-on: https://chromium-review.googlesource.com/787976
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 7417ab7..67207ca 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -3785,6 +3785,15 @@
fieldType->setLayoutQualifier(fieldLayoutQualifier);
+ if (mShaderVersion < 310 || memberIndex != fieldList->size() - 1u ||
+ typeQualifier.qualifier != EvqBuffer)
+ {
+ // ESSL 3.10 spec section 4.1.9 allows for runtime-sized arrays.
+ checkIsNotUnsizedArray(field->line(),
+ "array members of interface blocks must specify a size",
+ field->name().c_str(), field->type());
+ }
+
if (typeQualifier.qualifier == EvqBuffer)
{
// set memory qualifiers
@@ -4007,85 +4016,88 @@
int safeIndex = -1;
- if (baseExpression->isArray())
+ if (index < 0)
{
- if (baseExpression->getQualifier() == EvqFragData && index > 0)
+ outOfRangeError(outOfRangeIndexIsError, location, "index expression is negative", "[]");
+ safeIndex = 0;
+ }
+
+ if (!baseExpression->getType().isUnsizedArray())
+ {
+ if (baseExpression->isArray())
{
- if (!isExtensionEnabled(TExtension::EXT_draw_buffers))
+ if (baseExpression->getQualifier() == EvqFragData && index > 0)
{
- outOfRangeError(outOfRangeIndexIsError, location,
- "array index for gl_FragData must be zero when "
- "GL_EXT_draw_buffers is disabled",
- "[");
- safeIndex = 0;
+ if (!isExtensionEnabled(TExtension::EXT_draw_buffers))
+ {
+ outOfRangeError(outOfRangeIndexIsError, location,
+ "array index for gl_FragData must be zero when "
+ "GL_EXT_draw_buffers is disabled",
+ "[]");
+ safeIndex = 0;
+ }
+ }
+ // Only do generic out-of-range check if similar error hasn't already been reported.
+ if (safeIndex < 0)
+ {
+ safeIndex = checkIndexLessThan(outOfRangeIndexIsError, location, index,
+ baseExpression->getOutermostArraySize(),
+ "array index out of range");
}
}
- // Only do generic out-of-range check if similar error hasn't already been reported.
- if (safeIndex < 0)
+ else if (baseExpression->isMatrix())
{
- safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
- baseExpression->getOutermostArraySize(),
- "array index out of range");
+ safeIndex = checkIndexLessThan(outOfRangeIndexIsError, location, index,
+ baseExpression->getType().getCols(),
+ "matrix field selection out of range");
}
- }
- else if (baseExpression->isMatrix())
- {
- safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
- baseExpression->getType().getCols(),
- "matrix field selection out of range");
- }
- else if (baseExpression->isVector())
- {
- safeIndex = checkIndexOutOfRange(outOfRangeIndexIsError, location, index,
- baseExpression->getType().getNominalSize(),
- "vector field selection out of range");
- }
+ else if (baseExpression->isVector())
+ {
+ safeIndex = checkIndexLessThan(outOfRangeIndexIsError, location, index,
+ baseExpression->getType().getNominalSize(),
+ "vector field selection out of range");
+ }
- ASSERT(safeIndex >= 0);
- // Data of constant unions can't be changed, because it may be shared with other
- // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new
- // sanitized object.
- if (safeIndex != index || indexConstantUnion->getBasicType() != EbtInt)
- {
- TConstantUnion *safeConstantUnion = new TConstantUnion();
- safeConstantUnion->setIConst(safeIndex);
- indexConstantUnion->replaceConstantUnion(safeConstantUnion);
- indexConstantUnion->getTypePointer()->setBasicType(EbtInt);
- }
+ ASSERT(safeIndex >= 0);
+ // Data of constant unions can't be changed, because it may be shared with other
+ // constant unions or even builtins, like gl_MaxDrawBuffers. Instead use a new
+ // sanitized object.
+ if (safeIndex != index || indexConstantUnion->getBasicType() != EbtInt)
+ {
+ TConstantUnion *safeConstantUnion = new TConstantUnion();
+ safeConstantUnion->setIConst(safeIndex);
+ indexConstantUnion->replaceConstantUnion(safeConstantUnion);
+ indexConstantUnion->getTypePointer()->setBasicType(EbtInt);
+ }
- TIntermBinary *node = new TIntermBinary(EOpIndexDirect, baseExpression, indexExpression);
- node->setLine(location);
- return node->fold(mDiagnostics);
+ TIntermBinary *node =
+ new TIntermBinary(EOpIndexDirect, baseExpression, indexExpression);
+ node->setLine(location);
+ return node->fold(mDiagnostics);
+ }
}
- else
- {
- TIntermBinary *node = new TIntermBinary(EOpIndexIndirect, baseExpression, indexExpression);
- node->setLine(location);
- // Indirect indexing can never be constant folded.
- return node;
- }
+
+ TIntermBinary *node = new TIntermBinary(EOpIndexIndirect, baseExpression, indexExpression);
+ node->setLine(location);
+ // Indirect indexing can never be constant folded.
+ return node;
}
-int TParseContext::checkIndexOutOfRange(bool outOfRangeIndexIsError,
- const TSourceLoc &location,
- int index,
- int arraySize,
- const char *reason)
+int TParseContext::checkIndexLessThan(bool outOfRangeIndexIsError,
+ const TSourceLoc &location,
+ int index,
+ int arraySize,
+ const char *reason)
{
- if (index >= arraySize || index < 0)
+ // Should not reach here with an unsized / runtime-sized array.
+ ASSERT(arraySize > 0);
+ if (index >= arraySize)
{
std::stringstream reasonStream;
reasonStream << reason << " '" << index << "'";
std::string token = reasonStream.str();
outOfRangeError(outOfRangeIndexIsError, location, reason, "[]");
- if (index < 0)
- {
- return 0;
- }
- else
- {
- return arraySize - 1;
- }
+ return arraySize - 1;
}
return index;
}
@@ -4759,9 +4771,6 @@
{
type->makeArray(arraySize);
}
- checkIsNotUnsizedArray(typeSpecifier.getLine(),
- "array members of structs must specify a size",
- declarator->name().c_str(), type);
checkIsBelowStructNestingLimit(typeSpecifier.getLine(), *declarator);
}
@@ -4792,7 +4801,7 @@
// ensure we do not specify any storage qualifiers on the struct members
for (unsigned int typeListIndex = 0; typeListIndex < fieldList->size(); typeListIndex++)
{
- const TField &field = *(*fieldList)[typeListIndex];
+ TField &field = *(*fieldList)[typeListIndex];
const TQualifier qualifier = field.type()->getQualifier();
switch (qualifier)
{
@@ -4814,6 +4823,9 @@
error(field.line(), "disallowed type in struct", field.type()->getBasicString());
}
+ checkIsNotUnsizedArray(field.line(), "array members of structs must specify a size",
+ field.name().c_str(), field.type());
+
checkMemoryQualifierIsNotSpecified(field.type()->getMemoryQualifier(), field.line());
checkBindingIsNotSpecified(field.line(), field.type()->getLayoutQualifier().binding);