sunLoadingImage
whowedImag
decoration left 1
decoration left 2
transhome
transprojects
transgallery
transarticles
decoration rigth
English

Show/Hide search bar
black cat logo variable logo
[20 Jan 2013]

Shader Subroutines in GLSL

There are situations when you have to change behavior of a shader. For example, to change lighting model between simple and more complex. Only internal calculations of lighting is different, but not input and output interface of the shader. You can achive this task by simply selecting another shader with required functionality, but switching of shaders is quite slow operation and may decrease performance of the application. You can also set uniform variable, and determine behavior by checking value of that uniform variable with if statement. This may also decrease performance. The problem can be solved with help of GLSL shader subroutines. Shader subroutines use principle that is very similar to function pointers in C++: by call of function pointer, you are calling function to which function pointer points. "Function pointers" in GLSL are called Shader Subroutines.
First of all you have declare a type of a subroutine - signature of input and output parameters, and a name of the type. Then define and implement functions, which have interface compatible with previously defined subroutine type. And finally you have to declare subroutine (in other words - pointer to function) which is special form of uniform variable. Before rendering you have to set this uniform subroutine to index of function that will be executed by subroutine. By default subroutines execute first compatible function.
// declare type of subroutine
// subroutine returnType subroutineType(type param1, ...);
subroutine vec4 testType();

// declare functions that are compatible with type of subroutine
// subroutine (subroutineType) returnType functionName(type param1, ...);
subroutine (testType) vec4 func1()
{
   return vec4(1, 1, 1, 0);
}
subroutine (testType) vec4 func2()
{
   return vec4(0.5, 0.5, 0.5, 0.5);
}

// uniform variable, that takes role of pointer to function
subroutine uniform testType dynamicFunction;

// now you can call function to which subroutine points
dynamicFunction();
Before rendering set function that should be called by subroutine (from C++ code). First select the shader with glUseProgram(). Then get location of uniform subroutine. There is special function to get location of subroutine - glGetSubroutineUniformLocation().
After that get indexes of functions that are compatible with subroutine. Use glGetSubroutineIndex() to achieve this.
You can set which function should be executed by the subroutine with glUniformSubroutinesuiv() function. You have to pass array of indices to this function. Each entry in array corresponds to uniform subroutine (element 0 - first subroutine in shader, 1 - second, and so on). Value of index corresponds to index of function, which will be called by subroutine. Number of subroutines, which you should configure can be retrieved with help of GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS value.
Following example shows how to select function func1 for subroutine:
// select shader program
glUseProgram(shaderProgram);

// get index of subroutine uniform
GLint locationOfFunction = glGetSubroutineUniformLocation(
         shaderProgram, GL_FRAGMENT_SHADER, "dynamicFunction");

// get indices of compatible functions
GLuint indexFunc1 = glGetSubroutineIndex(shaderProgram, GL_FRAGMENT_SHADER, "func1");
GLuint indexFunc2 = glGetSubroutineIndex(shaderProgram, GL_FRAGMENT_SHADER, "func2");

// get number of active subroutine uniforms in shader
GLsizei numActiveUniforms;
glGetProgramStageiv(shaderProgram, GL_FRAGMENT_SHADER,
         GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS, &numActiveUniforms);

// set indices for all active uniform subroutines
std::unique_ptr indices(new GLuint[numActiveUniforms]);
indices[locationOfFunction] = indexFunc1;
glUniformSubroutinesuiv(GL_FRAGMENT_SHADER, numActiveUniforms, indices.get());
To get maximum supported number of subroutines and subroutine uniforms:
GLint maxSubroutines, maxSubroutinesUniformLocations;
glGetIntegerv(GL_MAX_SUBROUTINES, &maxSubroutines);
glGetIntegerv(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, &maxSubroutinesUniformLocations);
Following code snippet enumerates all uniform subroutines and compatible functions in the shader:
GLint numActiveSubroutinveUniforms, numCompatibleSubroutines, tLen;
const int maxNameSize = 64;
char tName[maxNameSize];

// don't forget to activate the shader
glUseProgram(shaderProgram);

// get number of uniform subroutines from the shader
glGetProgramStageiv(shaderProgram, GL_FRAGMENT_SHADER,
         GL_ACTIVE_SUBROUTINE_UNIFORMS, &numActiveSubroutinveUniforms);

// get data for each uniform subroutine
for (int uniformSubroutine = 0; uniformSubroutine < numActiveSubroutinveUniforms; ++uniformSubroutine)
{
   // get name for uniform subroutine with index uniformSubroutine
   glGetActiveSubroutineUniformName(
         shaderProgram,
         GL_FRAGMENT_SHADER,
         uniformSubroutine,
         maxNameSize,
         &tLen,
         tName
      );

   // get number of compatible functions
   GLint numCompatibleSubroutines;
   glGetActiveSubroutineUniformiv(
         shaderProgram,
         GL_FRAGMENT_SHADER,
         uniformSubroutine,
         GL_NUM_COMPATIBLE_SUBROUTINES,
         &numCompatibleSubroutines
      );

   // get indices of compatible functions
   std::unique_ptr indices(new GLint[numCompatibleSubroutines]);
   glGetActiveSubroutineUniformiv(
         shaderProgram,
         GL_FRAGMENT_SHADER,
         uniformSubroutine,
         GL_COMPATIBLE_SUBROUTINES,
         indices.get()
      );

   // get names of compatible functions
   for(int compatibleSubroutine=0;
      compatibleSubroutine<numCompatibleSubroutines; compatibleSubroutine++)
   {
      glGetActiveSubroutineName(
            shaderProgram,
            GL_FRAGMENT_SHADER,
            indices[compatibleSubroutine],
            maxNameSize,
            &tLen,
            tName
         );
   }
}



Sun and Black Cat- Igor Dykhta (igor dykhta email) 2007-2014