Program Introspection

From OpenGL.org
Jump to: navigation, search

Program Introspection is a mechanism for taking a program object and querying information about it, so as to be able to interface and interact with it. For example, if there is a uniform, you may need to query its location so that you can set its value.

Naming

The introspection APIs will often handle make a single "variable" definition in GLSL appear as though it were multiple variables. This is done for variables that are not basic types or arrays of basic types (note that in GLSL, "basic types" includes vector and matrix types).

For example, consider the following:

struct A_Struct
{
  vec4 first;
  vec2 second;
  vec2 third[3];
};
 
uniform A_Struct unif;

The struct itself cannot be queried via the introspection API; the struct is just a prototype, an aggregate type definition. The variable unif​ is a uniform​, of type `A_Struct`.

However, if you try to get the uniform location, or any other property, of unif​, you will be unable to do so. As far as the introspection API is concerned, there is no unif​. Because the introspection API is built around basic types, struct aggregates of basic type are not directly visible.

Instead, each sub-element of the struct that is a basic type is visible. So there is a uniform named unif.first​. There is a uniform named unif.second​. And so forth.

This rule is applied recursively. Consider the following:

struct Inner
{
  vec2 inOne;
  vec4 inTwo;
};
 
struct Outer
{
  vec4 first;
  Inner data;
  vec2 second;
  vec2 third[3];
};
 
uniform Outer unif;

The uniforms generated from this are as follows: unif.first​, unif.data.inOne​, unif.data.inTwo​, etc. There is no unif.data​ variable as far as the introspection API is concerned.

Arrays

Arrays work differently, depending on whether they are arrays of basic types or arrays of aggregates (structs or arrays are aggregates. Arrays of arrays requires either GL 4.3 or the ARB_arrays_of_arrays extension).

An array of basic type represents a single resource:

struct Aggregate
{
  vec2 main;
  vec2 sec[3];
};
 
uniform vec3 anArray[7];
uniform Aggregate unif;

anArray[0]​ is a single resource of type vec3​, which has an array size of 7. There is no resource named anArray[3]​; just for index 0. Similarly, unif.sec[0]​ is a single resource of type vec2​, which has an array size of 3.

Note that the array index can be removed in these cases. Thus the query-by-name APIs will accept either anArray​ or anArray[0]​. But if you query the name, you will always get the "[0]" version.

Arrays of aggregates (structs or, if OpenGL 4.3 or ARB_arrays_of_arrays is available other arrays) are more complicated. They work like structs, such that each individual array element is taken as a separate resource with a distinct name. Consider:

struct Aggregate
{
  vec2 main;
  vec2 sec[3];
};
 
uniform Aggregate unifArray[5];

There is no uniform named unifArray​. However, there is a uniform named unifArray[0].main​. There is one named unifArray[2].sec[0]​, which is an array of basic types. There are five of each of these. As before, the bottom-level array can be clipped off: unifArray[2].sec​ is equivalent to unifArray[2].sec[0]​.

These rules are applied recursively up the hierarchy.

Interface block member naming

Interface blocks can have effects on the names of their members. If the interface block has an instance name, then the member name will be prefixed by the block name (not the instance name).

uniform BlockName
{
  int mem;
};
 
uniform BlockName2
{
  int mem;
} instanceName2;

The above code will create two uniforms, each of which is in its own separate block. The name of the first is "mem", in both GLSL and OpenGL's introspection API. The name of the second is "BlockName2.men" in the introspection API, but the GLSL shader will call it "instanceName2.mem".

The reason for this is that blocks can be shared between shader stages. They are connected by their block name; it is perfectly valid to link two shaders that use blocks with the same block name (and member definitions), but with different instance names. This is commonly used when passing data between the various Vertex Processing shader stages. So while within a particular shader stage only one instance name applies, at the program level multiple instance names could apply to the same block. Therefore the block name, the name that is constant across shader stages, is used instead.

Note that this naming applies to all interface block types. So variables declared in input and output blocks are also decorated as such.

Shader storage blocks

Shader storage block
Core in version 4.4
Core since version 4.3
Core ARB extension ARB_shader_storage_buffer_object

The array rules work slightly differently for shader storage block members. They have the concept of a "top-level array".

When recursively moving through the members of a shader storage block, the first array encountered is considered the "top-level array." This array is considered special, and it has a special size field (separate from the array size) in the query API. Top-level arrays are considered a single resource.

Consider this:

buffer SSBlock
{
  vec2 first[10];
  vec3 double_dim[2][5];
  vec4 triple_dim[5][4][8];
};

first[0]​ is a top-level array, with a type of vec2​ and a top-level array size of 10. There is no resource named SSBlock.first[3]​.

double_dim[0][0]​ is a top-level array, with a top-level array size of 2. But it also has a regular array size of size 5, with a type of vec3​. So there is only one resource; there is no double_dim[0][3]​ and so forth, just the base resource.

triple_dim[0][0][0]​ is a top-level array, with a top-level array size of 5. It also has a regular array size of 8. However, there is another resource named triple_dim[0][1][0]​. And two more at that.

In total, the above shader storage block definition has 6 member resources, 4 of which are derived from triple_dim​.

With SS-Blocks, the regular array index comes from the last array index, while the top-level index comes from the first. And the top-level takes priority, so if there is only one array index, it is the top-level array index.

Old style

The APIs in this section represent an older interface to program information. If OpenGL 4.3 or ARB_program_interface_query is available, you should use those APIs. Be advised that the new APIs are the only way to query the following information:

Attributes

If a program has a Vertex Shader, vertex shader inputs, aka Vertex Attributes, can be queried. Vertex attributes can be active or inactive. Attributes that are unused are inactive; they do not have a binding. The number of active attributes in a program can be retrieved with glGetProgramiv with GL_ACTIVE_ATTRIBUTES​.

To retrieve information about an attribute, call this function:

 void glGetActiveAttrib( GLuint program​, GLuint index​, GLsizei bufSize​, GLsizei *length​, GLint *size​, GLenum *type​, char *name​ );

The index​ is a number on the half-open range [0, GL_ACTIVE_ATTRIBUTES​); it represents a particular active attribute. name​ will be filled in with the name of the attribute; bufSize​ is the size of this character array. If length​ is not NULL, it will be filled in with the number of characters written to name​. The size​ will be filled with the number of elements in the attribute array, if the attribute is an array type. Otherwise, it will be set to 1. The type​ will be set to the OpenGL enumeration for the GLSL type of the attribute.

To get the attribute location (the one used by glVertexAttribPointer, not the index​ field used above), we use this function:

 GLint glGetAttribLocation( GLuint program​, const char *name​ );

If name​ is not an active attribute in program​, the function will return -1.

Fragment Outputs

Fragment color numbers and indices can be queried with these functions:

 GLint glGetFragDataLocation(GLuint program​, const char * name​);
 GLint glGetFragDataIndex(GLuint program​, const char * name​);

Note that you must provide a name for these functions. Unlike with attributes, this API cannot be used to enumerate the names and properties of fragment shader outputs. You must know what the names of the locations are.

Uniforms and blocks

Active uniforms are uniforms that the shader compiler/linker has determined are in active use by the executable code. Uniforms that are not active cannot be queried.

All uniforms, whether uniform block names or just uniform names, are assigned an index when the program is linked. This index is not the same as a uniform location or a uniform block index.

You can enumerate all of the active uniforms by using glGetProgram with GL_ACTIVE_UNIFORMS​. Thus, all active uniform indices will have indices less than this value. You can then loop over this range.

Note: This will iterate over all active uniforms, including those declared in uniform blocks. If you want to iterate over only the active uniforms in the default block (ie: not in a block), then you must skip any uniform who's GL_UNIFORM_BLOCK_INDEX​​ is not -1.

However, you can also get uniform indices from string names of the uniforms. This is done with this function:

 void glGetUniformIndices(GLuint program​, GLsizei uniformCount​, const char ** uniformNames​, GLuint *uniformIndices​);

This will cause the array uniformIndices​, which is of size uniformCount​ to be filled with the indices for the strings in the uniformNames​ list (also of size uniformCount​); If a uniform name in the string list doesn't correspond to the name of an active uniform, then the corresponding index in uniformIndices​ will be GL_INVALID_INDEX​.

Once you have an index, you can query the name of the uniform with this function:

 void glGetActiveUniformName( GLuint program​, GLuint uniformIndex​, GLsizei bufSize​, GLsizei *length​, char *uniformName​);

This function stores the name in the uniformName​ buffer, which is of size bufSize​ bytes. The actual length of uniformName​ is stored in length​ (unless length​ is NULL, which is allowed).

General information about uniforms can be queried with this function, which can retrieve information for multiple uniforms at once:

 void glGetActiveUniformsiv( GLuint program​, GLsizei uniformCount​, const GLuint *uniformIndices​, GLenum pname​, GLint *params​ );

The uniformIndices​ array is a list of uniform indices uniformCount​ in length. These are the uniforms that the user is asking to acquire. pname​ is an enum that determines what information to query for the uniforms. params​ are the values that get returned; it is an array uniformCount​ in length. One value for each uniform index is generated.

The possibilities for pname​ are:

  • GL_UNIFORM_TYPE​: Retrieves the GLenum for the uniform's type.
  • GL_UNIFORM_SIZE​: Retrieves the size of the uniform. For arrays, this is the length of the array1. For non-arrays, this is 1.
  • GL_UNIFORM_NAME_LENGTH​: The length of that uniform's name.
  • GL_UNIFORM_BLOCK_INDEX​: The uniform block index for this uniform, which can be used to query information about this block. If this uniform is not in a block, the value will be -1.
  • GL_UNIFORM_OFFSET​: The byte offset into the beginning of the uniform block for this uniform. If the uniform is not in a block, the value will be -1.
  • GL_UNIFORM_ARRAY_STRIDE​: The byte stride for elements of the array, for uniforms in a uniform block. For non-array uniforms in a block, this value is 0. For uniforms not in a block, the value will be -1.
  • GL_UNIFORM_MATRIX_STRIDE​: The byte stride for columns of a column-major matrix or rows for a row-major matrix, for uniforms in a uniform block. For non-matrix uniforms in a block, this value is 0. For uniforms not in a block, the value will be -1.
  • GL_UNIFORM_IS_ROW_MAJOR​: GL_TRUE​ if the matrix is row-major and the uniform is in a block. GL_FALSE​ is returned if the uniform is column-major, the uniform is not in a block (all non-block matrices are column-major), or simply not a matrix type.
  • GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX​: If the uniform defines an atomic counter, then this value will be the index of the buffer that this counter is associated with. Otherwise, it will be -1.
1: To facilitate optimizations, OpenGL implementations are allowed to implicitly shrink arrays if they can determine, from the linked program code, that certain array indices cannot be reached. Thus, this value may be smaller than what the shaders originally stated.

An older function, superseded by glGetActiveUniformName and glGetActiveUniformsiv also exists:

 void glGetActiveUniform( GLuint program​, GLuint index​, GLsizei bufSize​, GLsizei *length​, GLint *size​, GLenum *type​, char *name​ );

This retrieves the name as in glGetActiveUniformName, but it also retrieves GL_UNIFORM_TYPE​ in type​, GL_UNIFORM_SIZE​ in size​, and GL_UNIFORM_NAME_LENGTH​ in length​.

The length of the longest uniform name in a program can be queried with GL_ACTIVE_UNIFORM_MAX_LENGTH​ through glGetProgramiv. You can use this along with GL_ACTIVE_UNIFORMS​ to allocate an array of strings that will be sufficiently large enough to hold the string names for a program's uniforms.

Uniform blocks

The above APIs can query information about uniforms inside or outside of a block, but getting information about the uniform block itself, requires a different set of APIs.

Each active uniform block has an index. As with the uniform API, you can query this in two ways. You can iterate over the active uniform blocks by calling glGetProgram(GL_ACTIVE_UNIFORM_BLOCKS) to get the limit to iterate over. Or you can get the index for a uniform block by name with this function:

 void glGetUniformBlockIndex( GLuint program​​, const char *name​​ );

With the index, properties about the block as a while can be queried. The numerical properties can be queried with this function:

 void glGetActiveUniformBlockiv(GLuint program​, GLuint uniformBlockIndex​, GLenum pname​, GLint *params​);

pname​ must be one of these:

  • GL_UNIFORM_BLOCK_BINDING​​: The current block binding, as set either within the shader or from glUniformBlockBinding.
  • GL_UNIFORM_BLOCK_DATA_SIZE​: The buffer object storage size needed for this block.
  • GL_UNIFORM_BLOCK_NAME_LENGTH​: The length of this block's name.
  • GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS​: The number of active uniforms within this block.
  • GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES​: params​ will be filled in with the uniform indices of all uniforms that are stored in this block. It will receive GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS​ number of uniforms.
  • GL_UNIFORM_BLOCK_REFERENCED_BY_*​: GL_FALSE​ if the uniform block is not referenced by an active * shader, where * is the particular shader stage in question. It can be VERTEX_SHADER​, FRAGMENT_SHADER​, or GEOMETRY_SHADER​. If GL 4.0/ARB_tessellation_shader, then it can also be TESS_CONTROL_SHADER​ or TESS_EVALUATION_SHADER​. If GL 4.3/ARB_compute_shader, then COMPUTE_SHADER​ is also possible.

The name of a block can be queried with this function:

 void glGetActiveUniformBlockName(GLuint program​, GLuint uniformBlockIndex​, GLsizei bufSize​, GLsizei *length​, GLchar *uniformBlockName​);

You can use the GL_UNIFORM_BLOCK_NAME_LENGTH​ query to get how much space you should reserve.

Subroutines

Shader Subroutine Query
Core in version 4.4
Core since version 4.0
Core ARB extension ARB_shader_subroutine
V · E

Because the list of subroutine resources (of all kinds) are broken down by shader stage, all functions that query information about them take a shadertype​ parameter in addition to the program​ object to query from. The shadertype​ is the shader stage enumerator name for the stage to query information about.

Each active subroutine (a subroutine function which can be used with a subroutine uniform that itself is active) has an index. There are two ways to get the index for a particular subroutine function. If you have the name of a subroutine, you can query the index with this function:

GLuint glGetSubroutineIndex(GLuint program​, GLenum shadertype​, const GLchar *name​);

If name​ does not name an active subroutine function, then GL_INVALID_INDEX​ will be returned. Otherwise, it will be the index of the subroutine function.

To iterate through the list of all subroutine functions, you can query the total number of subroutines. This is done via this function:

void glGetProgramStageiv(GLuint program​, GLenum shadertype​, GLenum pname​, GLint *values​);

To query the number of active subroutine functions, pname​ must be GL_ACTIVE_SUBROUTINES​. This stores the number of active subroutine functions as a single integer in values​.

To get the name of a subroutine function from an index, pass the index​ to this function:

void glGetActiveSubroutineName(GLuint program​, GLenum shadertype​, GLuint index​, GLsizei bufsize​, GLsizei *length​, GLchar *name​);

bufsize​ is the total number of bytes that name​ points to; OpenGL will not write more than this number of bytes. If length​ is not NULL, the function will write the number of characters written into name​.

There is no way to query the length of the name for a specific subroutine function (at least, not with this API. You must use the query API for that). However, you can query the largest subroutine function name used by a shader stage. This is done with glGetProgramStage, using the GL_ACTIVE_SUBROUTINE_MAX_LENGTH​ enumerator.

The name is the only information that can be queried about a subroutine function directly.

Like regular uniforms, subroutine uniforms have both a location and an index. The location is used when actually selecting which subroutine to use for that uniform, while the index is only used to query information about that uniform.

To iterate through the number of subroutine uniforms for a stage and query information about them, you must first get the number of subroutine uniform indices. This uses glGetProgramStage with GL_ACTIVE_SUBROUTINE_UNIFORMS​. The valid indices range from 0 to that value minus one.

Armed with a subroutine uniform index​, one can query general information about the subroutine uniform with this function:

void glGetActiveSubroutineUniformiv(GLuint program​, GLenum shadertype​, GLuint index​, GLenum pname​, GLint *values​);

pname​ defines what information is to be set into values​:

  • GL_NUM_COMPATIBLE_SUBROUTINES​: The number of subroutine functions that can be used with this uniform.
  • GL_COMPATIBLE_SUBROUTINES​: This returns an array of indices for each of the subroutine functions that can be used with this uniform. values​ must be at least GL_NUM_COMPATIBLE_SUBROUTINES​ entries in size.
  • GL_UNIFORM_SIZE​: The number of entries in the subroutine uniform. Subroutine uniforms can be in arrays, so this is the count of array elements. If the subroutine uniform is not an array,
  • GL_UNIFORM_NAME_LENGTH​: The length of the uniform's name.

To get the subroutine uniform's name, call this function:

void glGetActiveSubroutineName(GLuint program​, GLenum shadertype​, GLuint index​, GLsizei bufsize​, GLsizei *length​, GLchar *name​);

The subroutine uniform location is used to know how to assign a subroutine function index to the subroutine uniform when it comes time to render with the shader.

Regular uniform variables are assigned locations arbitrarily. Subroutine uniforms are a bit different. The number of subroutine uniform locations assigned to a particular shader stage can be determined by calling glGetProgramStage with GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS​. All subroutine uniform locations less than that number (and >= 0) are active subroutine uniform locations. This is required by the specification.

Because subroutine uniforms can be arrays, there can be more subroutine uniform locations than there are subroutine uniform indices. Each subroutine uniform only counts as one for the index list, but arrayed subroutine uniforms take up multiple locations.

To get a subroutine uniform's location given the name, use this function:

GLint glGetSubroutineUniformLocation(GLuint program​, GLenum shadertype​, const GLchar *name​);

If name​ names an arrayed subroutine uniform, then the location refers to X consecutive locations starting from the returned location, where X is the array size declared in the shader. If the subroutine uniform is not active, this function will return -1.

Atomic counters

Atomic Counters
Core in version 4.4
Core since version 4.2
Core ARB extension ARB_shader_atomic_counters

Information about Atomic Counters can be queried via the following APIs. Atomic counter variables are opaque types as well as uniforms. As such, most of the information about atomic counters can be queried with the uniform interface.

What cannot be queried is the information about buffer object bindings and offsets into those buffers. Thus, OpenGL provides a special API just for querying the storage parameter information from a program.

Each atomic counter variable must have a buffer binding. The querying interface looks at buffer bindings, not variables. So if four variables all use the same binding index (with different offsets), then interface will only show 1 buffer binding.

To enumerate all of the active buffer bindings in a program, use glGetProgram(GL_ACTIVE_ATOMIC_COUNTER_BUFFERS). To query information about each binding, use this function:

void glGetActiveAtomicCounterBufferiv(Gluint program​, GLuint bufferIndex​, GLenum pname​, GLint *params​)

bufferIndex​ is a number on the half-open range from 0 to the number of active atomic counter buffers, which represents a specific buffer binding. pname​ is the specific data to query for this atomic counter. It can take the following values, which define what values are stored into params​:

  • GL_ATOMIC_COUNTER_BUFFER_BINDING​: The atomic counter buffer binding index for this buffer.
  • GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE​: The minimum size (implementation-dependent) of the buffer region that must be bound by the user. That is, when the user uses glBindBufferRange (or glBindBufferBase), the bound range must be at least this large.
  • GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS​​ and GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES​: The first enum returns the number of active atomic counter variables used by this binding point. The second enum will fill params​ with uniform indices for those atomic counter variables. The user must pass an array big enough to store GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS​​ indices.
  • GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_*​: GL_FALSE​ if the atomic counter buffer is not referenced by an active * shader, where * is the particular shader stage in question. The stage can be VERTEX_SHADER​, FRAGMENT_SHADER​, GEOMETRY_SHADER​, TESS_CONTROL_SHADER​, or TESS_EVALUATION_SHADER​. If GL 4.3/ARB_compute_shader is available, then COMPUTE_SHADER​ is also possible.

Interface query

Interface Query
Core in version 4.4
Core since version 4.3
Core ARB extension ARB_program_interface_query

This new interface provides a uniform mechanism for querying just about everything from a program. At least, you can query everything that external OpenGL code can interface with. You can't query information about number of functions (unless they're subroutines) or global variables or things like that. But uniforms, inputs/outputs, etc? All of those are made available through this consistent interface.

These APIs rely on some new concepts. Each linked program provides a number of interfaces, where each interface represents a set of active resources of some kind. An interface is a conceptual grouping. All of the uniforms, for example, are an interface. The active resources of the uniform interface represents all of the active uniforms in the program. And so forth.

The idea is to take all of the various different functions for introspecting programs and put them in a single interface that is extensible without adding new functions.

Interfaces

An interface represents a particular, named collection of resources that can be queried from a program. Each interface has a particular enumerator dedicated to it. The available interfaces are:

  • GL_UNIFORM​: The set of active uniforms in a program. This includes uniforms located in uniform blocks.
  • GL_UNIFORM_BLOCK​: The set of active uniform blocks in a program. These resources describe the blocks themselves, not the specific variables within them.
  • GL_ATOMIC_COUNTER_BUFFER​: The set of active atomic counter buffers in a program. This describes the buffer bindings, not the actual atomic variables. Those are uniforms.
  • GL_PROGRAM_INPUT​: The set of active user-defined inputs to the first shader stage in this program. If the first stage is a Vertex Shader, then this is the list of active attributes. If the program only contains a Compute Shader, then there are no inputs.
  • GL_PROGRAM_OUTPUT​: The set of active user-defined outputs from the final shader stage in this program. If the final stage is a Fragment Shader, then this represents the fragment outputs that get written to individual color buffers. If the program only contains a Compute Shader, then there are no outputs.
  • GL_TRANSFORM_FEEDBACK_VARYING​: The set of active outputs from the last Vertex Processing shader stage in the program, which can be used as outputs for Transform Feedback.
  • GL_TRANSFORM_FEEDBACK_BUFFER​​: Requires OpenGL 4.4 or ARB_enhanced_layouts. Like GL_ATOMIC_COUNTER_BUFFER​, this describes the arrangement of buffers used by a shader for transform feedback. The variables themselves are described by GL_TRANSFORM_FEEDBACK_VARYING​.
  • GL_BUFFER_VARIABLE​: The set of active buffer variables in the program. These are all of the variables stored within shader storage blocks or defined with the buffer​ qualifier.
  • GL_SHADER_STORAGE_BLOCK​: The set of active shader storage blocks. Much like the interface for uniform blocks, these resources describe the blocks themselves, not the specific variables within them.
  • GL_*_SUBROUTINE​: The set of active subroutines in the program. The "*" represents the shader stage to query from; unlike other interfaces, this only queries about specific stages. The "*" can be:
  • GL_*_SUBROUTINE_UNIFORM​: The set of active subroutine uniforms in the program. Subroutine uniforms behave differently from regular uniforms; they cannot be queried via the GL_UNIFORM​ interface. As before, the "*" represents the shader stage to query from; unlike other interfaces, this only queries about specific stages.

While the main purpose of the interfaces are to allow you to query information about the resources themselves, there is some per-interface information that is important. This information is usually an aggregate of all of the resources in the interface, such as maximum name length and such.

Per-interface information can be fetched via this function:

void glGetProgramInterfaceiv(GLuint program​, GLenum programInterface​, GLenum pname​, GLint * params​);

The programInterface​ is one of the interfaces, as defined above. pname​ is the particular data you want to query, and it gets written into params​. The possible pname​ values that can be queried from an interface are:

GL_ACTIVE_RESOURCES​​
The number of active resources of that interface type.
GL_MAX_NAME_LENGTH​​
The length of the longest string name among all resources of that interface type. This is useful for pre-allocating buffers if you are fetching names; you can always allocate the maximum length. As with all OpenGL string length APIs, the length includes the null-terminator. GL_ATOMIC_COUNTER_BUFFER​ and GL_TRANSFORM_FEEDBACK_BUFFER​ are unnamed, so you cannot use this pname​ with that interface.
Note: The reason for the last restriction is that those describe the buffer binding, not the counters themselves. The variables are uniforms (for atomic counters) or outputs (for transform feedback), so they names for them are queried through those interfaces. Just like GL_UNIFORM_BLOCK​s, GL_ATOMIC_COUNTER_BUFFER​s contain GL_UNIFORM​s. Similarly, GL_TRANSFORM_FEEDBACK_BUFFER​ contains GL_TRANSFORM_FEEDBACK_VARYING​s.
GL_MAX_NUM_ACTIVE_VARIABLES​​
The largest number of active variables among all resources in this interface. This should only be queried for interfaces that contain active variables. Namely, GL_UNIFORM_BLOCK​, GL_SHADER_STORAGE_BLOCK​, GL_TRANSFORM_FEEDBACK_BUFFER​, and GL_ATOMIC_COUNTER_BUFFER​; all others will return 0. Thus, if you use GL_UNIFORM_BLOCK​ with this param, you are asking for the maximum number of active variables that any active uniform block in the program has. Again, this is useful for preallocating buffers and so forth.
GL_MAX_NUM_COMPATIBLE_SUBROUTINES​​
The largest number of compatible subroutines among all subroutine variables for that shader stage. This can only be used with the GL_*_SUBROUTINE​ interfaces.

Resource indices

Active resources within an interface have indices. Each interface for a program maintains a separate list of indices. You can get an index for an active resource in one of two ways. You can query the GL_ACTIVE_RESOURCES​ as above and iterate over the resources. Or you can get the resource index by name:

 GLuint glGetProgramResourceIndex(GLuint program​, GLenum programInterface​, const char * name​);

Since GL_ATOMIC_COUNTER_BUFFER​ resources don't have names, you cannot query their indices by name (you query for the GL_UNIFORM​ that references the buffer, then query it's GL_ATOMIC_COUNTER_BUFFER_INDEX​​ property).

If the resource named by name​ is not an active resource, GL_INVALID_INDEX​ is returned.

Resource properties

Once an index for an active resource has been acquired, properties from that active resource can be queried. This is done via calls to this function:

 void glGetProgramResourceiv(GLuint program​, GLenum programInterface​, GLuint index​, GLsizei propCount​, const Glenum * props​, GLsizei bufSize​, GLsizei * length​, GLint * params​);

This function can retrieve multiple properties (though all from the same resource) into a buffer all at once. Most properties will only write one value to params​, but some will write several (in all cases, the length can be queried).

The sheer volume of information that can be queried from this interface is massive. It is all documented on glGetProgramResource's reference page, including the list of which interfaces support which properties. So there is no need to repeat it here.

There are some properties that are notable in that they cannot be queried from the older APIs or that they use different names:

GL_NUM_ACTIVE_VARIABLES​ and GL_ACTIVE_VARIABLES​
For interfaces that contain other resources (uniform/shader storage blocks and atomic counter buffers), this query is used to return a list of the active variables that they contain. The particular interface to use these indices with depends on what is being queried. If GL_UNIFORM_BLOCK​ or GL_ATOMIC_COUNTER_BUFFER​ is used, then the indices are indices into the GL_UNIFORM​ interface. If GL_SHADER_STORAGE_BLOCK​ is used, then they are indices into the GL_BUFFER_VARIABLE​ interface. If GL_TRANSFORM_FEEDBACK_BUFFER​ is used, then they are indices in the GL_TRANSFORM_FEEDBACK_VARYING​ interface.
GL_REFERENCED_BY_*_SHADER​
These are used to detect which shader stages use the variable. All of the variable interfaces (except GL_TRANSFORM_FEEDBACK_VARYING​ and the subroutine uniforms), can detect where they come from.

There is one property that cannot be queried from the above function: the resource's name. That must be queried with this function:

 GLuint glGetProgramResourceName(GLuint program​, GLenum programInterface​, GLuint index​, GLsizei bufSize​, GLsizei * length​, char * name​);

Only interfaces who's resources have names (ie: not GL_ATOMIC_COUNTER_BUFFER​ or GL_TRANSFORM_FEEDBACK_BUFFER​) can be queried this way.

Shortcuts

While the above interface is exceedingly comprehensive, it is less than convenient for accessing certain commonly-used information. Specifically, the GL_LOCATION​ and GL_LOCATION_INDEX​ fields. These fields have special APIs that mimic functions like glGetAttribLocation or glGetFragDataIndex:

 GLint glGetProgramResourceLocation(GLuint program​, GLenum programInterface​, const char * name​);
 GLint glGetProgramResourceLocationIndex(GLuint program​, GLenum programInterface​, const char * name​);

These functions are equivalent to calling glGetProgramResourceIndex with name​, and if it returns a valid index, calling glGetProgramResource with the appropriate field. The programInterface​ parameter must match with what is allowed for querying those parameters with glGetProgramResource. So the LocationIndex​ can only be used with the GL_PROGRAM_OUTPUT​ interface.

Examples

This system is rather complex. So here is some example code for doing common tasks.


V · E

Iteration over all non-block uniform variables, fetching their names, types, and locations:

GLint numUniforms = 0;
glGetProgramInterfaceiv(prog, GL_UNIFORM, GL_ACTIVE_RESOURCES, &numUniforms);
const GLenum properties[4] = {GL_BLOCK_INDEX​, GL_TYPE​, GL_NAME_LENGTH​, GL_LOCATION};
 
for(int unif = 0; unif < numUniforms; ++unif)
{
  GLint values[4];
  glGetProgramResourceiv(prog, GL_UNIFORM, unif, 4, properties, 4, NULL, values);
 
  //Skip any uniforms that are in a block.
  if(properties[0] != -1)
    continue;
 
  //Get the name. Must use a std::vector rather than a std::string for C++03 standards issues.
  //C++11 would let you use a std::string directly.
  std::vector<char> nameData(values[2]);
  glGetProgramResourceName(prog, GL_UNIFORM, unif, nameData.size(), NULL, &nameData[0]);
  std::string name(nameData.begin(), nameData.end() - 1);
}


V · E

Iteration over all uniforms within each uniform block.

GLint numBlocks = 0;
glGetProgramInterfaceiv(prog, GL_UNIFORM_BLOCK, GL_ACTIVE_RESOURCES, &numBlocks );
const GLenum blockProperties[1] = {GL_NUM_ACTIVE_VARIABLES}
const GLenum activeUnifProp[1] = {GL_ACTIVE_VARIABLES}
const GLenum unifProperties[3] = {GL_NAME_LENGTH​, GL_TYPE​, GL_LOCATION};
 
for(int blockIx = 0; blockIx < numBlocks; ++numBlocks)
{
  GLint numActiveUnifs = 0;
  glGetProgramResourceiv(prog, GL_UNIFORM_BLOCK, blockIx, 1, blockProperties, 1, NULL, numActiveUnifs);
 
  if(!numActiveUnifs)
    continue;
 
  std::vector<GLint> blockUnifs(numActiveUnifs);
  glGetProgramResourceiv(prog, GL_UNIFORM_BLOCK, blockIx, 1, activeUnifProp, numActiveUnifs, NULL, &blockUnifs[0]);
 
  for(int unifIx = 0; unifIx < numActiveUnifs; ++unifIx)
  {
    GLint values[3];
    glGetProgramResourceiv(prog, GL_UNIFORM, unifIx, 3, unifProperties, 4, NULL, values);
 
    //Get the name. Must use a std::vector rather than a std::string for C++03 standards issues.
    //C++11 would let you use a std::string directly.
    std::vector<char> nameData(values[0]);
    glGetProgramResourceName(prog, GL_UNIFORM, blockUnifs[unifIx], nameData.size(), NULL, &nameData[0]);
    std::string name(nameData.begin(), nameData.end() - 1);
  }
}


Reference