Shader optimisation

This reduces the runtime of calls to `Shader::stateHash()` and `Shader::state()` by ~35% in benchmarks using example production shaders. It's hard to say what is enough optimisation to close an open-ended ticket like #760, but I'd like to think that this on top of the significant optimisations already implemented in #771 and #780 should be sufficient for now. When we're using IPR in anger we can do some broader profiling of the process as a whole and let that guide future optimisation efforts, rather than continue to work on this one component in isolation - does that sound reasonable?