Multiple render passes in GLSL: separate shaders or one shader?

Started by
4 comments, last by 21st Century Moose 11 years, 1 month ago

I've been playing around with doing multiple render passes in a fragment shader. I have FBOs with attached textures that I bind and then render to. On each pass, the previous rendered texture is available for reading in the fragment shader. I am doing three passes, all with the same shader. I simply update a uniform variable named "pass" between passes, and that variable is linked to if statements that contain what should be done for each pass.

It all works, but I'm wondering if there's a better way to do this. I read that others will use separate shaders altogether, and swap them between passes (by making a call to glLinkProgram, I assume). That seems like it would have more overhead unless they're already compiled and attached. Is this a good approach or am I overlooking something?

Advertisement

You can e.g. compile the same vertex shader 3 times and link it to 3 different fragment shaders to make 3 different program objects, and all of this can be done once-only at startup, meaning that calls to glLinkProgram at runtime are unnecessary (just make the appropriate glUseProgram call instead). Also look at GL_ARB_separate_shader_objects (standard in GL4.1+) for another method of doing this.

In relation to the latter, it's worth noting that GLSL's concept of a vertex shader and fragment shader linked into a "program object" is an artificial API construct that's actually at variance with how the underlying hardware really works, and that GLSL is very much the odd-one-out in having this concept; neither the older GL ARB assembly programs nor D3D shaders have it, and this isn't a problem at all outside of GLSL (pre 4.1).

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

I'm also wondering about this too. I have one generic vertex shader which I compile each time per pixel shader, which is the post-processor element. I try to stay away from extensions since they don't appear to be a standard part of OpenGL, and so not every graphics card would support that requiring a fallback. Is that correct?

I'm also wondering about this too. I have one generic vertex shader which I compile each time per pixel shader, which is the post-processor element. I try to stay away from extensions since they don't appear to be a standard part of OpenGL, and so not every graphics card would support that requiring a fallback. Is that correct?

You're showing some confusion over what is and isn't an extension here. Let's take the example of GL_ARB_separate_shader_objects here.

On hardware supporting OpenGL 4.0 or lower it's an extension. You need to check the extension string, get it's entry points, make sure that they're all present, etc.

On hardware supporting OpenGL 4.1 or higher it's not an extension. It's a standard part of the OpenGL feature set, so if you've got such hardware you can rely on it being available and just use it.

All of this is assuming that you don't have driver bugs, of course.

So the question comes down to this: which GL_VERSION is the minimum you want to support?

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

You can e.g. compile the same vertex shader 3 times and link it to 3 different fragment shaders to make 3 different program objects, and all of this can be done once-only at startup, meaning that calls to glLinkProgram at runtime are unnecessary (just make the appropriate glUseProgram call instead). Also look at GL_ARB_separate_shader_objects (standard in GL4.1+) for another method of doing this.

That's a good idea, but is it generally understood that there's a performance benefit to this? Let's say I want to draw to two separate framebuffer objects. When switching programs between them, I also have to send all the uniforms again. If I just use one shader with if(), I only have to update one uniform (whatever pass I'm on).

The if/else way seems much more convenient, but if it's possible there's a performance benefit to switching programs, that's worth consideration.

You can e.g. compile the same vertex shader 3 times and link it to 3 different fragment shaders to make 3 different program objects, and all of this can be done once-only at startup, meaning that calls to glLinkProgram at runtime are unnecessary (just make the appropriate glUseProgram call instead). Also look at GL_ARB_separate_shader_objects (standard in GL4.1+) for another method of doing this.

That's a good idea, but is it generally understood that there's a performance benefit to this? Let's say I want to draw to two separate framebuffer objects. When switching programs between them, I also have to send all the uniforms again. If I just use one shader with if(), I only have to update one uniform (whatever pass I'm on).

The if/else way seems much more convenient, but if it's possible there's a performance benefit to switching programs, that's worth consideration.

Depends on how good your GPU is at branching. But on balance, the overhead of switching shaders and sending uniforms is going to be incredibly small compared to that of shading > 1,000,000 pixels, so any performance issues from it are going to be down in the noise on any performance graph.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement