Sign in to follow this  
synthetix

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

Recommended Posts

synthetix    192

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?

Share this post


Link to post
Share on other sites
mhagain    13430

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).

Edited by mhagain

Share this post


Link to post
Share on other sites
Vincent_M    969

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?

Share this post


Link to post
Share on other sites
mhagain    13430

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?

Share this post


Link to post
Share on other sites
synthetix    192

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.

Share this post


Link to post
Share on other sites
mhagain    13430

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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this