Simple Introduction to OpenGL 4 Shader Subroutines

1 – Overview

This extension adds support to shaders for “indirect subroutine calls”, where a single shader can include many subroutines and dynamically select through the API which subroutine is called from each call site. Switching subroutines dynamically in this fashion can avoid the cost of recompiling and managing multiple shaders, while still retaining most of the performance of specialized shaders.

Subroutines are a nice alternative to conditional tests or shader swaps and allows to manage shader combinations. With subroutines, you can have a single GPU program (an übershader) that can perform several tasks instead of a collection of specialised GPU programs. Subroutines are easy to setup and use.

To illustrate this article, I coded with GLSL Hacker a small demo that renders a PQ torus knot in three colors: red, blue and yellow. Each color is managed by a subroutine. Just press on the SPACE key to change the subroutine. The demo is available in the host_api/gl-400-arb-shader-subroutine/ folder of the code sample pack.

Next you have to declare a subroutine uniform called Color. Color is a kind of pointer function and you will call it to draw the torus knot with FragColor = Color();

subroutine uniform color_t Color;

Now the real subroutines. Each subroutine must follow the prototype declared previously. To tell to the GLSL compiler that a function is a subroutine, just add the subroutine qualifier before the function:

subroutine(color_t)
vec4 ColorRed()
{
return vec4(1, 0, 0, 1);
}

Now ColorRed() is a subroutine. Same thing for ColorBlue() and ColorYellow().

Last thing, call the subroutine:

void main()
{
FragColor = Color();
}

Easy isn’t it? Now let’ see how intialize the subroutines in the OpenGL side. The OpenGL specification defines several functions for subroutines. In our case, only two are interesting: glGetSubroutineIndex() and glUniformSubroutinesuiv().

Each subroutine has an index and the number of indices is limited. You can retrieve the max number of subroutines using GL_MAX_SUBROUTINES:

GLint n = 0;
glGetIntegerv(GL_MAX_SUBROUTINES, &n);

On a GeForce GTX 660, GL_MAX_SUBROUTINES gives 1024.

The index of a subroutine can be retrieved using glGetSubroutineIndex(). Here is how to get the index of the ColorRed() subroutine: