PDA

View Full Version : MRTs: does a shader need to write to all of them?



CortS
07-20-2010, 01:13 PM
I'm seeing strange behavior while trying to set up multiple render targets.

I'm setting up an FBO with three color attachments, rendering a test object, and then combining the three offscreen buffers in a separate pass. The GLSL program I'm using to render the test object declares three output fragment colors (outFrag0, outFrag1 and outFrag2), which I've mapped to the three render targets using glDrawBuffers() and glBindFragDataLocation(). All of this appears to work correctly; the final composited frame looks correct, and if I inspect each of the three render targets individually, they all contain the expected data. So far so good.

The problem comes if I switch to a GLSL program that doesn't write to all three render targets. If I use a program that only writes to outFrag0, for example, I would expect that nothing would be written to outFrag1 and outFrag2. Instead, I end up with the same data written to all three render targets. If I write to outFrag0 and outFrag1, then render target #3 gets a copy of the data in render target #2.

Is this the intended behavior? If I bind an FBO with N render targets, do all of my shaders need to explicitly write to all N render targets? That seems like a needless waste of bandwidth, if nothing else.

I can post code if necessary, but I wanted to validate the theory first.

Thanks!
- cort

DmitryM
07-20-2010, 02:34 PM
As far as I know, the glDrawBuffers controls destination buffers, but *not* the shader code. So the driver does what it's supposed to do, in my mind.

CortS
07-20-2010, 03:40 PM
So, you're saying I should call glDrawBuffers() before every mesh I render, based on how many render targets the mesh's shader is writing to? That doesn't seem consistent with documentation I've seen elsewhere (such as the OpenGL wiki page on Framebuffer Objects (http://www.opengl.org/wiki/Framebuffer_Object)), which states that glDrawBuffers() is generally just called once at FBO initialization time.

The more I think about it, it doesn't seem like what I'm seeing should be correct. At shader link time, I'm using glBindFragDataLocation() to map specific shader variable names to specific fragment color numbers. At FBO creation time, I'm using glDrawBuffers() to map fragment color numbers to FBO attachment points. If a shader is only writing to fragment color 1, and the currently-bound FBO has an image attached to fragment color 1, then why would it write anything to the images attached to fragment color 0, or 2? All the shader knows is the index(s) of the image(s) it's writing to; it shouldn't have to be aware of the images it's *not* writing to as well, should it?

DmitryM
07-20-2010, 04:39 PM
Fragment output positions are initialized automatically for you, you can just override them by calling glBindFragDataLocation. The unwritten fragment outputs are considered undefined.

Treat glDrawBuffers as a 'buffer mask' in a manner similar to other gl*Mask functions.

Edit: the association with the 'mask' is not that clear.
The correct definition is given in the GL spec - I recommend looking in it.

CortS
07-20-2010, 05:04 PM
Aha -- sure enough, there it is in the "Shader Outputs" portion of section 3.9.2 of the GL spec, clear as day: "Any colors, or color components, associated with a fragment that are not written by the fragment shader are undefined."

I stand corrected, and surprised; I would've thought that unwritten fragment colors would be treated similarly to a "discard" operation. Thanks Dmitry!

Also, rather than re-calling glDrawBuffers() throughout your frame, it looks like the preferred method for temporarily masking out writes to a specific color buffer is to use glColorMaskIndexedEXT(), from the EXT_draw_buffers2 extension.

- cort