GLSL compile-time constants / conditional comp.

Hi,

I’ve written several uber shaders but need to split them up into smaller versions which only do limited functionality or for instance fewer number of lights. I don’t want to do this by hand for every shader permutation, and really want to be able to pass (at compile-time) constants or #defines into them from my application to setup fixed size loops (for lighting) or conditionally compiled blocks of shader code (#ifdef#endif).

I remember that in CG or the CGFX format you can seletively compile areas of the shader or set constants externally from the shader, maybe similar to passing in uniforms but at compilation.

Is there an equivalent in GLSL? Alternatively, I could preprocess the GLSL code in my app to find and replace certain keywords if this is not possible.

Thanks,
Chris.

>> Is there an equivalent in GLSL?

The spec says nothing about optimizations like these. If it’s not in the spec there’s little to say one way or the other.

Read the various vendor perf documents and bench your code thoroughly.

Using #define statements does work, at least on Nvidia. I used them to replace uniform variables by constants. However, this was slower to compile so I did an offline pre-pass to to the string replacement in the shader code directly before sending it to the GLSL compiler.

Is there a way to pass the #defines to the GLSL compiler when compiling/linking similar to for instance the -D switch on some gcc compilers? This would be the only useful way I can think of that #defines could be used.

Otherwise, bertgp, I’ll need to do something similar to what you describe.

No, but you can do something like this:
string defines = Load(“Defines.frag”);
string source = Load(“Shader.frag”);
LoadProgram(defines + source);

There was talk of some sort of include mechanism for GLSL in the days preceding GL3, but it appears that’s been either shelved for a later version or abandoned outright.

Instead of concatenating the strings yourself, you can proivde multiple strings when you call glShaderSource and the driver will concatenate the strings for you. It needs to copy them anyway in its own allocated memory so it could even be a little bit faster since you avoid creating another temporary string object with “defines + source”.

bertgp already gave you the tip about providing multiple strings to glShaderSource. You can have the first string be “compilation directives/constants” and the second be the actual shader source.

Also, with this, you can generally get much more readable shader code by using shader constants and “if” checks based on them to select branches than by using #define and #if.

For instance, instead of:

#define FOG_MODE VTX_EXP2
...
#if FOG_MODE == VTX_EXP2
...
#endif

Use:

const int FOG_MODE = VTX_EXP2
...
if ( FOG_MODE == VTX_EXP2 )
 ...

Of course with the latter you need to define int values for each valid value – worth it for the increase in shader readability.

I remember that in CG or the CGFX format you can seletively compile areas of the shader or set constants externally from the shader, maybe similar to passing in uniforms but at compilation.

CG_LITERAL parameters IIRC.

Is there an equivalent in GLSL?

As was mentioned above, not officially. But if you use NVidia’s Cg compiler (cgc) to cross-compile GLSL, you can see that const values are expanded into the shader and used to fold out branches, loops, and such.

Also note that “const” means anything the compiler can infer is “const”, not just those things with a const in front of them. This is a really good thing because there’s currently a quirk in NVidia’s GLSL compiler where you can’t do this:

const int MY_ARRAY[ 2 ] = int[]( 1, 2 )

You have to drop the const. But NVidia’s compiler is aggressive enough it folds those values in too because it recognizes they’re const.

Note that there are a very few circumstances where you do need to use an #if instead. For instance, if the length of an array would be dynamically computed to be 0, you have to compile it out with #if – because GLSL annoyingly doesn’t accept that as valid… Grr…

I’ve decided to go with bertgp’s suggestion of sending several strings to glShaderSource. The first string can then be hardcoded or dynamically constructed with constants or #defines. This seems like the most straightforward method.

Thanks everyone…