I am making my first steps in understanding technical side of shaders, so I have a question about texture packing and texture sampling performance.
Let’s say I have a hypothetical case: a material which needs Albedo with no alpha channel, Normal Map, Metalness, Roughness and AO maps.
I can go with traditional MRA texture packing, so I will have 3 texture samplers for 3 DXT1 textures (Albedo, Normal, MRA)
I can go with a little bit more tricky texture packing using 2 texture samples for 2 DXT5 textures (RGB(Albedo)+A(Roughness), R(Metalness)+GA(Normal)+B(AO) calculating the 3rd normal map channel in my shader using Normal Reconstruct Z function). Yeah I know, that my Normal map quality will be decreased in this case, but let’s imagine that it’s not the case where it’s crucial.
As far as I know, having alpha channel almost doubles the texture size, so seems like having 3 DXT1 textures will take less video memory than having 2 DXT5 textures.
The deal is I don’t know exactly how much performance does Sample Texture 2D node cost. I heard it’s one of the most expensive nodes. I tried to read some technical articles about texture sampling process, but it’s just not my level yet.
Can anyone kindly explain me in which cases reducing the number of Texture Samples may be better than saving video memory?
The very short version is: Sampling fewer times is faster than using less texture memory when it is.
It’s dependent on what hardware you’re using. It’s dependent on how complex the shader is. It’s dependent on what the geometry the shader is on looks like. It’s dependent on how much stuff is visible in the scene.
However there is one place where fewer samples is usually better than total texture memory usage. When your shader is sampling from many textures already. How many is too many? See above… it depends. But if you’re sampling more than 16 times, usually sampling less is faster.
Beyond that, larger textures are an issue and become slower than sampling more times if you’re running out of GPU memory, or memory bandwidth. When does that happen? See above… it depends.
Basically the only way to know which one is faster is to try both. But most of the time you probably won’t be able to accurately measure the difference because it’s unlikely to be a major factor.
Thank you for your answer. Everything seems clear.
One more question to ask.
Is using two channel Normal Maps with Normal Reconstruct Z function really have a sense?
Seems like this trick may save some memory and a sample in case of material that requires only Albedo, Normal map and Roughness. So the Roughness can be stored to the one of the normal maps channel.
But as far as I don’t really understand the complexity of the Normal Reconstruct Z function, I am afraid that using it may significantly slow down my shader.
Also as far as I am new in making shaders. Is there an easy way to check my shader’s complexity/performance in Unity? Something like the Stats window in Unreal Engine material Editor? Or may be you’d kindly give me a direction what to learn to be able to profile my shader in a more efficient way?
Modern GPUs, even relatively low end mobile GPUs, have way more ALU (arithmetic logic units, aka math hardware) perf than anything else. Trading less data for more math makes sense in almost all cases at this point. Reconstructing the Z from a normal map’s X and Y costs about 3 instructions on the GPU. That’s effectively nothing. You could add 50 instructions to a shader today and have a hard time measuring the difference in performance before and after.
Beyond that, it’s usually higher quality than storing all 3 channels. Even if you stuck with DXT1 to store a normal map, storing just the x and y and reconstructing the z will usually end up looking a little better because it’s a little easier, and thus more accurate, to compress just two changing colors than 3. And the default “DXTn” (DXT5 with X in A and Y in G) format used by Unity is much higher quality than that. Depending on the normal map, even if you use the extra two channels for roughness and AO, the resulting reconstructed normal can still end up looking a bit better than a basic DXT1.
As for profiling, the best way to start off with is enable GPU profiling in Unity. By default all the numbers you see, even the “rendering” ones, are all CPU time. After that you’ll get real GPU render time stats, and can even see per-draw times. The next thing I’d recommend is don’t think about it too much. Hyper optimizing shaders in the way you expect it to be faster is usually a waste of time, because you’ll probably be wrong about what’ll make them faster. Write the shader code in a way that makes it easy for you to understand how things are working, then try optimizations and see if they make any difference.