5
$\begingroup$

I am running this fragment shader on every pixel on screen. I am trying to make it as efficient as possible. BTW I am running open gl es 2.0.

The code segment below is only a sample from the code, there are about 56 different calls to Gaussian() spread across different functions. I am wondering wether it would be valuable to replace the calls to Gaussian() with there appropriate resulting float value.

I know a lot of times stuff like this is pre-calculated on compilation of the shader, or calculated only once because the gpu realizes it is the same calculation for all fragments.

So would it be worthwhile for me to manually calculate each of these and replace them with their values?

uniform float scalar;
varying float scalart;
float deviationScale = 1.2;
float finalScalar = 1.1;
float Gaussian(float x, float deviation)
{
    return (1.0 / sqrt(2.0 * 3.141592 * deviation)) * exp(-((x * x) / (2.0 * deviation)));
}
vec3 blurS5()
{
    vec3 blr = vec3(0.0);
    blr += texture2D(s_texture, (v_texcoord + vec2(2.0 * scalart, 0.0))).xyz *  finalScalar * Gaussian(2.0, 2.0 * deviationScale) ;
    blr += texture2D(s_texture, (v_texcoord + vec2(1.0 * scalart, 0.0))).xyz *  finalScalar * Gaussian(1.0, 2.0 * deviationScale) ;

    blr += texture2D(s_texture, (v_texcoord + vec2(0.0 * scalart, 0.0))).xyz *  finalScalar * Gaussian(0.0, 2.0 * deviationScale) ;

    blr += texture2D(s_texture, (v_texcoord + vec2(-1.0 * scalart, 0.0))).xyz *  finalScalar * Gaussian(-1.0, 2.0 * deviationScale) ;
    blr += texture2D(s_texture, (v_texcoord + vec2(-2.0 * scalart, 0.0))).xyz *  finalScalar * Gaussian(-2.0, 2.0 * deviationScale) ;
    return blr;

}

void main()
{
        if (scalar == 2.)
        {
            gl_FragColor = vec4(blurS5(), 1.0);
        }
        else if (scalar == 3.)
        {
            gl_FragColor = vec4(blurS9(), 1.0);
        }
        //There are waaayyy more of these
}
$\endgroup$
4
$\begingroup$

It depends. On a desktop, if the Gaussian function is always called with the same arguments, the shader compiler will probably optimize it so it's evaluated only once. On a mobile platform though, the compiler has less time to do optimizations and might miss it. Either way, you can confirm by looking at the generated assembly.

Note that Aras Pranckevičius of Unity wrote a GLSL optimizer to address the problem of sub-optimal compilers on mobile: https://github.com/aras-p/glsl-optimizer

That being said, regarding your sample code, I would definitely at least expose (1.0 / sqrt(2.0 * 3.141592 * deviation)) as a uniform.

$\endgroup$
2
$\begingroup$

The shader compiler will very likely perform common-subexpression consolidation, and compute that function once, and reuse it each time it appears in the shader.

You might think that, since deviationScale and all the other args are constants, it is possible it gets constant-folded entirely and never executes at runtime at all. But this is incorrect, as sqrt() and exp() are not compile-time constexpr functions. The compiler cannot assume any specific implementation (and associated numerical properties) of these math functions, and so the entire function will not get optimized out, and it will not get re-used across threads either, as this is precluded by the shading language's programming model.

Your best move is to pre-calculate the entire expression and stash it in a uniform, if it is important that this be optimal.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.