Why is the shader compiler not smart enough to know not to load unused texture channels into video memory? Or is it mostly a hard disk storage issue of redundant channels in monochrome textures?
Is there a way to profile in the editor to measure actual performance difference of workflows using packed vs non packed materials?
“Why is the shader compiler not smart enough to know not to load unused texture channels into video memory? Or is it mostly a hard disk storage issue of redundant channels in monochrome textures?” Neither. Texture samples arent cheap, channel packing is a hack to get around it.
I still pack textures. Especially if it can make it over the lip of one-less-sampler (with associated instructions).
You can do a lot with a dozen (or less!) instructions from what’s freed up from not using a texture/sampler.
besides the issue of texture lookups, the fewer texture files you have the smaller your project will be. That makes everything go faster.
Thanks, this makes a bit more sense - Even if the compiled code could be optimized to not read unused texture channels, you would still have the overhead of constructing each sampler.
You can use a static-switch parameter, but you have to be careful with how you use those else you could make too many path-permutations and create a great many shaders from just one graph.
Generally speaking, less is more. Just having a node that is used has overhead, so even if you sample from a texture 1 time, just having it exist in the material has a cost in and of itself. This is why packing is valuable as you can carry that cost of the sampler across x-samples.
I still use it, mainly for stuff like:
Basically getting masks for parts of the texture instead of the whole thing, then using this cropping tool to apply them to the right part of the material for effects/glow.
This can save hundreds of mb’s on bigger projects.
If a part of the material is always going to be glowing and reflect no light, we can use it’s color value as an emissive ID mask. For simplicity, we can treat each channel as a bit and get 2^3=8 IDs (2^4=16 with alpha and more with other textures). The ID can then be used to get the emission value from a much smaller emission texture; it can also be used with parameters or procedural effects, too.
As seen in the first picture, the emission texture is only 8 pixels (8x1), which is super small; it’s basically just a palette texture. Plus, this works without any additional setup on both the texture side and the material side: you just set your diffuse and emission textures and you’re done.
Others have given good answers, too, I’ll just chime in with mine.
First, texture sample operations have a cost. The hardware may pay the same cost (in cycles) for sampling and filtering a single-channel texture, as it does for a 3- or 4-channel texture. Might as well get more data with that read operation!
Separately, the graphics card can’t know whether you’ll always be using that particular texture with that particular shader. From the graphics cards point of view, you can bind any texture object to any sampler (more or less) and clearly you have some shaders that use all channels of some texture. There’s no way to pinky-promise to never bind a particular texture to that particular sampler.
Finally, texture compression generally saves more memory than trying to just trim out a single channel; the reason this is so is that material properties generally vary together, and as long as there is some correlation between each channel, the compressors can do a good job.
Packing two or three channels into a texture will give you higher runtime performance, AND less memory usage, than trying to use two or three separate texture maps and samplers.