Some drivers compile special versions of shaders when uniforms have special values. Consider for example this shader:
uniform float x;
in vec4 i;
out vec4 o;
void main() {
o = i * x;
}
This shader contains a single multiplication. But when x is 0 or 1, the multiplication could be optimized away.
In general, this is a good idea, as it makes the shader run faster. The problem with this is that it takes time to do it. And uniforms usually are changed in the middle of drawing something, I want this to be fast.
What’s the difference between these?, they seem to solve the same issue in different ways.
It’s not quite the same.
Uniform buffer objects are perfect for global state like matrices, light parameters and so on. They can be shared between multiple shaders, so you can put all the state in a buffer object and when changing shader you just need to bind a single buffer object instead of setting all the uniforms again.
They can also be used for material parameters. You would probably use at least two uniform buffer objects, one for material specific parameters, one for global state.
Program state objects don’t solve this issue, because you would have to change the values in every single program state object every time you change a matrix.
On the other hand, program state objects are perfect for material parameters, textures and so on. These are properties that don’t change while rendering. You would have a program state object for each “material” (that is, a shader/texture/UBO combination), and this never changes.
Also, uniform buffer objects are not really usable for textures. So at least sampler uniforms would have to be set separately.
This is a mechanism on top of uniform buffer objects. Without it, each time you switch material, you have to:
- activate the shader
- bind all uniform buffer objects
- bind all textures
- set all remaining uniforms that are not in buffer objects
Instead, you just bind a single program state object that contains all necessary information, already nicely preprocessed, perhaps even with shaders optimized for the particular uniforms.
Point 4) would basically be the “old” glUniform. This is the only thing that overlaps a bit with uniform buffer objects, so you could deprecated it. But I think retaining the old glUniform mechanism in addition to buffer objects could be quite useful for compiler optimizations, because these uniforms could be effectively treated as constants. This is something some vendors already do now, but “hidden” and with the side effect that a uniform change may take an unexpected amount of time.
With program state objects the problem would vanish, because a uniform change at runtime is no longer necessary. Just put everything that changes often in a buffer object. Everything else (e.g. material constants) remains a normal uniform and can be used by the compiler to optimize, but this time there is another object in between, so the compiler needs to do this stuff only once and store the result in the state object.
In this regard, the program state objects are exactly the same as vertex array objects: A cache that prevents the driver from having to process everything again and again. Nothing more, nothing less.