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).

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 nounif​. 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.

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).

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:

structAggregate{vec2main;vec2sec[3];};uniformAggregateunifArray[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).

uniformBlockName{intmem;};uniformBlockName2{intmem;}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.

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.

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.

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.

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:

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:

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:

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.

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:

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.

Subroutines

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:

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:

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,

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:

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

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.

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

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_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_*_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.

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_BLOCKs, GL_ATOMIC_COUNTER_BUFFERs contain GL_UNIFORMs. Similarly, GL_TRANSFORM_FEEDBACK_BUFFER contains GL_TRANSFORM_FEEDBACK_VARYINGs.

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:

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:

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:

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​:

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.

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

GLintnumUniforms=0;glGetProgramInterfaceiv(prog,GL_UNIFORM,GL_ACTIVE_RESOURCES,&numUniforms);constGLenumproperties[4]={GL_BLOCK_INDEX​,GL_TYPE​,GL_NAME_LENGTH​,GL_LOCATION};for(intunif=0;unif<numUniforms;++unif){GLintvalues[4];glGetProgramResourceiv(prog,GL_UNIFORM,unif,4,properties,4,NULL,values);//Skip any uniforms that are in a block.if(values[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::stringname(nameData.begin(),nameData.end()-1);}

GLintnumBlocks=0;glGetProgramInterfaceiv(prog,GL_UNIFORM_BLOCK,GL_ACTIVE_RESOURCES,&numBlocks);constGLenumblockProperties[1]={GL_NUM_ACTIVE_VARIABLES};constGLenumactiveUnifProp[1]={GL_ACTIVE_VARIABLES};constGLenumunifProperties[3]={GL_NAME_LENGTH​,GL_TYPE​,GL_LOCATION};for(intblockIx=0;blockIx<numBlocks;++blockIx){GLintnumActiveUnifs=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(intunifIx=0;unifIx<numActiveUnifs;++unifIx){GLintvalues[3];glGetProgramResourceiv(prog,GL_UNIFORM,blockUnifs[unifIx],3,unifProperties,3,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::stringname(nameData.begin(),nameData.end()-1);}}