title: glDrawBuffers usage for multiple render targets, under OS X
body: <p>I'm having some very strange behaviour from my multiple-render-target code, and have started to wonder whether I'm catastrophically misunderstanding the way that this is supposed to work.</p>
  
  <p>I'm running in a version 2.1 context.  Here's the core bit of render setup code I'm executing:</p>
  
  <pre><code>glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
  GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
  glDrawBuffers(2,buffers);  
  </code></pre>
  
  <p>My shader then writes out color data to <code>gl_FragColor[0]</code> and <code>glFragColor[1]</code>.</p>
  
  <p>This is essentially the same situation as was discussed in <a href="https://stackoverflow.com/questions/7207422/setting-up-opengl-multiple-render-targets">this question</a>.  However, when I run this on OS X, my shader only outputs to the first render target.  OpenGL throws no errors, either during the construction of the FBO with two color attachments, or at any point during the rendering process.</p>
  
  <p>When I examine what's going on via the OSX 'OpenGL Profiler' "trace" view, it shows the driver's side of this code execution as being:                                                                                  </p>
  
  <pre><code>2.86 µs glBindFramebufferEXT(GL_FRAMEBUFFER, 1);
  3.48 µs glDrawBuffersARB(2, {GL_COLOR_ATTACHMENT0, GL_ZERO});
  </code></pre>
  
  <p>Which perhaps explains why nothing was being written to <code>GL_COLOR_ATTACHMENT1</code>; it seems to be being replaced by <code>GL_ZERO</code> in the call to <code>glDrawBuffers</code>!</p>
  
  <p>If I switch the order of the buffers in the <code>buffers[]</code> array to be <code>GL_COLOR_ATTACHMENT1_EXT</code> first and then <code>GL_COLOR_ATTACHMENT0_EXT</code>, then my shader only writes into <code>GL_COLOR_ATTACHMENT1_EXT</code>, and <code>GL_COLOR_ATTACHMENT0_EXT</code> appears to be replaced with <code>GL_ZERO</code>.</p>
  
  <p>Here's where it gets weird.  If I use the code:</p>
  
  <pre><code>GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
  glDrawBuffers(3, buffers);
  </code></pre>
  
  <p>Then the statistics view shows this:</p>
  
  <pre><code>0.46 µs glDrawBuffersARB(3, {GL_COLOR_ATTACHMENT0, GL_ZERO, GL_COLOR_ATTACHMENT1});
  </code></pre>
  
  <p>OpenGL still throws no errors, and my shader successfully writes out data to both color attachments, even though it's writing to <code>gl_FragColor[0]</code> and <code>gl_FragColor[1]</code>.</p>
  
  <p>So even though my program is now working, it seems to me like this shouldn't work.  And I was curious to see how far I could push this, hoping that pushing OpenGL to an eventual failure would be educational.  So I tried compiling with this code:</p>
  
  <pre><code>GLenum buffers[] = { GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT };
  glDrawBuffers(4, buffers);
  </code></pre>
  
  <p>When running that, the OpenGL Profiler "trace" view shows this as being executed:</p>
  
  <pre><code>4.26 µs glDrawBuffersARB(4, {GL_COLOR_ATTACHMENT0, GL_ZERO, GL_COLOR_ATTACHMENT1, GL_ZERO});
  </code></pre>
  
  <p>And now OpenGL is throwing "invalid framebuffer operations" all over the place, but my shader is still successfully writing color data to both color attachment points.</p>
  
  <p>Does all this make sense to anyone?  Have I catastrophically misunderstood the way that <code>glDrawBuffers</code> is supposed to be called?</p>
  
  <p>According to the OpenGL Profiler's "Resources" view, my framebuffer (number 1) looks fine;  it does have two color attachments attached, as expected.</p>
  
  <pre><code>Attached Objects:
  {
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT0
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_TEXTURE
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: 1
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT: 0
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: 0
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: 0
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT1
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_TEXTURE
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: 2
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT: 0
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: 0
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: 0
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT2
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_NONE
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT3
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_NONE
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT4
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_NONE
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT5
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_NONE
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT6
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_NONE
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_COLOR_ATTACHMENT7
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_NONE
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_DEPTH_ATTACHMENT
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_TEXTURE
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT: 3
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT: 0
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT: 0
      GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT: 0
    }
    {
      GL_FRAMEBUFFER_ATTACHMENT: GL_STENCIL_ATTACHMENT
      GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT: GL_NONE
    }
  }
  </code></pre>