glslfunction-pointersraytracing

glsl function pointer (or equivalent)


I am attempting to call one of many functions based on the value of a variable. The variable is set during runtime, so code on the CPU will not work. Using an if/switch statement will be slow due to the large number of possibilities.[probably incorrect]

I am looking for a way to store the functions in an array, either by using function pointers, or by storing the actual equations (e.g. texcoord.x*2) in an array.

Example pseudo-code:

in vec2 texcoord;
out vec4 color;

int number; //Assigned a number between 0 and 300 during runtime
    
float func1(void) {
  return texcoord.x + texcoord.y;
}

float func2(void) {
  return texcoord.x*2;
}

...

float func299(void) {
  return texcoord.y - 7;
}

void main(void) {
  number = <something calculated during runtime>;
  float output = func<number>(); //              <--------------
  color = vec4(output, output, output, 1);
}

Solution

  • GLSL does not have function pointers. Even SPIR-V doesn't have function pointers. Shaders represent a limited execution environment. And one of those limitations is the lack of a requirement for a stack. And you can't really have arbitrary function pointers without one of those.

    The horribly il-advised1 shader subroutines feature of GLSL 4.00 probably won't help either. It would only help if your <something calculated during runtime> were a CPU-generated value, and that seems unlikely in your case.

    The only general solution you have is a switch statement. And quite frankly, there's nothing wrong with that.

    I mean, you're already going to absolutely murder your performance by doing this kind of condition anyway. No matter how it gets implemented, if different instances in the same wavefront are executing separate code, you're kinda screwed performance-wise.

    Not to mention, a switch statement does not necessarily get slower based on how many conditions there are. How it gets implemented depends entirely on the hardware. If jump tables are available, it can be done reasonably efficiently (ignoring the above performance issue, of course).

    Also, there is Colonel Thirty Two's idea, where you encode each function's operation as a dot-product with a constant vector. Obviously, this limits what your various functions can actually execute. But if it works in your case, it works.

    1 If you want to contest this, consider that SPIR-V provides an analog to every feature of GLSL, no matter how redundant, silly, or unnecessary... except shader subroutines.