3
\$\begingroup\$

I am trying to find a way to organize my evergrowing number of shader techniques/functions (I am coding in sm_3.0). One way is to do this:

float4 PS_Crossroads(PS_INPUT input, uniform bool left_right) : COLOR0
{
    if (left_right)
        GoLeft();
    else
        GoRight();
    ...
}

technique LEFT
{
    pass Pass1
    {
        VertexShader = compile vs_3_0 VertSh();
        PixelShader = compile ps_3_0 PS_Crossroads(true);
    }
}

technique RIGHT
{
    pass Pass1
    {
        VertexShader = compile vs_3_0 VertSh();
        PixelShader = compile ps_3_0 PS_Crossroads(false);
    }
}

My question is will this cause a branch at runtime or will the compiler be smart enough to split it into two separate techniques?

Thanks.

P.S. Ok I won't post this as answer, because we already have an answer by someone much more experienced than me, but from my humble tests (on different PCs) and my humble Google research, this approach (using uniform constants) is efficient and indeed forces the compiler to create different shader versions for each branch, removing flow controll completely from the finall compiled shader. I tested the compiled shader codes with Nvidia NSight.

\$\endgroup\$
2
\$\begingroup\$

In HLSL, creating separate techniques that pass different compile-time values into a shader function will definitely generate efficient code. Optimizing away control flow due to compile-time constants is implemented in Microsoft's HLSL bytecode compiler, which means it doesn't matter which GPU or drivers you have; the optimization is already done by the time the shader gets to the driver's shader compiler. (Unless you disable optimization in the HLSL compiler - in which case, the optimization may very well still be done at the driver level.)

In GLSL, where there is no IHV-independent compiler and bytecode language, things are less certain. Shader compilers are generally pretty aggressive about optimizing away compile-time constants in any form, and a good mature GLSL compiler will have no trouble with this. But there are some platforms/drivers where the GLSL compiler doesn't do a good job, which led Unity developer Aras Pranckevičius to develop a standalone GLSL optimizer.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ but what I want to ask is, dones't the front end compiler have some overhead itself? what I mean is that you pass uniforms after the compilation at least this is the case with openGL? so if that was the case wouldn't it need to kick in the optimization after determining the uniforms value? \$\endgroup\$ – concept3d Nov 30 '13 at 21:25
  • \$\begingroup\$ which sounds more like branch prediction to me. \$\endgroup\$ – concept3d Nov 30 '13 at 21:33
  • \$\begingroup\$ @concept3d I don't think anyone here is talking about setting uniforms at runtime. If you look at the OP's code, each technique is passing a different, compile-time constant value for the uniform parameter. That causes the compiler to generate two copies of the code, each optimized for its own value of the uniform parameter. It's just like using #ifs, but with nicer syntax. \$\endgroup\$ – Nathan Reed Nov 30 '13 at 23:51
  • \$\begingroup\$ yes I noticed that after I re-read the question. And I think it's is lame that GLSL doesn't have this feature and I have been using #if for a while now and can't say it's cleaner. \$\endgroup\$ – concept3d Dec 1 '13 at 4:59
  • \$\begingroup\$ I must set your answer as the right one, as even concept3d has agreed with it. As I mentioned in the question, my humble tests showed that it indeed generates different compiled shaders and I have changed my shader code to use this feature already. However, I am still struggling. I now have one big MAIN function that accepts many a uniform parameter and a number of techniques that call MAIN with various combinations of these parameters. The problem is, whenever I need to add another bool parameter, my technique count essentially doubles :). I wonder if there is a way around it? \$\endgroup\$ – cubrman Dec 4 '13 at 8:16
3
\$\begingroup\$

You can't be sure

Well, depending on the compiler and the GPU, because uniform variables only change per pass the GPU will more likely always take that branch for each pass, but unless you use a profiler you will never know and can't be sure for every driver/GPU.

[Edit]Based on Nathan Reed comment, the HLSL compiler will optimize constant uniforms because its optimization can happen at Microsoft front-end compiler, before being passed to the GPU driver compiler. Though this is not the always case with GLSL.

Alternative technique

But anyway people don't usually organize shaders this way. My recommendation is to use compile time tokens and do conditional compilations, where you have one big shader that calculates different things, then you compile different versions of it, without rewriting it, this way you will be 100% sure there is no branching overhead. This technique is also called uber-shader which is covered in this answer.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I am marking your post as answer as you explained every part of my qestion. \$\endgroup\$ – cubrman Nov 21 '13 at 14:13
  • \$\begingroup\$ @cubrman if this didn't solve the problem or you want extra explanation please you can ask for more info. \$\endgroup\$ – concept3d Nov 21 '13 at 14:15
  • \$\begingroup\$ Actually yes :). I can't quite find how they change the flags at the CPU dynamicaly. I need to organize my shader code in such a way that I can tell at the CPU level what path to use for the current mesh. \$\endgroup\$ – cubrman Nov 21 '13 at 14:19
  • \$\begingroup\$ I explained the technique in the linked answer. But what really need to do is sth like preprocessor directives. and when you compile the shader (during your application execution) you just pre-pend your shader source code with some token. \$\endgroup\$ – concept3d Nov 21 '13 at 14:23
  • 1
    \$\begingroup\$ @cubrman The other main option is just to pass #defines to the shader source before compiling it, e.g. #define TYPE 15, which will be 00001111 in binary, where the bits are flags for various techniques to use or not use (e.g. normal mapping, texturing, etc.). And in the shader code you do, for example, #if (TYPE & 8) == 0 to test if the fourth flag is set. Then you compile the shader once for every TYPE combination and bind the correct shader before rendering depending on the type of the mesh (for that you would have to store some kind of type information during loading of the data) \$\endgroup\$ – TravisG Nov 21 '13 at 14:27
0
\$\begingroup\$

This is not required by the specification, but most public drivers do implement this in some fashion.

The only way to be sure your specific shader is optimised this way is, unfortunately, profiling.

See also: https://stackoverflow.com/questions/4176247/shader-branching-question for more information on such branching.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.