You Convey To Know : Uber-Shaders: Development Or Optimization
Let's but imagine that you lot guide keep an uber-shader, in addition to an uber-interface for it. The uber-shader tin exercise most 100 cool shading tricks, in addition to is laid amongst a struct similar this:
struct shader {
int tex_mode;
int tex_ref;
int want_shadows;
int want_emissive_tex;
int lit_ref;
...
};
A unmarried component "setup" takes a shader struct in addition to sets all of the OpenGL parameters to move inward happen. This component knows what the GLSL code looks similar in addition to does the correct thing.This pattern has been a win for us amongst X-Plane because:
- The encapsulated setup component tin bargain amongst hw-specific issues. For example, if you lot tin gauge the shader state asking using the fixed-function pipeline on quondam hardware, this gets hidden inward "setup" in addition to customer code doesn't care.
- Since you lot guide keep access to all state at the same time, you lot tin exercise things similar selection from a laid of customized shaders based on state combinations. (In other words, you lot tin exercise a large number of highly optimized shaders for specific cases.)
What exercise you lot exercise if you lot require to modify 1 parameter of the shader? The naive response is:
reset(&shader);
shader.tex_ref=something;
setup(&shader);
In other words, you lot tear downwards OpenGL state, modify the request, in addition to then construct it upwards again.Well, that seems inefficient, doesn't it? What if at that spot is a fast path? (For example, if all you lot are changing is polygon offset, all you lot genuinely require to exercise is telephone band glPolygonOffset.
One extension to the uber-shader interface is a serial of 'evolution' APIs that modify a unmarried parameter, e.g.
change_tex(&shader,something);
Naively this is equivalent to the reset/change/setup code above, but the implementation mightiness exercise something clever, similar only rebind the texture unit of measurement but exit the shader object alone.
Is this a win? It seems reasonable to promise so. For example, if the state existence changed is effectively a uniform passed to the GPU (or GPU state non related to shading), nosotros mightiness live on a lot closer to minimal state change.
Optimization
What happens when your shader gets genuinely large in addition to complex? One occupation is that the logic inward customer code that sets upwards the shader gets large in addition to complex. For example: if the source texture has no alpha channel in addition to at that spot is no overlay texture, you lot tin disable alpha blending. Disabling alpha blending mightiness live on a huge functioning win - peradventure your app is bottlenecked on raster ops. But having this logic everywhere inward the customer code isn't adept - it way that you're non certain that you lot guide keep ideal optimization at every shader point.
One way approximately this is to write an optimization component every bit component of the uber-shader code, e.g.
optimize(&shader);
The optimizer goes through all the requested shader state in addition to "harmonizes" it. Because the optimizer is component of the shader setup code, the cognition of how the shader genuinely plant is instantly isolated to the 1 house inward the app that should know such things. Now you lot tin set fairly complex logic inward house to honor fast paths in addition to guide keep them every time.
Clash of Optimizations
The occupation amongst optimization vs. development is they don't play prissy together. The development functions assume that you lot know the origin state of your shader earlier you lot modify it. But the optimization API mightiness guide keep changed your shader inward an unexpected way. For example:
- You laid upwards a shader amongst a texture in addition to blending.
- You run the optimizer on it. The optimizer turns off blending because the texture doesn't genuinely guide keep an alpha channel.
- You run the development API to modify to a texture that does guide keep an alpha channel.
My solution to this is a fleck unsmooth but goes similar this:
- There are no development APIs.
- Changing state requires changing the master copy shader in addition to re-optimizing.
- Inside the shader, all state changes are lazy in addition to tracked (e.g. nosotros only modify GL state if nosotros genuinely require to).
- We never reset state piece inward the midpoint of shader ops.
- We calculate the optimal shader for filling.
- When nosotros instruct to modify state, the "reset" of the shader genuinely does nothing.
- We calculate a novel optimal shader.
- When nosotros instruct to laid upwards that novel shader, almost all of the GL state modify is a no-op. In particular, if nosotros could guide keep "evolved" (E.g. genuinely nosotros only require to modify the texture) that's all nosotros volition genuinely change.