Fix attribute layout location linking.
When a vertex attribute has a GLSL layout location qualifier, it takes
precedence over the binding location provided through the
glBindAttribLocation API call.
OpenGL ES 3.0.5 spec:
"If an active attribute has a binding explicitly set within the shader
text and a different binding assigned by BindAttribLocation, the
assignment in the shader text is used."
Change-Id: If0bc0dc01a8ff6189703f2be26f1938fbff5f5ae
Reviewed-on: https://swiftshader-review.googlesource.com/20168
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/OpenGL/compiler/OutputASM.cpp b/src/OpenGL/compiler/OutputASM.cpp
index 9ed34b2..c18ecf7 100644
--- a/src/OpenGL/compiler/OutputASM.cpp
+++ b/src/OpenGL/compiler/OutputASM.cpp
@@ -400,12 +400,12 @@
registerIndex = 0;
}
- Attribute::Attribute(GLenum type, const std::string &name, int arraySize, int location, int registerIndex)
+ Attribute::Attribute(GLenum type, const std::string &name, int arraySize, int layoutLocation, int registerIndex)
{
this->type = type;
this->name = name;
this->arraySize = arraySize;
- this->location = location;
+ this->layoutLocation = layoutLocation;
this->registerIndex = registerIndex;
}
diff --git a/src/OpenGL/compiler/OutputASM.h b/src/OpenGL/compiler/OutputASM.h
index 2f90266..b037a67 100644
--- a/src/OpenGL/compiler/OutputASM.h
+++ b/src/OpenGL/compiler/OutputASM.h
@@ -144,12 +144,12 @@
struct Attribute
{
Attribute();
- Attribute(GLenum type, const std::string &name, int arraySize, int location, int registerIndex);
+ Attribute(GLenum type, const std::string &name, int arraySize, int layoutLocation, int registerIndex);
GLenum type;
std::string name;
int arraySize;
- int location;
+ int layoutLocation;
int registerIndex;
};
diff --git a/src/OpenGL/libGLESv2/Program.cpp b/src/OpenGL/libGLESv2/Program.cpp
index 77912cc..dc64813 100644
--- a/src/OpenGL/libGLESv2/Program.cpp
+++ b/src/OpenGL/libGLESv2/Program.cpp
@@ -277,19 +277,7 @@
GLint Program::getAttributeLocation(const char *name)
{
- if(name)
- {
- std::string strName(name);
- for(auto const &it : linkedAttribute)
- {
- if(it.name == strName)
- {
- return getAttributeBinding(it);
- }
- }
- }
-
- return -1;
+ return name ? getAttributeLocation(std::string(name)) : -1;
}
int Program::getAttributeStream(int attributeIndex)
@@ -1591,80 +1579,55 @@
// Determines the mapping between GL attributes and vertex stream usage indices
bool Program::linkAttributes()
{
+ static_assert(MAX_VERTEX_ATTRIBS <= 32, "attribute count exceeds bitfield count");
unsigned int usedLocations = 0;
- // Link attributes that have a binding location
+ // Link attributes that have a GLSL layout location qualifier
for(auto const &attribute : vertexShader->activeAttributes)
{
- int location = (attributeBinding.find(attribute.name) != attributeBinding.end()) ? attributeBinding[attribute.name] : -1;
-
- if(location != -1) // Set by glBindAttribLocation
+ if(attribute.layoutLocation != -1)
{
- int rows = VariableRegisterCount(attribute.type);
-
- if(rows + location > MAX_VERTEX_ATTRIBS)
+ if(!linkAttribute(attribute, attribute.layoutLocation, usedLocations))
{
- appendToInfoLog("Active attribute (%s) at location %d is too big to fit", attribute.name.c_str(), location);
return false;
}
-
- // In GLSL 3.00, attribute aliasing produces a link error
- // In GLSL 1.00, attribute aliasing is allowed
- if(vertexShader->getShaderVersion() >= 300)
- {
- for(auto const &it : linkedAttribute)
- {
- int itLocStart = getAttributeBinding(it);
- ASSERT(itLocStart >= 0);
- int itLocEnd = itLocStart + VariableRegisterCount(it.type);
- for(int i = 0; i < rows; i++)
- {
- int loc = location + i;
- if((loc >= itLocStart) && (loc < itLocEnd))
- {
- appendToInfoLog("Attribute '%s' aliases attribute '%s' at location %d", attribute.name.c_str(), it.name.c_str(), location);
- return false;
- }
- }
- }
- }
-
- linkedAttributeLocation[attribute.name] = location;
- linkedAttribute.push_back(attribute);
- for(int i = 0; i < rows; i++)
- {
- usedLocations |= 1 << (location + i);
- }
}
}
- // Link attributes that don't have a binding location
+ // Link attributes that have an API provided binding location but no GLSL layout location
for(auto const &attribute : vertexShader->activeAttributes)
{
- int location = (attributeBinding.find(attribute.name) != attributeBinding.end()) ? attributeBinding[attribute.name] : -1;
+ int bindingLocation = (attributeBinding.find(attribute.name) != attributeBinding.end()) ? attributeBinding[attribute.name] : -1;
- if(location == -1) // Not set by glBindAttribLocation
+ if(attribute.layoutLocation == -1 && bindingLocation != -1)
{
- int rows = VariableRegisterCount(attribute.type);
- int availableIndex = AllocateFirstFreeBits(&usedLocations, rows, MAX_VERTEX_ATTRIBS);
-
- if(availableIndex == -1 || availableIndex + rows > MAX_VERTEX_ATTRIBS)
+ if(!linkAttribute(attribute, bindingLocation, usedLocations))
{
- appendToInfoLog("Too many active attributes (%s)", attribute.name.c_str());
- return false; // Fail to link
+ return false;
}
-
- linkedAttributeLocation[attribute.name] = availableIndex;
- linkedAttribute.push_back(attribute);
}
}
- for(auto const &it : linkedAttribute)
+ // Link attributes that don't have a binding location nor a layout location
+ for(auto const &attribute : vertexShader->activeAttributes)
{
- int location = getAttributeBinding(it);
+ if(attribute.layoutLocation == -1 && attributeBinding.find(attribute.name) == attributeBinding.end())
+ {
+ if(!linkAttribute(attribute, -1, usedLocations))
+ {
+ return false;
+ }
+ }
+ }
+
+ ASSERT(linkedAttribute.size() == vertexShader->activeAttributes.size());
+
+ for(auto const &attribute : linkedAttribute)
+ {
+ int location = getAttributeLocation(attribute.name);
ASSERT(location >= 0);
- int index = vertexShader->getSemanticIndex(it.name);
- int rows = std::max(VariableRegisterCount(it.type), 1);
+ int index = vertexShader->getSemanticIndex(attribute.name);
+ int rows = VariableRegisterCount(attribute.type);
for(int r = 0; r < rows; r++)
{
@@ -1675,17 +1638,69 @@
return true;
}
- int Program::getAttributeBinding(const glsl::Attribute &attribute)
+ bool Program::linkAttribute(const glsl::Attribute &attribute, int location, unsigned int &usedLocations)
{
- if(attribute.location != -1)
+ int rows = VariableRegisterCount(attribute.type);
+
+ if(location == -1)
{
- return attribute.location;
+ location = AllocateFirstFreeBits(&usedLocations, rows, MAX_VERTEX_ATTRIBS);
+
+ if(location == -1 || location + rows > MAX_VERTEX_ATTRIBS)
+ {
+ appendToInfoLog("Too many active attributes (%s)", attribute.name.c_str());
+ return false; // Fail to link
+ }
+ }
+ else
+ {
+ if(rows + location > MAX_VERTEX_ATTRIBS)
+ {
+ appendToInfoLog("Active attribute (%s) at location %d is too big to fit", attribute.name.c_str(), location);
+ return false;
+ }
+
+ // In GLSL 3.00, attribute aliasing produces a link error
+ // In GLSL 1.00, attribute aliasing is allowed
+ if(vertexShader->getShaderVersion() >= 300)
+ {
+ for(auto const &previousAttrib : linkedAttribute)
+ {
+ int previousLocation = getAttributeLocation(previousAttrib.name);
+ int previousRows = VariableRegisterCount(previousAttrib.type);
+
+ if(location >= previousLocation && location < previousLocation + previousRows)
+ {
+ appendToInfoLog("Attribute '%s' aliases attribute '%s' at location %d", attribute.name.c_str(), previousAttrib.name.c_str(), location);
+ return false;
+ }
+
+ if(location <= previousLocation && location + rows > previousLocation)
+ {
+ appendToInfoLog("Attribute '%s' aliases attribute '%s' at location %d", attribute.name.c_str(), previousAttrib.name.c_str(), previousLocation);
+ return false;
+ }
+ }
+ }
+
+ for(int i = 0; i < rows; i++)
+ {
+ usedLocations |= 1 << (location + i);
+ }
}
- std::map<std::string, GLuint>::const_iterator it = linkedAttributeLocation.find(attribute.name);
- if(it != linkedAttributeLocation.end())
+ linkedAttributeLocation[attribute.name] = location;
+ linkedAttribute.push_back(attribute);
+
+ return true;
+ }
+
+ int Program::getAttributeLocation(const std::string &name)
+ {
+ std::map<std::string, GLuint>::const_iterator attribute = linkedAttributeLocation.find(name);
+ if(attribute != linkedAttributeLocation.end())
{
- return it->second;
+ return attribute->second;
}
return -1;
diff --git a/src/OpenGL/libGLESv2/Program.h b/src/OpenGL/libGLESv2/Program.h
index 501d081..a232926 100644
--- a/src/OpenGL/libGLESv2/Program.h
+++ b/src/OpenGL/libGLESv2/Program.h
@@ -228,7 +228,8 @@
bool linkTransformFeedback();
bool linkAttributes();
- int getAttributeBinding(const glsl::Attribute &attribute);
+ bool linkAttribute(const glsl::Attribute &attribute, int location, unsigned int &usedLocations);
+ int getAttributeLocation(const std::string &name);
Uniform *getUniform(const std::string &name) const;
bool linkUniforms(const Shader *shader);
diff --git a/src/OpenGL/libGLESv2/libGLESv2.cpp b/src/OpenGL/libGLESv2/libGLESv2.cpp
index 789b0c8..b3f1d20 100644
--- a/src/OpenGL/libGLESv2/libGLESv2.cpp
+++ b/src/OpenGL/libGLESv2/libGLESv2.cpp
@@ -2345,7 +2345,6 @@
if(context)
{
-
es2::Program *programObject = context->getProgram(program);
if(!programObject)