When setting up attribute locations for an OpenGL shader program, you are faced with two options:
glBindAttribLocation() before linking to explicitly define an attribute location.
or
glGetAttribLocation() after linking to obtain an automatically assigned attribute location.
What is the utility for using one over the other?
And which one, if any, is preferred in practice?
I know one good reason to prefer explicit location definition.
Consider that you hold your geometry data in Vertex Array Objects. For a given object, you create a VAO in such way that the indices correspond to, for example:
Now consider that you want to draw one object with two different shaders. One shader requires position and normal data as input, the other - positions and texture coords.
If you compile those shaders, you will notice that the first shader will expect the positions at attribute index 0 and normals at 1. The other would expect positions at 0 but texture coords at 1.
Quoting https://www.opengl.org/wiki/Vertex_Shader:
This means that you wouldn't be able to use your VAO with both shaders. Instead of having one VAO per, say, object, you'd need - in the worst case - a separate VAO per object per shader.
Forcing the shaders to use your own attribute numbering convention via
glBindAttribLocation
can solve this problem easily - all you need to do is to keep a consistent relation between attributes and their estabilished IDs, and force the shaders to use that convention when linking.(That's not really a big issue if you don't use separate VAOs, but still might make your code clearer.)
BTW:
There's a third option in OpenGL/GLSL 3.3: Specify the location directly in shader code. It looks like this:
But this is not present in GLSL ES shader language.
The third option, ie
layout(location=0) in vec4 position;
in the shader code, is now available in OpenGL ES 3.0/GLSL 300 es. Only for vertex shader input variables though.Another answer here is that glGetAttribLocation returns data to the caller, which means that it implicitly requires a pipeline flush. If you call it right after you compile your program, you're essentially forcing asynchronous compilation to occur synchronously.