Getting a list of active vertex input attributes and locations
As covered in the previous recipe, the input variables within a vertex shader are linked to generic vertex attribute indices at the time the program is linked. If we need to specify the relationship, we can either use layout qualifiers within the shader, or we could call glBindAttribLocation
before linking.
However, it may be preferable to let the linker create the mappings automatically and query for them after program linking is complete. In this recipe, we'll see a simple example that prints all the active attributes and their indices.
Getting ready
Start with an OpenGL program that compiles and links a shader pair. You could use the shaders from the previous recipe.
As in previous recipes, we'll assume that the handle to the shader program is stored in a variable named programHandle
.
How to do it...
After linking and enabling the shader program, use the following code to display the list of active attributes:
Start by querying for the number of active attributes:
GLint numAttribs; glGetProgramInterfaceiv(programHandle, GL_PROGRAM_INPUT,GL_ACTIVE_RESOURCES, &numAttribs);
Loop through each attribute and query for the length of the name, the type and the attribute location, and print the results to standard out:
GLenum properties[] = {GL_NAME_LENGTH, GL_TYPE, GL_LOCATION}; printf("Active attributes:\n"); for( int i = 0; i < numAttribs; ++i ) { GLint results[3]; glGetProgramResourceiv(programHhandle, GL_PROGRAM_INPUT,i, 3, properties, 3, NULL, results); GLint nameBufSize = results[0] + 1; char * name = new char[nameBufSize]; glGetProgramResourceName(programHandle, GL_PROGRAM_INPUT, i, nameBufSize, NULL, name); printf("%-5d %s (%s)\n", results[2], name, getTypeString(results[1])); delete [] name; }
How it works...
In step 1, we query for the number of active attributes, by calling glGetProgramInterfaceiv
. The first argument is the handle to the program object, and the second (GL_PROGRAM_INPUT
) indicates that we are querying for information about the program input variables (the vertex attributes). The third argument (GL_ACTIVE_RESOURCES
) indicates that we want the number of active resources. The result is stored in the location pointed to by the last argument numAttribs
.
Now that we have the number of attributes, we query for information about each one. The indices of the attributes run from 0 to numAttribs-1
. We loop over those indices and for each we call glGetProgramResourceiv
to get the length of the name, the type and the location. We specify what information we would like to receive by means of an array of GLenum
values called properties
. The first argument is the handle to the program object, the second is the resource that we are querying (GL_PROGRAM_INPUT
). The third is the index of the attribute, the fourth is the number of values in the properties
array, which is the fifth argument. The properties
array contains GLenum
s, which specify the specific properties we would like to receive. In this example, the array contains: GL_NAME_LENGTH
, GL_TYPE
, and GL_LOCATION
, which indicates that we want the length of the attribute's name, the data type of the attribute and its location. The sixth argument is the size of the buffer that will receive the results; the seventh argument is a pointer to an integer that would receive the number of results that were written. If that argument is NULL
, then no information is provided. Finally, the last argument is a pointer to a GLint
array that will receive the results. Each item in the properties
array corresponds to the same index in the results
array.
Next, we retrieve the name of the attribute by allocating a buffer to store the name and calling glGetProgramResourceName
. The results
array contains the length of the name in the first element, so we allocate an array of that size with an extra character just for good measure. The OpenGL documentation says that the size returned from glGetProgramResourceiv
includes the null terminator, but it doesn't hurt to make sure by making a bit of additional space. In my tests, I've found this to be necessary on the latest NVIDIA drivers.
Finally, we get the name by calling glGetProgramResourceName
, and then print the information to the screen. We print the attribute's location, name and type. The location is available in the third element of the results array, and the type is in the second. Note the use of the function getTypeString
. This is a simple custom function that just returns a string representation of the data type. The data type is represented by one of the OpenGL defined constants GL_FLOAT
, GL_FLOAT_VEC2
, GL_FLOAT_VEC3
, and so on. The getTypeString
function consists of just one big switch statement returning a human-readable string corresponding to the value of the parameter (see the source code for glslprogram.cpp
in the example code for this book).
The output of the previous code looks like this when it is run on the shaders from the previous recipes:
Active attributes: 1 VertexColor (vec3) 0 VertexPosition (vec3)
There's more...
It should be noted that in order for a vertex shader input variable to be considered active, it must be used within the vertex shader. In other words, a variable is considered active if it is determined by the GLSL linker that it may be accessed during program execution. If a variable is declared within a shader, but not used, the previous code will not display the variable because it is not considered active and effectively ignored by OpenGL.
The previous code is only valid for OpenGL 4.3 and later. Alternatively, you can achieve similar results with the functions glGetProgramiv
, glGetActiveAttrib
and glGetAttribLocation
.
See also
The Compiling a shader recipe
The Linking a shader program recipe
The Sending data to a shader using vertex attributes and vertex buffer objects recipe