Conditionals in shaders


I am pretty new to shader programming, and I have read that it is not great to use conditionals in them to have the shader perform different tasks depending on some outside state. Is that true? In most cases or just some?

For example, it seems to me like switching shader programs would be a bigger deal than putting an IF-THEN in the shader, where you are branching on a something for example like

if (mode == GRADIENT_MODE) {
// set vertex color according to gradient
else if (mode == PAINT_MODE) {
// set vertex color to current color

This is just an example and there may be better ways to switch between gradient and flat coloring, but in general, is it bad to have a shader with statements like this? I was just thinking it would be easy to have a more general-purpose vertex shader, where the function is determined by some kind of uniform mode flag.

I am going to try testing it out different ways, but I thought I would ask if it’s better in general to switch between a bunch of different shader programs during a render frame, or to have just one or two shaders which branch inside to perform different functions?


Conditionals are bad only when different fragments can take different paths; particularly fragments near each other. Your example will not take much of a performance hit but something like

if (frag is odd)
   set colour to white
   set colour to black

would be very bad

Nice question. I’m quite in favour of injecting #define values and creating permutations of shaders (within reason of course). However, I haven’t ever noticed much performance difference doing exactly what you describe.

In regard to the more dynamic branching, I’ve also tried merging branches by combining operations into a single statement. Such as:
colour = vec4(vec3(i%2), 1.0);
but again I don’t notice any significant difference. Am I right in assuming that the recent cards and compilers are very good at merging those relatively small if-statements into code that doesn’t require complex thread rescheduling?

Could it also be that (or a combination of) the scheduler is smart enough to recognise it needs to group every second fragment and, as long as there are enough, shouldn’t take any extra time?

I’d love to see a list of tricky things like that the driver devs implement. I be they’re locked in a safe somewhere.

Branching in modern GPUs is fairly safe, performance-wise, as opposed to what it used to be like in pre-OpenGL3 hardware. Things like,

 value = cond ? val1 : val2

tend to use conditional assignment, a fairly lightweight operation if both val1 and val2 are single values or easily evaluated. More complicated conditions will simply execute both paths and only take one of the results if the uniformity of the conditional can’t be determined. Changing shaders is also a very expensive operation, especially if you’re continually doing this with small batches. It’s important to weigh the cost of changing the shader vs. using a uniform.

For example, if you’re just changing a color or simple value, keep the same shader. But if you’re toggling complex materials or lighting on or off, you may want to try both approaches on a typical scene and see which runs faster. Different hardware and driver behaviour makes this fairly difficult to predict, and I’ve been surprised a few times.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.