Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenGL 4.1 GL_ARB_separate_program_objects usefulness

Tags:

opengl

I have been reading this OpenGL4.1 new features review.I don't really understand the idea behind GL_ARB_separate_program_objects usage , at least based on how the post author puts it:

It allows to independently use shader stages without changing others shader stages. I see two mains reasons for it: Direct3D, Cg and even the old OpenGL ARB program does it but more importantly it brings some software design flexibilities allowing to see the graphics pipeline at a lower granularity. For example, my best enemy the VAO, is a container object that links buffer data, vertex layout data and GLSL program input data. Without a dedicated software design, this means that when I change the material of an object (a new fragment shader), I need different VAO... It's fortunately possible to keep the same VAO and only change the program by defining a convention on how to communicate between the C++ program and the GLSL program. It works well even if some drawbacks remains.

Now ,this line :

For example, my best enemy the VAO, is a container object that links buffer data, vertex layout data and GLSL program input data.Without a dedicated software design, this means that when I change the material of an object (a new fragment shader), I need different VAO...

makes me wonder.In my OpenGL programs I use VAO objects and I can switch between different shader programs without doing any change to VAO itself.So ,have I misunderstood the whole idea? Maybe he means we can switch shaders for the same program without re-linking ?

like image 920
Michael IV Avatar asked Dec 02 '22 23:12

Michael IV


2 Answers

I'm breaking this answer up into multiple parts.

What the purpose of ARB_separate_shader_objects is

The purpose of this functionality is to be able to easily mix-and-match between vertex/fragment/geometry/tessellation shaders.

Currently, you have to link all shader stages into one monolithic program. So I could be using the same vertex shader code with two different fragment shaders. But this results in two different programs.

Each program has its own set of uniforms and other state. Which means that if I want to change some uniform data in the vertex shader, I have to change it in both programs. I have to use glGetUniformLocation on each (since they could have different locations). I then have to set the value on each one individually.

That's a big pain, and it's highly unnecessary. With separate shaders, you don't have to. You have a program that contains just the vertex shader, and two programs that contain the two fragment shaders. Changing vertex shader uniforms doesn't require two glGetUniformLocation calls. Indeed, it's easier to cache the data, since there's only one vertex shader.

Also, it deals with the combinatorial explosion of shader combinations.

Let's say you have a vertex shader that does simple rigid transformations: it takes a model-to-camera matrix and a camera-to-clip matrix. Maybe a matrix for normals too. And you have a fragment shader that will sample from some texture, do some lighting computations based on the normal, and return a color.

Now let's say you add another fragment shader that takes extra lighting and material parameters. It doesn't have any new inputs from the vertex shaders (no new texture coordinates or anything), just new uniforms. Maybe it's for projective lighting, which the vertex shader isn't involved with. Whatever.

Now let's say we add a new vertex shader that does vertex weighted skinning. It provides the same outputs as the old vertex shader, but it has a bunch of uniforms and input weights for skinning.

That gives us 2 vertex shaders and 2 fragment shaders. A total of 4 program combinations.

What happens when we add 2 more compatible fragment shaders? We get 8 combinations. If we have 3 vertex and 10 fragment shaders, we have 30 total program combinations.

With separate shaders, 3 vertex and 10 fragment shaders needs 30 program pipeline objects, but only 13 program objects. That's over 50% fewer program objects than the non-separate case.

Why the quoted text is wrong

Now ,this line [...] makes me wonder.

It should make you wonder; it's wrong in several ways. For example:

the VAO, is a container object that links buffer data, vertex layout data and GLSL program input data.

No, it does not. It ties buffer objects that provide vertex data to the vertex formats for that data. And it specifies which vertex attribute indices that goes to. But how tightly coupled this is to "GLSL program input data" is entirely up to you.

Without a dedicated software design, this means that when I change the material of an object (a new fragment shader), I need different VAO...

Unless this line equates "a dedicated software design" with "reasonable programming practice", this is pure nonsense.

Here's what I mean. You'll see example code online that does things like this when they set up their vertex data:

glBindBuffer(GL_ARRAY_BUFFER, buffer_object);
glEnableVertexAttribArray(glGetAttribLocation(prog, "position"));
glVertexAttribPointer(glGetAttribLocation(prog, "position"), ...);

There is a technical term for this: terrible code. The only reason to do this is if the shader specified by prog is somehow not under your direct control. And if that's the case... how do you know that prog has an attribute named "position" at all?

Reasonable programming practice for shaders is to use conventions. That's how you know prog has an attribute named "position". But if you know that every program is going to have an attribute named "position", why not take it one step further? When it comes time to link a program, do this:

GLuint prog = glCreateProgram();
glAttachShader(prog, ...); //Repeat as needed.
glBindAttribLocation(prog, 0, "position");

After all, you know that this program must have an attribute named "position"; you're going to assume that when you get it's location later. So cut out the middle man and tell OpenGL what location to use.

This way, you don't have to use glGetAttribLocation; just use 0 when you mean "position".

Even if prog doesn't have an attribute named "position", this will still link successfully. OpenGL doesn't mind if you bind attribute locations that don't exist. So you can just apply a series of glBindAttribLocation calls to every program you create, without problems. Indeed, you can have multiple conventions for your attribute names, and as long as you stick to one set or the other, you'll be fine.

Even better, stick it in the shader and don't bother with the glBindAttribLocation solution at all:

#version 330
layout(location = 0) in vec4 position;

In short: always use conventions for your attribute locations. If you see glGetAttribLocation in a program, consider that a code smell. That way, you can use any VAO for any program, since the VAO is simply written against the convention.

I don't see how having a convention equates to "dedicated software design", but hey, I didn't write that line either.

like image 110
Nicol Bolas Avatar answered Jan 01 '23 18:01

Nicol Bolas


I can switch between different shader programs

Yes, but you have to replace whole programs altogether. Separate shader objects allow you to replace only one stage (e.g. only vertex shader).

If you have for example N vertex shaders and M vertex shaders, using conventional linking you would have N * M program objects (to cover all posible combinations). Using separate shader objects, they are separated from each other, and thus you need to keep only N + M shader objects. That's a significant improvement in complex scenarios.

like image 42
Bartek Banachewicz Avatar answered Jan 01 '23 19:01

Bartek Banachewicz