Implement location layout qualifier for uniforms
This is a complete implementation of the uniform location layout
qualifier. Uniform location set in the shader is plumbed to shader
linking, which does several link-time checks for conflicts and
recursively applies the location to struct members.
Validate that location is consistent as specified in the table in
section 9.2.1 of the ESSL 3.10.4 spec. The location set in the shader
overrides the one set via the CHROMIUM_bind_uniform_location API.
Location conflicts must be checked even if the uniforms are not
statically used. Because of this unused uniforms are now recorded
during uniform linking. After linking checks are done, unused uniforms
are pruned from the program state.
Location is validated against the maximum number of uniform locations
at compile time as specified in section 4.4.3 of the ESSL 3.10.4 spec.
All dEQP uniform location tests don't yet pass due to unrelated bugs.
BUG=angleproject:1442
TEST=angle_end2end_tests, dEQP-GLES31.functional.uniform_location.*
Change-Id: I1f968e971f521fbc804b01e1a7c2b4d14f24d20f
Reviewed-on: https://chromium-review.googlesource.com/447942
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 258af09..47325d4 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -130,6 +130,7 @@
mMaxNumViews(resources.MaxViewsOVR),
mMaxImageUnits(resources.MaxImageUnits),
mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
+ mMaxUniformLocations(resources.MaxUniformLocations),
mDeclaringFunction(false)
{
mComputeShaderLocalSize.fill(-1);
@@ -845,8 +846,13 @@
{
if (layoutQualifier.location != -1)
{
- error(location, "invalid layout qualifier: only valid on program inputs and outputs",
- "location");
+ const char *errorMsg = "invalid layout qualifier: only valid on program inputs and outputs";
+ if (mShaderVersion >= 310)
+ {
+ errorMsg =
+ "invalid layout qualifier: only valid on program inputs, outputs, and uniforms";
+ }
+ error(location, errorMsg, "location");
}
}
@@ -1199,7 +1205,23 @@
return;
}
- if (publicType.qualifier != EvqVertexIn && publicType.qualifier != EvqFragmentOut)
+ bool canHaveLocation =
+ publicType.qualifier == EvqVertexIn || publicType.qualifier == EvqFragmentOut;
+
+ if (mShaderVersion >= 310 && publicType.qualifier == EvqUniform)
+ {
+ canHaveLocation = true;
+
+ // Valid uniform declarations can't be unsized arrays since uniforms can't be initialized.
+ // But invalid shaders may still reach here with an unsized array declaration.
+ if (!publicType.isUnsizedArray())
+ {
+ TType type(publicType);
+ checkUniformLocationInRange(identifierLocation, type.getLocationCount(),
+ publicType.layoutQualifier);
+ }
+ }
+ if (!canHaveLocation)
{
checkLocationIsNotSpecified(identifierLocation, publicType.layoutQualifier);
}
@@ -1369,6 +1391,17 @@
}
}
+void TParseContext::checkUniformLocationInRange(const TSourceLoc &location,
+ int objectLocationCount,
+ const TLayoutQualifier &layoutQualifier)
+{
+ int loc = layoutQualifier.location;
+ if (loc >= 0 && loc + objectLocationCount > mMaxUniformLocations)
+ {
+ error(location, "Uniform location out of range", "location");
+ }
+}
+
void TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
TIntermAggregate *fnCall)
{
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 3a70e86..6283d30 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -395,6 +395,10 @@
void checkImageBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkSamplerBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
+ void checkUniformLocationInRange(const TSourceLoc &location,
+ int objectLocationCount,
+ const TLayoutQualifier &layoutQualifier);
+
TIntermTyped *addBinaryMathInternal(TOperator op,
TIntermTyped *left,
TIntermTyped *right,
@@ -469,6 +473,7 @@
int mMaxNumViews;
int mMaxImageUnits;
int mMaxCombinedTextureImageUnits;
+ int mMaxUniformLocations;
// keeps track whether we are declaring / defining a function
bool mDeclaringFunction;
};
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index 415369e..fe7c71d 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -196,6 +196,8 @@
resources->MaxComputeImageUniforms = 4;
resources->MaxCombinedImageUniforms = 4;
+ resources->MaxUniformLocations = 1024;
+
resources->MaxCombinedShaderOutputResources = 4;
resources->MaxComputeWorkGroupCount[0] = 65535;
diff --git a/src/compiler/translator/ShaderVars.cpp b/src/compiler/translator/ShaderVars.cpp
index a6c885d..84a4e3d 100644
--- a/src/compiler/translator/ShaderVars.cpp
+++ b/src/compiler/translator/ShaderVars.cpp
@@ -189,20 +189,20 @@
{
}
-Uniform::Uniform(const Uniform &other) : ShaderVariable(other), binding(other.binding)
+Uniform::Uniform(const Uniform &other) : VariableWithLocation(other), binding(other.binding)
{
}
Uniform &Uniform::operator=(const Uniform &other)
{
- ShaderVariable::operator=(other);
+ VariableWithLocation::operator=(other);
binding = other.binding;
return *this;
}
bool Uniform::operator==(const Uniform &other) const
{
- return ShaderVariable::operator==(other) && binding == other.binding;
+ return VariableWithLocation::operator==(other) && binding == other.binding;
}
bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
@@ -211,30 +211,34 @@
{
return false;
}
- return ShaderVariable::isSameVariableAtLinkTime(other, true);
+ if (location != -1 && other.location != -1 && location != other.location)
+ {
+ return false;
+ }
+ return VariableWithLocation::isSameVariableAtLinkTime(other, true);
}
-InterfaceVariable::InterfaceVariable() : location(-1)
+VariableWithLocation::VariableWithLocation() : location(-1)
{
}
-InterfaceVariable::~InterfaceVariable()
+VariableWithLocation::~VariableWithLocation()
{
}
-InterfaceVariable::InterfaceVariable(const InterfaceVariable &other)
+VariableWithLocation::VariableWithLocation(const VariableWithLocation &other)
: ShaderVariable(other), location(other.location)
{
}
-InterfaceVariable &InterfaceVariable::operator=(const InterfaceVariable &other)
+VariableWithLocation &VariableWithLocation::operator=(const VariableWithLocation &other)
{
ShaderVariable::operator=(other);
location = other.location;
return *this;
}
-bool InterfaceVariable::operator==(const InterfaceVariable &other) const
+bool VariableWithLocation::operator==(const VariableWithLocation &other) const
{
return (ShaderVariable::operator==(other) && location == other.location);
}
@@ -247,19 +251,19 @@
{
}
-Attribute::Attribute(const Attribute &other) : InterfaceVariable(other)
+Attribute::Attribute(const Attribute &other) : VariableWithLocation(other)
{
}
Attribute &Attribute::operator=(const Attribute &other)
{
- InterfaceVariable::operator=(other);
+ VariableWithLocation::operator=(other);
return *this;
}
bool Attribute::operator==(const Attribute &other) const
{
- return InterfaceVariable::operator==(other);
+ return VariableWithLocation::operator==(other);
}
OutputVariable::OutputVariable()
@@ -270,19 +274,19 @@
{
}
-OutputVariable::OutputVariable(const OutputVariable &other) : InterfaceVariable(other)
+OutputVariable::OutputVariable(const OutputVariable &other) : VariableWithLocation(other)
{
}
OutputVariable &OutputVariable::operator=(const OutputVariable &other)
{
- InterfaceVariable::operator=(other);
+ VariableWithLocation::operator=(other);
return *this;
}
bool OutputVariable::operator==(const OutputVariable &other) const
{
- return InterfaceVariable::operator==(other);
+ return VariableWithLocation::operator==(other);
}
InterfaceBlockField::InterfaceBlockField() : isRowMajorLayout(false)
diff --git a/src/compiler/translator/Types.cpp b/src/compiler/translator/Types.cpp
index 48818fb..c7982f3 100644
--- a/src/compiler/translator/Types.cpp
+++ b/src/compiler/translator/Types.cpp
@@ -445,6 +445,36 @@
return totalSize;
}
+int TType::getLocationCount() const
+{
+ int count = 1;
+
+ if (getBasicType() == EbtStruct)
+ {
+ count = structure->getLocationCount();
+ }
+
+ if (isArray())
+ {
+ if (count == 0)
+ {
+ return 0;
+ }
+
+ unsigned int currentArraySize = getArraySize();
+ if (currentArraySize > static_cast<unsigned int>(std::numeric_limits<int>::max() / count))
+ {
+ count = std::numeric_limits<int>::max();
+ }
+ else
+ {
+ count *= static_cast<int>(currentArraySize);
+ }
+ }
+
+ return count;
+}
+
TStructure::TStructure(const TString *name, TFieldList *fields)
: TFieldListCollection(name, fields),
mDeepestNesting(0),
@@ -583,9 +613,9 @@
size_t TFieldListCollection::calculateObjectSize() const
{
size_t size = 0;
- for (size_t i = 0; i < mFields->size(); ++i)
+ for (const TField *field : *mFields)
{
- size_t fieldSize = (*mFields)[i]->type()->getObjectSize();
+ size_t fieldSize = field->type()->getObjectSize();
if (fieldSize > INT_MAX - size)
size = INT_MAX;
else
@@ -594,6 +624,24 @@
return size;
}
+int TFieldListCollection::getLocationCount() const
+{
+ int count = 0;
+ for (const TField *field : *mFields)
+ {
+ int fieldCount = field->type()->getLocationCount();
+ if (fieldCount > std::numeric_limits<int>::max() - count)
+ {
+ count = std::numeric_limits<int>::max();
+ }
+ else
+ {
+ count += fieldCount;
+ }
+ }
+ return count;
+}
+
int TStructure::calculateDeepestNesting() const
{
int maxNesting = 0;
diff --git a/src/compiler/translator/Types.h b/src/compiler/translator/Types.h
index 676036c..530c840 100644
--- a/src/compiler/translator/Types.h
+++ b/src/compiler/translator/Types.h
@@ -62,7 +62,10 @@
if (mObjectSize == 0)
mObjectSize = calculateObjectSize();
return mObjectSize;
- };
+ }
+
+ // How many locations the field list consumes as a uniform.
+ int getLocationCount() const;
protected:
TFieldListCollection(const TString *name, TFieldList *fields)
@@ -332,6 +335,9 @@
// Full size of single instance of type
size_t getObjectSize() const;
+ // Get how many locations this type consumes as a uniform.
+ int getLocationCount() const;
+
bool isMatrix() const { return primarySize > 1 && secondarySize > 1; }
bool isNonSquareMatrix() const { return isMatrix() && primarySize != secondarySize; }
bool isArray() const { return array; }
diff --git a/src/compiler/translator/VariableInfo.cpp b/src/compiler/translator/VariableInfo.cpp
index b4f2eaa..a201b4c 100644
--- a/src/compiler/translator/VariableInfo.cpp
+++ b/src/compiler/translator/VariableInfo.cpp
@@ -570,6 +570,7 @@
Uniform uniform;
setCommonVariableProperties(variable.getType(), variable.getSymbol(), &uniform);
uniform.binding = variable.getType().getLayoutQualifier().binding;
+ uniform.location = variable.getType().getLayoutQualifier().location;
return uniform;
}