Shader Subroutine

From OpenGL Wiki
(Redirected from Subroutine Uniform)
Jump to navigation Jump to search
Shader Subroutine
Core in version 4.6
Core since version 4.0
Core ARB extension ARB_shader_subroutine

Shader Subroutines are special GLSL functions which can have variations. The specific variation that will be called is selected by the OpenGL code.

There is the concept of a "subroutine type". This represents a particular function signature (parameters and return value). Functions which match a subroutine type's signature can be defined as a "subroutine" of that type. All of the subroutines that are declared within a subroutine type for a list of variations of that type which could be called.

Once a subroutine type is defined, the shader can declare one or more global "variables" of that subroutine type. Each variable represents a specific, user-specified variation of that function to be called. Because these are set from OpenGL code before rendering, these are called "subroutine uniform"s (even though they are declared with uniform, they do not work like regular uniforms in most ways). The shader code can call these subroutine uniforms as though they were functions.

The user can query indices and locations for subroutine types, subroutine uniforms, and subroutine functions. These queries allow the user to specify which subroutine function shall be called by which subroutine uniform.

Note: This specification must happen every time the current program or program pipeline object binding is changed. This is not program object state, and the context will destroy this state after each new binding.

Shader subroutines cannot be used by SPIR-V shaders. You can of course emulate this functionality by using a switch statement, based on the value of a uniform variable.

Definition[edit]

The first step in defining shader subroutines is to define a subroutine type. A subroutine type consists of a named function signature. It represents a specific set of subroutines that can be called.

A subroutine type is declared as follows:

subroutine FuncReturnType SubroutineTypeName(Type0 param0, Type1 param1, ...);

The SubroutineTypeName is the name of the subroutine type defined by this statement. Multiple different named types can use the same function signature.

A specific function definition (and only a function definition. If you use this syntax, the function must have a body) can be associated with one or more subroutine types as follows:

subroutine(SubroutineTypeName) FuncReturnType FunctionName(Type0 param0, Type1 param1, ...)
{ ... }

FunctionName's function signature (parameters and return type) must exactly match the subroutine type's signature.

GLSL normally allows function overloading. However, subroutine functions cannot be overloaded; you cannot declare a second FunctionName with a different set of parameters. Even if the second function is not a subroutine.

A function can be associated with multiple subroutine types by listing multiple SubroutineTypeNames, separated by commas. Obviously, all of those subroutine types must use the same signature.

Note: This is a subroutine definition, but it is also a function definition. So you can call FunctionName directly in GLSL, and doing so will work exactly as if it were not defined with subroutine. However, that's not using it as a subroutine; to learn how to do that, read on.

Subroutine uniforms[edit]

Warning: Despite the name and declaration syntax, subroutine uniforms are not in any way like a regular Uniform. They have their own introspection API and interface. Also, they are not program object state, unlike actual uniforms.

A subroutine uniform can be created by using the subroutine keyword with the uniform storage qualifier, as well as a subroutine type as defined above:

subroutine uniform SubroutineTypeName SubroutineUniformName;

Each subroutine uniform represents one of the subroutine variations defined by the SubroutineTypeName. Exactly which subroutine is used for a particular uniform is defined by the user at render time.

Subroutine uniforms cannot be passed around to other GLSL code or used in any way except by calling or using the array accessor when calling a particular element of a subroutine uniform array.

Subroutine uniforms can be arrayed. With OpenGL 4.3 or ARB_arrays_of_arrays, they can even be in arrays of arrays (multidimensional arrays). The array definition must be explicitly sized, and the array index at the point of use must be a Dynamically Uniform Expression. When an arrayed subroutine uniform is considered active (ie: the shader has determined that it can be called), all of the array elements are active, even if the shader can determine if some of the array elements are not used.

Calling[edit]

To use a subroutine for its intended purpose (switching between different functions without linking the shader), you must call the subroutine uniform, not one of the subroutine definitions. Essentially, you treat SubroutineUniformName as though it were a function who's prototype is defined by SubroutineTypeName:

vec4 ret = SubroutineUniformName(arg0, arg1, ...);

This will invoke the particular subroutine that the user specified before rendering.

Arrayed subroutine uniforms simply use an array index before the call:

vec4 ret = SubroutineUniformName[2](arg0, arg1, ...);

This index must be a Dynamically Uniform Expression.

Subroutine resources[edit]

After linking a program, each Shader Stage within that program has two sets of resources that deal with subroutines. These resources are the list of defined subroutine functions and the list of subroutine uniforms. Querying these resources is important for how to actually set these things from shaders. The OpenGL 4.3 or ARB_program_interface_query feature can be used to query these resources. If those are not available, the following dedicated query APIs can be used.

Note: Each shader stage within a program has it's own set of resources. Unlike regular uniforms, subroutine resources are not shared among shader stages. Thus, every program potentially has 12 sets: two for each of the 6 shader stages.


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 glGetActiveSubroutineUniformName(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.

In-shader specification[edit]

Resource Specification
Core in version 4.6
Core since version 4.3
Core ARB extension ARB_explicit_uniform_location
V · E

Shader Subroutines use a number of resources that can be automatically assigned to each shader stage at link time. However, the user can explicitly define them within the shader text as well, to avoid having to query them.

Subroutine functions each have a specific index that identifies that particular subroutine among all subroutines in a shader stage. This subroutine can be set from within a shader using the index layout qualifier:

layout(index = 2) subroutine(SubroutineTypeName, ...) ...;

This sets the particular subroutine function definition to index 2. No two subroutine functions may have the same index. Also, this index is subject to the limitations on the number of subroutines in a shader stage.

Each subroutine uniform variable within a shader stage has a particular position in that stage's list of subroutine uniforms. This location in the array can be set using the location layout qualifer:

layout(location = 1) subroutine uniform SubroutineTypeName subroutineVariableName;

This means that array index 1 in the call to glUniformSubroutines refers to the variable subroutineVariableName.

The number of active subroutine uniforms for this shader stage will be the largest location + 1. This means that some "active" uniform locations may be unused. If the above example were the only subroutine uniform, then the number of active subroutine uniforms will be considered to be 2, with location 0 going unusued.

Arrays of subroutine uniforms are always assigned consecutive uniform locations.

If some uniforms are user-assigned and some are linker-assigned, the linker will fill in the unused indices first, before increasing the number of active uniforms. So the number of active subroutine uniforms can be larger than this. The linker will never assign a subroutine uniform to a user-assigned subroutine uniform, even if the user-assigned one is not active.

The linking can fail if user-defined subroutine uniform assignments do not leave enough room for linker-assigned ones. Linker-assigned subroutine uniform locations for arrays must also be consecutively assigned, so if there is insufficient space to assign that many uniform locations, the linking will fail.

Runtime selection[edit]

The state for the selection of which subroutine functions to use for which subroutine uniforms is not part of the program object. Instead, it is part of the context state, similar to how texture bindings and uniform buffer bindings are part of context state.

Warning: The biggest difference between those state and the subroutine state is this: EVERY TIME you call glUseProgram, glBindProgramPipeline or glUseProgramStages, all of the current subroutine state is completely lost. This state is never preserved, so you must reset it every single time you change programs.

For each shader stage, a subroutine index must be set into each subroutine uniform. As previously stated, all active subroutine locations for a stage effectively form an array from 0 to GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS - 1. The API takes advantage of this, requiring that all active subroutine uniform locations to be set on a shader stage in a single call:

void glUniformSubroutinesuiv(GLenum shadertype​, GLsizei count​, const GLuint *indices​);
Warning: count​ must be exactly the value queried from GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS for the active program that provides the shader stage specified by shadertype​. It cannot be smaller or larger than this value; therefore, each call to this function must set all active subroutine uniforms for a stage all at once.

A subroutine uniform location is actually an index into the array specified by indices​. Each element in this array specifies the subroutine function index to be used by that subroutine uniform.

This function performs validation on indices​. It will detect:

  • If one of the indices​ does not specify a valid subroutine function index for that shader stage. This includes subroutine function indices that are larger than the number of active ones or those that (due to explicit specification of subroutine function indices) simply does not represent a valid subroutine function.
  • For each indices​, if the index does not specify a subroutine function that was defined with the subroutine type for that subroutine uniform.

Note that the subroutine uniform location array can contain locations that do not correspond to an active subroutine uniform location, due to explicit specification of subroutine uniform locations. You can put any index into such a location in the array.

Limitations[edit]

Unlike many of the shader limitations, each shader stage is required to have the same subroutine limits. There aren't separate limits for each stage; they all share the same limits.

No shader stage can have more than GL_MAX_SUBROUTINES number of subroutine functions. This effectively limits the number of subroutine types as well, since each subroutine function must use at least one type. This limit must be no less than 256.

No shader stage can have more than GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS number of individual subroutine uniform locations. Arrayed subroutine uniforms take up one location per element. This limit must be no less than 1024.

So there's quite a bit of room for shader subroutines.