4
votes

I'm working on a post-processing GLSL (ES) fragment shader for 2D shock waves. It's easy to distort the texture a bit in a circle and it already works for single shock waves. Now I need support for multiple waves at the same time, my ideas:

1st idea: Do post-processing (bind FBO -> bind previous texture -> draw call) for every wave. Easy implementation, but a lot state changes and draw calls.

2nd idea: Add data texture with all information for all waves and a single uniform with the number waves, so I can do something like that:

uniform int count;
uniform sampler2D dataTex;
const float dataTexSize = 32;
[...]
    vec4 pixel;
    for(int i = 0; i < count; i++)
    {
        vec2 dataTexPos = vec2(i / dataTexSize, 0);
        pixel += shockwave(texture2D(dataTex, dataTexPos));
    }
    pixel /= float(count);
[...]

But apparently GPUs don't like loops with a non-constant expression and the driver cannot unroll.

Is there a best practice to give shaders a "dynamic amount of work" ? My OpenGL version is 2.0 ES.

1
You can try profiling both implementations, on whatever hardware you have access to.Colonel Thirty Two
The problem is, that the second idea doesn't work at all, because I cannot compile the shader with a for-loop to a non-constant. I would like to know if there is a way to do it.Bastian Born
"But apparently GPUs don't like loops with a non-constant expression" No, crappy GPUs don't like that. ES 2.0 doesn't allow non-constant looping, but actually modern GPUs are perfectly fine with it. Desktop GL 3.0 ditched such requirements nearly a decade ago. In the mobile space, GL ES 3.0 ditched those requirements.Nicol Bolas
You're totally right. But unfortunately I'm limited to 2.0 ES.Bastian Born

1 Answers

2
votes

There are only a handful of ES2.0 GPUs (available in any reasonable numbers) that do not support dynamic looping in fragment shaders. Two that I specifically know of are Tegra 2 (Android GLSL fragment shader on NVIDIA tegra2 - but it is getting quite old) and Broadcom VC4 (Raspberry Pi and Amazon FireTV Stick). The ES 2.0 spec doesn't require support, but as @Nicol Bolas mentioned, the vast majority of mobile GPUs do support it. So, if you're not targeting any of these chips, then you can most likely just use the shader.

There unfortunately isn't a GLES extension to advertise support for dynamic looping. You basically just have to compile a shader with a dynamic loop, and see if it succeeds. In the case your target doesn't support it, you can 'fake' a dynamic loop, by compiling multiple shaders with a constant loop expression, instead of using a uniform. Eg:

int count = %d;
uniform sampler2D dataTex;
const float dataTexSize = 32;
[...]
    vec4 pixel;
    for(int i = 0; i < count; i++)
    {
        vec2 dataTexPos = vec2(i / dataTexSize, 0);
        pixel += shockwave(texture2D(dataTex, dataTexPos));
    }
    pixel /= float(count);
[...]

Each time you encounter a situation where you need a different loop count, replace the %d with the desired loop count and compile a new shader. As long as count has a reasonably small number of possible values, you won't end up with so many shaders.