What causes branching in GLSL depends on the GPU model and OpenGL driver version.
Most GPUs seem to have a form of "select one of two values" operation that has no branching cost:
n = (a==b) ? x : y;
and sometimes even things like:
if(a==b) {
n = x;
m = y;
} else {
n = y;
m = x;
}
will be reduced to a few select-value operation with no branching penalty.
Some GPU/Drivers have (had?) a bit of a penalty on the comparison operator between two values but a faster operation on comparison against zero.
Where it might be faster to do:
gl_FragColor.xyz = ((tmp1 - tmp2) != vec3(0.0)) ? E : tmp1;
rather than compare (tmp1 != tmp2)
directly but this is very GPU and driver dependant so unless you are targeting a very specific GPU and no others I recommend using the compare operation and leave that optimising job to the OpenGL driver as another driver might have an issue with the longer form and be faster with the simpler, more readable way.
"Branches" aren't always a bad thing either. For example on the SGX530 GPU used in the OpenPandora, this scale2x shader (30ms) :
lowp vec3 E = texture2D(s_texture0, v_texCoord[0]).xyz;
lowp vec3 D = texture2D(s_texture0, v_texCoord[1]).xyz;
lowp vec3 F = texture2D(s_texture0, v_texCoord[2]).xyz;
lowp vec3 H = texture2D(s_texture0, v_texCoord[3]).xyz;
lowp vec3 B = texture2D(s_texture0, v_texCoord[4]).xyz;
if ((D - F) * (H - B) == vec3(0.0)) {
gl_FragColor.xyz = E;
} else {
lowp vec2 p = fract(pos);
lowp vec3 tmp1 = p.x < 0.5 ? D : F;
lowp vec3 tmp2 = p.y < 0.5 ? H : B;
gl_FragColor.xyz = ((tmp1 - tmp2) != vec3(0.0)) ? E : tmp1;
}
Ended up dramatically faster than this equivalent shader (80ms) :
lowp vec3 E = texture2D(s_texture0, v_texCoord[0]).xyz;
lowp vec3 D = texture2D(s_texture0, v_texCoord[1]).xyz;
lowp vec3 F = texture2D(s_texture0, v_texCoord[2]).xyz;
lowp vec3 H = texture2D(s_texture0, v_texCoord[3]).xyz;
lowp vec3 B = texture2D(s_texture0, v_texCoord[4]).xyz;
lowp vec2 p = fract(pos);
lowp vec3 tmp1 = p.x < 0.5 ? D : F;
lowp vec3 tmp2 = p.y < 0.5 ? H : B;
lowp vec3 tmp3 = D == F || H == B ? E : tmp1;
gl_FragColor.xyz = tmp1 == tmp2 ? tmp3 : E;
You never know in advance how a specific GLSL compiler or a specific GPU will perform until you benchmark it.
To add the to point (even tho I don't have actual timing numbers and shader code to present you for this part) I currently use as my regular test hardware:
- Intel HD Graphics 3000
- Intel HD 405 Graphics
- nVidia GTX 560M
- nVidia GTX 960
- AMD Radeon R7 260X
- nVidia GTX 1050
As a wide range of different, common, GPU models to test with.
Testing each with Windows, Linux proprietary, and Linux open source OpenGL & OpenCL drivers.
And every time I try to micro-optimise GLSL shader (as in the SGX530 example above) or OpenCL operations for one particular GPU/Driver combo I end up equally hurting the performance on more than one of the other GPUs/Drivers.
So other than clearly reducing high-level mathematical complexity (eg: convert 5 identical divisions to a single reciprocal and 5 multiplications instead) and reducing texture lookups/bandwidth, it most likely will be a waste of your time.
Every GPU is too different from the others.
If you'd be working specifically on (a) gaming console(s) with a specific GPU this would be a different story.
The other (less significant for small game devs but still notable) aspect of this is that computer GPU drivers might one day silently replace your shaders (if your game becomes popular enough) with custom re-written ones optimised for that particular GPU. Doing that all work for you.
They will do this for popular games that are frequently used as benchmarks.
Or if you give your players access to the shaders so they can easily edit them themselves some of them might squeeze a few extra FPS for their own benefit.
For example there are fan-made shader & texture packs for Oblivion to dramatically increase frame rate on otherwise barely-playable hardware.
And lastly, once your shader get complex enough, your game almost completed, and you start testing on different hardware you'll be busy enough just fixing your shaders to work at all on a variety of GPUs as it is due to various bugs you wont have time to optimise them to that degree.