ES31: Enable 'location' layout qualifier on shader interfaces in compiler
This patch enables 'location' layout qualifier for vertex outputs and
fragment shader inputs when the shader version is 3.1 in ANGLE GLSL
compiler and adds the check on location conflicts for these varyings.
According to GLSL ES 3.1 SPEC (Chapter 4.4.1 and Chapter 4.4.2),
'location' layout qualifier is allowed on both inputs and outputs of
vertex and fragment shaders.
'location' layout qualifier on shader interfaces is only valid on shaders
whose version is 3.1 and above. According to GLSL ES 3.0 SPEC, vertex shader
cannot have output layout qualifiers (Chapter 4.3.8.2) and fragment shader
cannot have input layout qualifiers (Chapter 4.3.8.1).
The 'location' qualifier on varyings is used in the shader interface
matching defined in OpenGL ES 3.1. (OpenGL ES 3.1 SPEC Chapter 7.4.1). This
new link rule will be added to Program.cpp in another patch.
For the OpenGL ES 3.1 extension GL_OES_geometry_shader, according to
GL_OES_shader_io_blocks SPEC (Chapter 4.4.1 and Chapter 4.4.2), 'location'
layout qualifier is both valid on geometry shader inputs and outputs. This
feature will be implemented together with other rules on geometry shader
inputs and outputs.
BUG=angleproject:2144
TEST=angle_unittests
Change-Id: I62d85f7144c177448321c2db36ed7aaeaa1fb205
Reviewed-on: https://chromium-review.googlesource.com/645366
Commit-Queue: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
index e846d95..5eccbf2 100644
--- a/src/compiler/translator/CollectVariables.cpp
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -609,6 +609,7 @@
Varying varying;
setCommonVariableProperties(type, variable.getName(), &varying);
+ varying.location = type.getLayoutQualifier().location;
switch (type.getQualifier())
{
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 424cf68..99f46b2 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -40,6 +40,7 @@
#include "compiler/translator/ValidateLimitations.h"
#include "compiler/translator/ValidateMaxParameters.h"
#include "compiler/translator/ValidateOutputs.h"
+#include "compiler/translator/ValidateVaryingLocations.h"
#include "compiler/translator/VariablePacker.h"
#include "third_party/compiler/ArrayBoundsClamper.h"
@@ -396,6 +397,11 @@
if (success)
PruneEmptyDeclarations(root);
+ if (success && shaderVersion >= 310)
+ {
+ success = ValidateVaryingLocations(root, &mDiagnostics);
+ }
+
if (success && shaderVersion >= 300 && shaderType == GL_FRAGMENT_SHADER)
{
success = ValidateOutputs(root, getExtensionBehavior(), compileResources.MaxDrawBuffers,
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 4b66306..95dc947 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -63,7 +63,8 @@
const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier();
- if ((type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn) &&
+ if ((type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
+ IsVarying(type.getQualifier())) &&
layoutQualifier.location >= 0)
{
return true;
@@ -206,7 +207,8 @@
CommaSeparatedListItemPrefixGenerator listItemPrefix;
- if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn)
+ if (type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn ||
+ IsVarying(type.getQualifier()))
{
if (layoutQualifier.location >= 0)
{
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 1c6160f..efa9021 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -901,7 +901,7 @@
if (mShaderVersion >= 310)
{
errorMsg =
- "invalid layout qualifier: only valid on program inputs, outputs, and uniforms";
+ "invalid layout qualifier: only valid on shader inputs, outputs, and uniforms";
}
error(location, errorMsg, "location");
}
@@ -1221,9 +1221,9 @@
}
bool canHaveLocation = qualifier == EvqVertexIn || qualifier == EvqFragmentOut;
- if (mShaderVersion >= 310 && qualifier == EvqUniform)
+ if (mShaderVersion >= 310)
{
- canHaveLocation = true;
+ canHaveLocation = canHaveLocation || qualifier == EvqUniform || IsVarying(qualifier);
// We're not checking whether the uniform location is in range here since that depends on
// the type of the variable.
// The type can only be fully determined for non-empty declarations.
diff --git a/src/compiler/translator/ShaderVars.cpp b/src/compiler/translator/ShaderVars.cpp
index ccfb8dd..7a6e181 100644
--- a/src/compiler/translator/ShaderVars.cpp
+++ b/src/compiler/translator/ShaderVars.cpp
@@ -156,22 +156,27 @@
}
bool ShaderVariable::isSameVariableAtLinkTime(const ShaderVariable &other,
- bool matchPrecision) const
+ bool matchPrecision,
+ bool matchName) const
{
if (type != other.type)
return false;
if (matchPrecision && precision != other.precision)
return false;
- if (name != other.name)
+ if (matchName && name != other.name)
return false;
- ASSERT(mappedName == other.mappedName);
+ ASSERT(!matchName || mappedName == other.mappedName);
if (arraySize != other.arraySize)
return false;
if (fields.size() != other.fields.size())
return false;
+
+ // [OpenGL ES 3.1 SPEC Chapter 7.4.1]
+ // Variables declared as structures are considered to match in type if and only if structure
+ // members match in name, type, qualification, and declaration order.
for (size_t ii = 0; ii < fields.size(); ++ii)
{
- if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii], matchPrecision))
+ if (!fields[ii].isSameVariableAtLinkTime(other.fields[ii], matchPrecision, true))
{
return false;
}
@@ -224,7 +229,7 @@
{
return false;
}
- return VariableWithLocation::isSameVariableAtLinkTime(other, true);
+ return VariableWithLocation::isSameVariableAtLinkTime(other, true, true);
}
VariableWithLocation::VariableWithLocation() : location(-1)
@@ -326,7 +331,7 @@
bool InterfaceBlockField::isSameInterfaceBlockFieldAtLinkTime(
const InterfaceBlockField &other) const
{
- return (ShaderVariable::isSameVariableAtLinkTime(other, true) &&
+ return (ShaderVariable::isSameVariableAtLinkTime(other, true, true) &&
isRowMajorLayout == other.isRowMajorLayout);
}
@@ -339,13 +344,15 @@
}
Varying::Varying(const Varying &other)
- : ShaderVariable(other), interpolation(other.interpolation), isInvariant(other.isInvariant)
+ : VariableWithLocation(other),
+ interpolation(other.interpolation),
+ isInvariant(other.isInvariant)
{
}
Varying &Varying::operator=(const Varying &other)
{
- ShaderVariable::operator=(other);
+ VariableWithLocation::operator=(other);
interpolation = other.interpolation;
isInvariant = other.isInvariant;
return *this;
@@ -353,7 +360,7 @@
bool Varying::operator==(const Varying &other) const
{
- return (ShaderVariable::operator==(other) && interpolation == other.interpolation &&
+ return (VariableWithLocation::operator==(other) && interpolation == other.interpolation &&
isInvariant == other.isInvariant);
}
@@ -364,9 +371,11 @@
bool Varying::isSameVaryingAtLinkTime(const Varying &other, int shaderVersion) const
{
- return (ShaderVariable::isSameVariableAtLinkTime(other, false) &&
+ return (ShaderVariable::isSameVariableAtLinkTime(other, false, false) &&
InterpolationTypesMatch(interpolation, other.interpolation) &&
- (shaderVersion >= 300 || isInvariant == other.isInvariant));
+ (shaderVersion >= 300 || isInvariant == other.isInvariant) &&
+ (location == other.location) &&
+ (name == other.name || (shaderVersion >= 310 && location >= 0)));
}
InterfaceBlock::InterfaceBlock()
diff --git a/src/compiler/translator/ValidateVaryingLocations.cpp b/src/compiler/translator/ValidateVaryingLocations.cpp
new file mode 100644
index 0000000..f76d408
--- /dev/null
+++ b/src/compiler/translator/ValidateVaryingLocations.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright (c) 2002-2017 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.
+//
+// The ValidateVaryingLocations function checks if there exists location conflicts on shader
+// varyings.
+//
+
+#include "ValidateVaryingLocations.h"
+
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/util.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
+{
+ diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str());
+}
+
+int GetLocationCount(const TIntermSymbol *varying)
+{
+ const auto &varyingType = varying->getType();
+ if (varyingType.getStruct() != nullptr)
+ {
+ ASSERT(!varyingType.isArray());
+ int totalLocation = 0;
+ for (const auto *field : varyingType.getStruct()->fields())
+ {
+ const auto *fieldType = field->type();
+ ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray());
+
+ totalLocation += fieldType->getSecondarySize();
+ }
+ return totalLocation;
+ }
+ else
+ {
+ return varyingType.getSecondarySize() * static_cast<int>(varyingType.getArraySizeProduct());
+ }
+}
+
+using VaryingVector = std::vector<const TIntermSymbol *>;
+
+void ValidateShaderInterface(TDiagnostics *diagnostics, VaryingVector &varyingVector)
+{
+ // Location conflicts can only happen when there are two or more varyings in varyingVector.
+ if (varyingVector.size() <= 1)
+ {
+ return;
+ }
+
+ std::map<int, const TIntermSymbol *> locationMap;
+ for (const TIntermSymbol *varying : varyingVector)
+ {
+ const int location = varying->getType().getLayoutQualifier().location;
+ ASSERT(location >= 0);
+
+ const int elementCount = GetLocationCount(varying);
+ for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
+ {
+ const int offsetLocation = location + elementIndex;
+ if (locationMap.find(offsetLocation) != locationMap.end())
+ {
+ std::stringstream strstr;
+ strstr << "'" << varying->getSymbol()
+ << "' conflicting location with previously defined '"
+ << locationMap[offsetLocation]->getSymbol() << "'";
+ error(*varying, strstr.str().c_str(), diagnostics);
+ }
+ else
+ {
+ locationMap[offsetLocation] = varying;
+ }
+ }
+ }
+}
+
+class ValidateVaryingLocationsTraverser : public TIntermTraverser
+{
+ public:
+ ValidateVaryingLocationsTraverser();
+ void validate(TDiagnostics *diagnostics);
+
+ private:
+ bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
+ bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
+
+ VaryingVector mInputVaryingsWithLocation;
+ VaryingVector mOutputVaryingsWithLocation;
+};
+
+ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser()
+ : TIntermTraverser(true, false, false)
+{
+}
+
+bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+ const TIntermSequence &sequence = *(node->getSequence());
+ ASSERT(!sequence.empty());
+
+ const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
+ if (symbol == nullptr)
+ {
+ return false;
+ }
+
+ // Collect varyings that have explicit 'location' qualifiers.
+ const TQualifier qualifier = symbol->getQualifier();
+ if (symbol->getType().getLayoutQualifier().location != -1)
+ {
+ if (IsVaryingIn(qualifier))
+ {
+ mInputVaryingsWithLocation.push_back(symbol);
+ }
+ else if (IsVaryingOut(qualifier))
+ {
+ mOutputVaryingsWithLocation.push_back(symbol);
+ }
+ }
+
+ return false;
+}
+
+bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit,
+ TIntermFunctionDefinition *node)
+{
+ // We stop traversing function definitions because varyings cannot be defined in a function.
+ return false;
+}
+
+void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
+{
+ ASSERT(diagnostics);
+
+ ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation);
+ ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation);
+}
+
+} // anonymous namespace
+
+bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics)
+{
+ ValidateVaryingLocationsTraverser varyingValidator;
+ root->traverse(&varyingValidator);
+ int numErrorsBefore = diagnostics->numErrors();
+ varyingValidator.validate(diagnostics);
+ return (diagnostics->numErrors() == numErrorsBefore);
+}
+
+} // namespace sh
\ No newline at end of file
diff --git a/src/compiler/translator/ValidateVaryingLocations.h b/src/compiler/translator/ValidateVaryingLocations.h
new file mode 100644
index 0000000..a4dbab1
--- /dev/null
+++ b/src/compiler/translator/ValidateVaryingLocations.h
@@ -0,0 +1,23 @@
+//
+// Copyright (c) 2002-2017 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.
+//
+// The ValidateVaryingLocations function checks if there exists location conflicts on shader
+// varyings.
+//
+
+#ifndef COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_
+#define COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TDiagnostics;
+
+bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics);
+
+} // namespace sh
+
+#endif
\ No newline at end of file