uber-shaders

Started by
5 comments, last by jollyjeffers 16 years, 8 months ago
Are uber shaders where you program several rendering effects into one shader and enable/disable them by switches that vary once per frame? For example, in the shader you could have flags: if( bLighting ) // do lighting stuff if( bTexture ) // do texturing stuff if( bFogEnabled ) // do fog Is this the essence of an uber shader? Are they generally recommended? I think I can design my application to use only a few general uber shaders, which saves me from having to maintain a lot. It seems like the old fixed pipeline was like an uber shader--you could enable/disable stuff by controlling the render states (basically switches).
-----Quat
Advertisement
you're sort of there.

Uber shaders are more lengthy in run-time form than compile-time. What you're describing is more permutation and combinatorial-explosion territory - write one huge chunk of generalised code and let the compiler boil it down into many smaller and more specialised shaders.

An Uber shader might include code for several lighting models and pick which one according to a value fetched from a texture (or per-vertex attribute). It might perform distance/projection calculations and scale the lighting model complexity according to contribution to the image. Similar LOD schemes for dynamically switching from 'relief mapping' to 'parallax mapping' to 'normal mapping' according to Mip-Map level and so on (ATI's 'Toyshop' demo does this iirc).

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

Right, I left out the dynamic branching kind of stuff for simplicity in the question.

I read in the ATI programming guide that for dynamic branching, a coherency of at least 64 pixels/vertices is recommended. Does this mean chunks of 64 pixels/vertices should follow the same branch of logic?
-----Quat
Quote:Original post by Quat
I read in the ATI programming guide that for dynamic branching, a coherency of at least 64 pixels/vertices is recommended. Does this mean chunks of 64 pixels/vertices should follow the same branch of logic?
Yes, that's the right idea. Bare in mind that the value of 64 pixels might change from vendor to vendor and architecture to architecture - bare it in mind, but don't treat it as some fixed limit.

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

Quote:Are they generally recommended? I think I can design my application to use only a few general uber shaders, which saves me from having to maintain a lot.

It depends on your target hardware platform. If you only target latest graphics hardware than they might be a viable option. If you want to target hardware that does not have dynamic branches support in hardware, you might think about it. I would just time the results you get in PIX or any other tool to measure performance and think about it.
The obvious performance hit is that both sides of the branches are executed if the best case condition is not met. Think about several or all your branches that do not hit this condition. This pretty much limits what you want to do with them.

On the other side having lots of different shaders that just differ in minor details kills performance because shader switching gets really expensive. So you might work in those boundaries.

This is a problem that can be only solved based on your target group, the game genre and your personal preferences :-) ... and the level of interaction with the artists. So think of it also as a workflow problem. You want to involve your artists to get max benefit of all the freedom your solution will give them.
Sorry for the interruption. Just to make things more clear for myself, does the following code snippet falls into this uber-shader category? Plus, is the branch dynamic or static? I think the compiler optimizes the branch away, doesn't it? Is this a recommended design?

struct VS_INPUT {  /*....*/};struct VS_OUTPUT {  /*....*/};VS_OUTPUT vs_main( VS_INPUT Input, uniform bool bEnableSpecular ) {  // Calculate the diffuse lighting  if ( bEnableSpecular ) {    // Calculate the specular term  }};struct PS_INPUT{   /*....*/};float4 ps_main( PS_INPUT Input ) : COLOR0 {  /*....*/}technique DiffuseOnly {    pass p0 {        vertexshader = compile vs_3_0 vs_main( false );        pixelshader  = compile ps_3_0 vs_main();    }}technique DiffuseSpecular {    pass p0 {        vertexshader = compile vs_3_0 vs_main( true );        pixelshader  = compile ps_3_0 ps_main();    }}
You're using the uniform keyword which implies it is static branching. The compiler will evaluate it and emit different code accordingly.

Try running your .fx file against fxc.exe. I forget the exact parameters, but you can get a colour-coded HTML output of the assembly generated. It's fairly easy to read the basic details from and it should be fairly obvious what it's doing in this case [smile]

hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This topic is closed to new replies.

Advertisement