8

I'm looking for a way to use a function as an argument to another function in GLSL. In regular C, it can be simulated by passing a function pointer as a function argument. It also seems that other languages (like HLSL) now provide ways to deal with high-level constructs like higher-order functions, or can simulate them with clever use of HLSL structures. unfortunately I'm stuck with GLSL for now, and I can't find any way to simulate higher-order functions. Is it really impossible in current (4.2) GLSL ? Or am I missing some clever trick ?

common example of what I'm trying to achieve :

int f(someType f2, int i) {
    return f2(i);
}
Christian Rau
  • 45,360
  • 10
  • 108
  • 185
Alex Repain
  • 93
  • 1
  • 5
  • And you require this kind of (probably costly) indirection in a shader, to achieve...? – Christian Rau Feb 29 '12 at 15:42
  • Apart the fact that this kind of abstraction would be sweet to use, the practical problem I have is the following : I have a function f that switches over a parameter p (let's say, an int), and calls another function f2, which nature depends on the switched value. Being able to pass f2 as an argument would allow me to call directly without switching, since in the context of my application, I know exactly what function to call inside f when calling f itself. I don't really know if it's possible, nor what would it cost, but I think that going on a 100-case switch is probably worse. – Alex Repain Feb 29 '12 at 16:02
  • 2
    https://en.wikipedia.org/wiki/Defunctionalization – sigfpe Aug 01 '15 at 01:38

2 Answers2

5

I'm looking for a way to use a function as an argument to another function in GLSL.

Short answer: you can't.

The closest thing to this kind of functionality you'll get in GLSL is shader subroutines. And that only allows the external OpenGL API to select which subroutine to use, not the shader itself.

So just do the switch/case statement and get it over with.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks for the advice. It strikes me however, how GLSL is not able to provide higher-level constructs than pointer-less C ... – Alex Repain Mar 01 '12 at 09:56
  • 1
    @AlexRepain: GLSL has to run in an environment that doesn't even support *recursion*. Why would you expect function pointers to work? – Nicol Bolas Mar 01 '12 at 17:24
  • I don't expect function pointers to work, I'm asking if there is a workaround to it. Besides, I don't really need recursion per se, I'm just looking for some callback-passing. The fact that it could turn out to form a recursion and then blow up in my face would be an acceptable side-effect. Plus I don't see why the environment couldn't handle recursion at all (think tail-call recursion), if it can handle a 'while'... – Alex Repain Mar 01 '12 at 19:01
  • @AlexRepain: My point isn't that recursion affects what you want. My point is that if the GLSL execution environment is so static that it can't even allow recursion, it would be unreasonable to expect it to allow selecting which function to run dynamically. Shaders are very *static*. Most functions don't even exist; they're inlined away. – Nicol Bolas Mar 01 '12 at 19:42
  • Fair Point. I suppose that if a syntactic way to choose a function at run-time was provided, it would be boiled down to an inlined switch statement anyway. Thanks ! – Alex Repain Mar 01 '12 at 21:16
2

There are no higher-order functions in GLSL, but it's possible to simulate them:

#define second_order 1
#define second_order1 2
#define another_function 3
//there are no function pointers in GLSL, so I use integers instead

int call(int f2,int param1){
    //instead of a function, an integer is passed as a parameter
    switch(f2){
        case second_order:
            return param1*2;
        case second_order1:
            return param1*3;
    }
}

int call(int f2,int param1,int param2){
    //this function can be overloaded to accept more parameters
    switch(f2){
        case another_function:
            return param1 + param2;
    }
}

int f(int f2, int i) {
    return call(f2,i);
}

Alternatively, this can be done using structs:

struct function{
    int x;
};
function Sin(){
    return function(1);
}
function Cos(){
    return function(2);
}
float call(function func,float x){
    if(func == Sin()){
        return sin(x);
    }
    else if(func == Cos()){
        return cos(x);
    }
}

vec4 map(function func,vec4 a1){
    //this function can be overloaded for different array sizes
    vec4 a2;
    for(int i = 0; i < 4; i++){
        a2[i] = call(func,a1[i]);
    }
    return a2;
}

It's also possible to simulate generic second-order functions using macros:

#define map(function,input1,output1) \
for(int i = 0; i < input1.length(); i++){ \
    output1[i] = function(input1[i]); \
}

This macro can be used with any type of array:

float[] arr1 = float[](1.,3.,4.);
float[arr1.length()] output1;
map(sin,arr1,output1)
Anderson Green
  • 30,230
  • 67
  • 195
  • 328