In OpenGL is there a way to get a list of all uniforms & attribs used by a shader program?

25,913

Solution 1

Variables shared between both examples:

GLint i;
GLint count;

GLint size; // size of the variable
GLenum type; // type of the variable (float, vec3 or mat4, etc)

const GLsizei bufSize = 16; // maximum name length
GLchar name[bufSize]; // variable name in GLSL
GLsizei length; // name length

Attributes

glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &count);
printf("Active Attributes: %d\n", count);

for (i = 0; i < count; i++)
{
    glGetActiveAttrib(program, (GLuint)i, bufSize, &length, &size, &type, name);

    printf("Attribute #%d Type: %u Name: %s\n", i, type, name);
}

Uniforms

glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &count);
printf("Active Uniforms: %d\n", count);

for (i = 0; i < count; i++)
{
    glGetActiveUniform(program, (GLuint)i, bufSize, &length, &size, &type, name);

    printf("Uniform #%d Type: %u Name: %s\n", i, type, name);
}

OpenGL Documentation / Variable Types

The various macros representing variable types can be found in the docs. Such as GL_FLOAT, GL_FLOAT_VEC3, GL_FLOAT_MAT4, etc.

Solution 2

There has been a change in how this sort of thing is done in OpenGL. So let's present the old way and the new way.

Old Way

Linked shaders have the concept of a number of active uniforms and active attributes (vertex shader stage inputs). These are the uniforms/attributes that are in use by that shader. The number of these (as well as quite a few other things) can be queried with glGetProgramiv:

GLint numActiveAttribs = 0;
GLint numActiveUniforms = 0;
glGetProgramiv(prog, GL_ACTIVE_ATTRIBUTES, &numActiveAttribs);
glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &numActiveUniforms);

You can query active uniform blocks, transform feedback varyings, atomic counters, and similar things in this way.

Once you have the number of active attributes/uniforms, you can start querying information about them. To get info about an attribute, you use glGetActiveAttrib; to get info about a uniform, you use glGetActiveUniform. As an example, extended from the above:

GLint maxAttribNameLength = 0;
glGetProgramiv(prog, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH​, &maxAttribNameLength);
std::vector<GLchar> nameData(maxAttribNameLength)
for(int attrib = 0; attrib < numActiveAttribs; ++attrib)
{
  GLint arraySize = 0;
  GLenum type = 0;
  GLsizei actualLength = 0;
  glGetActiveAttrib(prog, attrib, nameData.size(), &actualLength, &arraySize, &type, &nameData[0]);
  std::string name((char*)&nameData[0], actualLength - 1);
}

Something similar can be done for uniforms. However, the GL_ACTIVE_UNIFORM_MAX_LENGTH​ trick can be buggy on some drivers. So I would suggest this:

std::vector<GLchar> nameData(256);
for(int unif = 0; unif < numActiveUniforms; ++unif)
{
  GLint arraySize = 0;
  GLenum type = 0;
  GLsizei actualLength = 0;
  glGetActiveUniform(prog, unif, nameData.size(), &actualLength, &arraySize, &type, &nameData[0]);
  std::string name((char*)&nameData[0], actualLength - 1);
}

Also, for uniforms, there's glGetActiveUniforms, which can query all of the name lengths for every uniform all at once (as well as all of the types, array sizes, strides, and other parameters).

New Way

This way lets you access pretty much everything about active variables in a successfully linked program (except for regular globals). The ARB_program_interface_query extension is not widely available yet, but it'll get there.

It starts with a call to glGetProgramInterfaceiv, to query the number of active attributes/uniforms. Or whatever else you may want.

GLint numActiveAttribs = 0;
GLint numActiveUniforms = 0;
glGetProgramInterfaceiv(prog, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &numActiveAttribs);
glGetProgramInterfaceiv(prog, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numActiveUniforms);

Attributes are just vertex shader inputs; GL_PROGRAM_INPUT means the inputs to the first program in the program object.

You can then loop over the number of active resources, asking for info on each one in turn, from glGetProgramResourceiv and glGetProgramResourceName:

std::vector<GLchar> nameData(256);
std::vector<GLenum> properties;
properties.push_back(GL_NAME_LENGTH​);
properties.push_back(GL_TYPE​);
properties.push_back(GL_ARRAY_SIZE​);
std::vector<GLint> values(properties.size());
for(int attrib = 0; attrib < numActiveAttribs; ++attrib)
{
  glGetProgramResourceiv(prog, GL_PROGRAM_INPUT, attrib, properties.size(),
    &properties[0], values.size(), NULL, &values[0]);

  nameData.resize(values[0]); //The length of the name.
  glGetProgramResourceName(prog, GL_PROGRAM_INPUT, attrib, nameData.size(), NULL, &nameData[0]);
  std::string name((char*)&nameData[0], nameData.size() - 1);
}

The exact same code would work for GL_UNIFORM; just swap numActiveAttribs with numActiveUniforms.

Solution 3

For anyone out there that finds this question looking to do this in WebGL, here's the WebGL equivalent:

var program = gl.createProgram();
// ...attach shaders, link...

var na = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
console.log(na, 'attributes');
for (var i = 0; i < na; ++i) {
  var a = gl.getActiveAttrib(program, i);
  console.log(i, a.size, a.type, a.name);
}
var nu = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
console.log(nu, 'uniforms');
for (var i = 0; i < nu; ++i) {
  var u = gl.getActiveUniform(program, i);
  console.log(i, u.size, u.type, u.name);
}
Share:
25,913
hyperlogic
Author by

hyperlogic

\(^_^)/

Updated on July 09, 2022

Comments

  • hyperlogic
    hyperlogic almost 2 years

    I'd like to get a list of all the uniforms & attribs used by a shader program object. glGetAttribLocation() & glGetUniformLocation() can be used to map a string to a location, but what I would really like is the list of strings without having to parse the glsl code.

    Note: In OpenGL 2.0 glGetObjectParameteriv() is replaced by glGetProgramiv(). And the enum is GL_ACTIVE_UNIFORMS & GL_ACTIVE_ATTRIBUTES.

  • Mecki
    Mecki about 13 years
    Perfect answer: Short, straight to the point and everything I ever wanted to know :-)
  • vallentin
    vallentin about 8 years
    Further, there's no such thing as GL_OBJECT_ACTIVE_UNIFORMS, there's a GL_OBJECT_ACTIVE_UNIFORMS_ARB but what you actually want to use is GL_ACTIVE_UNIFORMS. The same goes for attributes.
  • Tara
    Tara about 5 years
    Thanks for the samples. However actualLength - 1 should be actualLength - 1 as glGetActiveAttrib returns the length of the name excluding the NULL-character.
  • Makogan
    Makogan almost 5 years
    I just noticed something, this only gives you the location for the first element in a uniform array. Not for all of them. What do you do if you want / need all of them?
  • Ruslan
    Ruslan almost 4 years
    @Makogan the whole array has this location. To set it, you then use glUniform??v with count set to the number of elements in the array. And actual number of elements of the uniform is returned by glGetActiveUniform in its size output parameter.