That seems practical, thanks! However, what are the different types (bool, int) used for? I always thought shader permutations are about simple binary "used/not used" scenarios?
They're all integers in mine, but with a variable number of bits. If you don't specify, then you only get 1 bit by default, which is a boolean option
A scenario where you might want to be able to use a range of values rather than on/off is when you're passing a variable sized array of something into a shader, but want to be able to unroll the loop that operates on that array (i.e. you want the array size known at compile time).
e.g. you might have a shader that supports 1-4 spotlights.
Do you simply generate every available permutation, or do you do some content-processing to determine if a permutation does not need to be built because it can't be used?
At the moment I build all permutations. I don't have any shaders with 232 permutations, etc... I think the largest I've got at the moment is 25 permutations, and the largest I've had in the past was about ~240 valid permutations... so my data hasn't forced me to solved that problem yet
If I did end up in a situation where I had millions of valid permutations, I would add an extra annotation to my 'Option' field, specifying whether it is an "engine option", or a "material option".
The difference is that:
* "engine options" are set at runtime and can't be predicted -- e.g. is the object highlighted by a ray gun, so should be glowing right now. The compiler has to build all possible permutations of these options.
* "material options" are set by the artists and then don't change during gameplay. They can be known ahead of time, so the compiler can see which option values are used.
Then once I had that data (of knowable options and unknowable options), I'd generate a list of all the materials that use each shader, then generate a unique list of their "option" bitfields. Then I'd use that list, and the knowledge of the unknowable "engine options" to compile just the possibly-used permutations.
I know of one game engine where, at the moment that the artist selects a certain configuration of options, that permutation is compiled on the spot and added to a big cache on the network server (if it doesn't already exist). When it comes to shipping, this server contains all the required binary shader code (not much use for GL though...)
Talking about it, yet another question that pops up, whats the best way for storing the permutations? I can't simply use an array / std::vector with the permutation-id as the key because of the sheer amount of possibilities. Is a std::map with the id as key more appropriate? Or an vector/list with a std::pair of key and permutation ptr?
As above, I've not had to deal with this yet, so I just use an array of permutation bitfields... so I haven't thought about this too much.
Inside your rendering code, how do you intend to select a set of actual shader programs (i.e. pick the right permutation) for each object? Do you just get the material's permutation bitfield, and then find the programs that match it exactly?
Do you have any concept of an "engine option" like I mentioned above?