I have tried to create a fully procedural material for planet-like surfaces, based on a noise node. Yeah, I know it is heavy, but I can afford it in that kind of scene I have in mind.
After a bunch of iterations i noticed, that my material grown up to 700+ instructions, having just a noise and blending of three layers of material functions, that were very lightweight on each own (30-50 instructions each).
So I decided to figure out what’s going on. I created one material with a noise and couple of Lerps, and one with with a noise and just single Lerp:
The Heavy-one:
The Light-one:
As you can see the Heavy-one have almost 300 more instructions for 4 Lerps. I examined generated shader code, and it looks like each input of Material Attributes node get computed separately.
And each execution path ends up with its own call to Noise node function. So that Noise operation gets executed as many times, as many Material Attributes inputs are connected to it.
In complicated networks, and those which use layered approach it may lead to huge overhead, while the value computed is always the same across each individual pixel.
“That could not be that bad…”, I thought. Documentation says, that shaders are run through optimizer, before they are passed to GPU.
So I placed both of these shaders on a two big walls on a test map, so I could get full screen covered with that material and packaged a version to see how it runs.
Here is the results (Rendered at Epic quality, 2560x1440, GeForce GTX 760):
Even in packaged version this two shaders differ a lot. I get 20-22 ms in GPU counter when none of this walls is visible, so real difference is as between 2 and 9 ms
So, may the shader generator receive some optimisations of such cases?