The Problem
If you make a basic layered material like this:
You would expect that the texture mask is sampled once. But its sampled for each layer because it’s a unique parameter. (I don’t talk about “Samplers” but texture lookups, you can put them to shared where they would at least not use additional “Samplers”)
You can see it in render doc it samples the texture for each mask again:
21: sample_indexable(texture2d)(float,float,float,float) r6.xyzw, v2.xyxx, t15.xyzw, s0
22: dp4 r1.w, r6.xyzw, cb4[1].xyzw
23: sample_indexable(texture2d)(float,float,float,float) r6.xyzw, v2.xyxx, t16.xyzw, s0
24: dp4 r3.w, r6.xyzw, cb4[2].xyzw
25: sample_indexable(texture2d)(float,float,float,float) r6.xyzw, v2.xyxx, t17.xyzw, s0
26: dp4 r4.w, r6.xyzw, cb4[3].xyzw
27: sample_indexable(texture2d)(float,float,float,float) r6.xyzw, v2.xyxx, t18.xyzw, s0
28: dp4 r5.w, r6.xyzw, cb4[4].xyzw
Personally I would like the option that it compiles the Texture Parameters like static ones. So that it can merge the sampling of the same ones together.
This is one of the main reason I don’t want to use the layered material system.
Quick way of seeing it
In Render Doc after you have selected the drawn mesh. Go into Pipeline State → Pixel Shader.
You can see all the resources of the drawcall. And will see that it sends duplicate resources for each Texture Parameter. (And will also sample them each)
Workarounds
Static Mask
You can use a static mask. Where you don’t expose the texture as a parameter. But this is kinda awkward. Because it’s no parameter the compiler can merge all the usages of the same Texture together.
Render Doc with static Texture:
21: sample_indexable(texture2d)(float,float,float,float) r6.xyzw, v2.xyxx, t15.xyzw, s0
Pass the mask along
You can pass the mask inside the material attributes. (in some channel you don’t need) And reuse it on higher layer blends.
Now we get the same result but it will only sample once:
Render Doc:
21: sample_indexable(texture2d)(float,float,float,float) r6.xyzw, v2.xyxx, t15.xyzw, s0
Improvements
Make it easier to share resources in the Material Layer Workflow. One of the best solutions would be to treat some parameters like global ones. (As if you would add a function to the material) Then you could check “use global parameter” and each parameter with the same name would be shared like usual. Then it would be easy to add a “mesh mask” parameter make it shared and so on each layer it would be reused. This would even improve the workflow because for baked masks I don’t need to go to each layer blend and fix it.
Are there actual Performance problems?
I don’t have done any performance testing. The impact should be pretty small on high end machines: It doesn’t add additional textures or samplers (if shared samplers is enabled on the parameter) but it does read the textures again and calculates interpolation. For example you have 5 layer masks using the same texture: so it needs to read 5 textures * 4 pixels * 4 channels. Because textures are interpolated. And to read 64 pixels again for no reason I don’t know… It hurts my soul. And is probably pretty bad for low - mid end hardware.
Docs
You can see from the official documentation that they reuse the textures. But they will be sampled multiple times. For no reason.
And the same material using the non layered workflow should be cheaper to render. Because you would just place one texture parameter for the masks but this is not possible in the layer workflow.