Yes. This should be fine.
In fact on some GL drivers (e.g. NVidia’s), the underlying generated assembly will have no subroutine call nor return in it at all (even though NVidia’s ASM shader assembly language has CAL / RET instructions). I just re-verified this by piping your shader through cgc. On NVidia GL drivers, another way to get access to this shader assembly is to use the glGetProgramBinary call Silence mentioned. With either, you can see exactly what the NVidia GL driver is generating behind-the-scenes when you give it a specific GLSL shader and assess the result yourself.
That said, even on drivers which may not do aggressive constant folding and resulting dead-code elimination (like NVidia’s does), a subroutine call and return are each coherent branches, so they should be pretty efficient on modern GPUs.
To answer a subsequent question you might have, this is also very, very efficient to do in GLSL, on NVidia GL drivers at least:
const int RENDERSTATE = <value2>;
if ( RENDERSTATE == <value1> )
{
<bunch of complex stuff>
}
else
{
<another bunch of complex stuff>
}
This is the core of a classic “ubershader” where you have some constant “Renderstate” values at the top of your shader that select which shader permutation you’re compiling, and you have logic in your shader to choose the correct branch(es) of logic to use for that Renderstate combination.
In the above example, the NVidia GLSL compiler (and possibly those for other vendors too; not sure) will, during compilation, plug in the value for RENDERSTATE into all references in the shader, pre-evaluate any constant expressions in the shader (e.g. the “if” expression), and pre-determine which paths of your shader are “dead code” which will never be executed and throw them out (e.g. the first block of code under the “if” statement, and even the evaluation of the “if” condition itself.
So for the above example, after compilation (including constant folding and dead code elimination), what’s left of your shader will look like this:
<another bunch of complex stuff>
As you can see, all of the logic that’s performing code branch selections based on the values of constant expressions is “free” in the sense that it’s never evaluated on the GPU. It’s evaluated in the compiler at compile time. This technique makes for a much more readable ubershader than if you’d done all this with preprocessor #if/#else/#endif statements (been there, done that).
Also, as I recall, you don’t really even need the “const” on the renderstate variable(s) on NVidia drivers. The NVidia GLSL compiler will look at your usage and figure that out for itself.