5
\$\begingroup\$

Sorry if this question has been asked in the past, but searching Google and here didn't yield relevant results, so here goes.

I'm working on a fragment shader that implements both conditional/boolean diffuse and bump mapping (that is to say, you don't need a diffuse texture or a normals texture, and if they're not present, they're simply changed to default values).

My current solution is to use a uniform float to say "mix amount". For example, computing the diffuse texel works as:

// Compute diffuse amount scaled by vCol
// If no texture is present (mDif = 0.0), then DiffuseTexel = vCol
// kT[0] is the diffuse texture
// vTex is the texture co-ordinates
// mDif is the uniform float containing the mix amount (either 0.0 or 1.0)
vec4 DiffuseTexel = vCol*mix(vec4(1.0), texture2D(kT[0], vTex), mDif);

While that works great and all, I was wondering if there's a better way of doing this, as I will never have any use for in-between values for funky effects. I know that perhaps the best solution is to simply write separate shaders for mDif=0.0 and mDif=1.0, but I'd like a more elegant solution than splicing shaders before compiling or writing multiple shader files and keeping each one updated.

Any ideas are greatly appreciated. =)

\$\endgroup\$
4
  • \$\begingroup\$ I don't see anything wrong with this, it's probably way better than any branching, but I'm no shader expert so I'll let the professionals talk. \$\endgroup\$ Jun 27, 2013 at 22:45
  • \$\begingroup\$ @LaurentCouvidou: Branches will be compiled into conditional operations so should be as fast or faster than multiplying by 1 or 0... on some hardware with some drivers at some optimization levels. :) \$\endgroup\$ Jun 27, 2013 at 23:50
  • \$\begingroup\$ @LaurentCouvidou What's wrong with this (generally) is that mix() will fail in case specials come into play (Inf, NaN). Also, it might produce unexpected results when the range of values is high and the data type is narrow (e.g. half). It would be much nicer to have select(x, y, cond) but generally it is ok to use the ternary operator. The compiler wont be able to optimize it though (as the C standard guarantees that only one of the branches gets evaluated--the other could be out of bounds access, division by zero, have side effects or just be expensive). \$\endgroup\$
    – the swine
    May 23, 2017 at 18:48
  • \$\begingroup\$ This gets a bit confusing on a GPU as both branches are always executed. But the threads in a warp are masked according to their condition values so that the possible exceptions (e.g. OOB) that would otherwise be raised are avoided. Still, the threads need to "step through" the masked code and time is spent. \$\endgroup\$
    – the swine
    May 23, 2017 at 18:52

1 Answer 1

4
\$\begingroup\$

Branching should be sufficiently fast on modern cards that you don't need to worry about it, and you can always profile it if you think there's a bottleneck. Branching will also make for somewhat more readable code, since your intent is much clearer, and the code you're currently using does imply the possibility of a "25% mix" between the two, for example.

Your current example also causes the texture to be sampled, even if it would be the "default" value, which you could avoid with an actual branch, potentially.

But there's really nothing horrible with your current solution, and if you have a need to support cards/versions that are old enough that branching isn't very fast, it's probably better.

\$\endgroup\$
3
  • \$\begingroup\$ Oh, I see. I thought that branches/conditionals in general were still pretty slow. Since that isn't the case, though, I may as well write the two versions into the shader and have the one I'll use enabled with a boolean #define during compilation. Thanks a lot for this information. =) \$\endgroup\$ Jun 28, 2013 at 0:02
  • 6
    \$\begingroup\$ @RubenNunez: non-uniform branches are a problem but uniform ones should be fine. Uniform branches are, essentially, those that only depend on constant and uniform variables and not on per-vertex/per-fragment data. That is, all shader executions follow a uniform code flow. The slowness of branching on GPUs is that in the non-uniform case, every shader ends up executing both branches but throwing away the unused case. When it's uniform, or the branched-to code is very short/cheap, things work much better. \$\endgroup\$ Jun 28, 2013 at 0:08
  • \$\begingroup\$ Oh, right. I remember reading something like that. I guess I must've forgotten it x_x. Thanks for the reminder, though. =D \$\endgroup\$ Jun 28, 2013 at 0:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .