Performance: One Complex Shader vs. Multiple Simple Shaders

Hey!

I keep reading that it’s best to use as few master materials as possible and work with instances to reduce draw calls. I’m wondering if that’s always true or if using several simple shaders rather than one complex one can be beneficial as well.

For example: I’m currently using one shader for objects that require textures, another one for objects that use flat constant3vector colors. With a couple of Add and Multiply nodes I could combine those two in a single shader which allows me to specify what I want to use in the material instance. Is there any point in doing that when it means more calculations need to take place within the shader?

In other words: In terms of performance, would it be best to combine all materials that use the same shading model in a single master material, no matter how complex it gets?

Thanks in advance for your input!

There is no performance advantage once you have flipped a switch; it effectively becomes a new material then. The main gains there are in keeping consistency and making workflow easier. If it becomes more work to wire a bunch of complicated stuff into one material, it might be counter productive. There is also the shader compile time to consider. If everything in your game used one huge swiss army material, any change to that material will kick of a TON of shader compiles. Keeping things reasonably broken up by different types makes sense to keep thing from happening. It’s all about balancing consistency and complexity. There is really no rule as to when one or the other becomes more important.

Thanks for the reply, Ryan. It definitely makes sense to keep usability and compile time in mind. But I’m still a bit confused about the performance aspect of it. If I get the answer in [this thread][1] and the [documentation about material instances][2] right, an instance only compiles as a separate material if I use a static parameter. For my texture/color switch I only use add and multiply nodes, like this:

So all instances I create of this material should be combined at compile-time and only use a single draw call, right? Leaving usability out of the equation for a moment, does that mean it’s better than using one material for textures and another for vector parameter colors?

I imagine it doesn’t matter much for very simple materials like above. But if the math nodes keep piling up, could the performance be increased by splitting it into separate materials to get rid of all the adds and multiplies?

Sure if you are not flipping switches then there is some performance boost.

I was thinking you meant adding a crazy number of switches to support every possible shader situation.

“I imagine it doesn’t matter much for very simple materials like above. But if the math nodes keep piling up, could the performance be increased by splitting it into separate materials to get rid of all the adds and multiplies?”

Only if there are things hooked up and compiled that are not making a visual difference for a good number of the materials. Ie, you could do a bunch of expensive operations and then multiply them by a scalarparameter with a value of 0 before adding to a material. That would “turn off” the effect without requiring a new shader permutation, but it would also always run those instructions for all instances, since materials do not branch based on masking by dynamic parameters.

In your above example, emissive with strength of 0 will cost something, however small. In that case its probably so slow that it is not worth worrying about. If your emissive operation was really expensive then I would be concerned.

Since only few of my objects actually require textures, it’s probably best if I keep those two material variation separate then, at least for the more expensive shader networks.

Either way, I think I now got a decent understanding of how these things work, so thanks for taking the time to explain it in detail!