Merge branch 'mesa_7_5_branch'
[mesa.git] / src / mesa / main / framebuffer.c
index 96f1b30c9b2c7204cbbecfce1b78619d10836d85..dc79b8ca61ad763b4de571cdb379172843e1e2b7 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * Mesa 3-D graphics library
- * Version:  6.5.1
+ * Version:  7.2
  *
- * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
+ * Copyright (C) 1999-2008  Brian Paul   All Rights Reserved.
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the "Software"),
 
 #include "glheader.h"
 #include "imports.h"
+#include "buffers.h"
 #include "context.h"
 #include "depthstencil.h"
+#include "macros.h"
 #include "mtypes.h"
 #include "fbobject.h"
 #include "framebuffer.h"
 #include "renderbuffer.h"
+#include "texobj.h"
 
 
 
@@ -107,11 +110,13 @@ _mesa_new_framebuffer(GLcontext *ctx, GLuint name)
    if (fb) {
       fb->Name = name;
       fb->RefCount = 1;
+      fb->_NumColorDrawBuffers = 1;
       fb->ColorDrawBuffer[0] = GL_COLOR_ATTACHMENT0_EXT;
-      fb->_ColorDrawBufferMask[0] = BUFFER_BIT_COLOR0;
+      fb->_ColorDrawBufferIndexes[0] = BUFFER_COLOR0;
       fb->ColorReadBuffer = GL_COLOR_ATTACHMENT0_EXT;
       fb->_ColorReadBufferIndex = BUFFER_COLOR0;
       fb->Delete = _mesa_destroy_framebuffer;
+      _glthread_INIT_MUTEX(fb->Mutex);
    }
    return fb;
 }
@@ -137,16 +142,18 @@ _mesa_initialize_framebuffer(struct gl_framebuffer *fb, const GLvisual *visual)
    /* save the visual */
    fb->Visual = *visual;
 
-   /* Init glRead/DrawBuffer state */
+   /* Init read/draw renderbuffer state */
    if (visual->doubleBufferMode) {
+      fb->_NumColorDrawBuffers = 1;
       fb->ColorDrawBuffer[0] = GL_BACK;
-      fb->_ColorDrawBufferMask[0] = BUFFER_BIT_BACK_LEFT;
+      fb->_ColorDrawBufferIndexes[0] = BUFFER_BACK_LEFT;
       fb->ColorReadBuffer = GL_BACK;
       fb->_ColorReadBufferIndex = BUFFER_BACK_LEFT;
    }
    else {
+      fb->_NumColorDrawBuffers = 1;
       fb->ColorDrawBuffer[0] = GL_FRONT;
-      fb->_ColorDrawBufferMask[0] = BUFFER_BIT_FRONT_LEFT;
+      fb->_ColorDrawBufferIndexes[0] = BUFFER_FRONT_LEFT;
       fb->ColorReadBuffer = GL_FRONT;
       fb->_ColorReadBufferIndex = BUFFER_FRONT_LEFT;
    }
@@ -192,17 +199,11 @@ _mesa_free_framebuffer_data(struct gl_framebuffer *fb)
          _mesa_reference_renderbuffer(&att->Renderbuffer, NULL);
       }
       if (att->Texture) {
-         /* render to texture */
-         att->Texture->RefCount--;
-         if (att->Texture->RefCount == 0) {
-            GET_CURRENT_CONTEXT(ctx);
-            if (ctx) {
-               ctx->Driver.DeleteTexture(ctx, att->Texture);
-            }
-         }
+         _mesa_reference_texobj(&att->Texture, NULL);
       }
+      ASSERT(!att->Renderbuffer);
+      ASSERT(!att->Texture);
       att->Type = GL_NONE;
-      att->Texture = NULL;
    }
 
    /* unbind _Depth/_StencilBuffer to decr ref counts */
@@ -223,45 +224,32 @@ _mesa_reference_framebuffer(struct gl_framebuffer **ptr,
       /* no change */
       return;
    }
-   if (*ptr) {
-      _mesa_unreference_framebuffer(ptr);
-   }
-   assert(!*ptr);
-   assert(fb);
-   _glthread_LOCK_MUTEX(fb->Mutex);
-   fb->RefCount++;
-   _glthread_UNLOCK_MUTEX(fb->Mutex);
-   *ptr = fb;
-}
-
 
-/**
- * Undo/remove a reference to a framebuffer object.
- * Decrement the framebuffer object's reference count and delete it when
- * the refcount hits zero.
- * Note: we pass the address of a pointer and set it to NULL.
- */
-void
-_mesa_unreference_framebuffer(struct gl_framebuffer **fb)
-{
-   assert(fb);
-   if (*fb) {
+   if (*ptr) {
+      /* unreference old renderbuffer */
       GLboolean deleteFlag = GL_FALSE;
+      struct gl_framebuffer *oldFb = *ptr;
 
-      _glthread_LOCK_MUTEX((*fb)->Mutex);
-      ASSERT((*fb)->RefCount > 0);
-      (*fb)->RefCount--;
-      deleteFlag = ((*fb)->RefCount == 0);
-      _glthread_UNLOCK_MUTEX((*fb)->Mutex);
+      _glthread_LOCK_MUTEX(oldFb->Mutex);
+      ASSERT(oldFb->RefCount > 0);
+      oldFb->RefCount--;
+      deleteFlag = (oldFb->RefCount == 0);
+      _glthread_UNLOCK_MUTEX(oldFb->Mutex);
       
       if (deleteFlag)
-         (*fb)->Delete(*fb);
+         oldFb->Delete(oldFb);
 
-      *fb = NULL;
+      *ptr = NULL;
    }
-}
-
+   assert(!*ptr);
 
+   if (fb) {
+      _glthread_LOCK_MUTEX(fb->Mutex);
+      fb->RefCount++;
+      _glthread_UNLOCK_MUTEX(fb->Mutex);
+      *ptr = fb;
+   }
+}
 
 
 /**
@@ -419,14 +407,14 @@ _mesa_ResizeBuffersMESA( void )
 /**
  * Examine all the framebuffer's renderbuffers to update the Width/Height
  * fields of the framebuffer.  If we have renderbuffers with different
- * sizes, set the framebuffer's width and height to zero.
+ * sizes, set the framebuffer's width and height to the min size.
  * Note: this is only intended for user-created framebuffers, not
  * window-system framebuffes.
  */
 static void
-update_framebuffer_size(struct gl_framebuffer *fb)
+update_framebuffer_size(GLcontext *ctx, struct gl_framebuffer *fb)
 {
-   GLboolean haveSize = GL_FALSE;
+   GLuint minWidth = ~0, minHeight = ~0;
    GLuint i;
 
    /* user-created framebuffers only */
@@ -436,21 +424,19 @@ update_framebuffer_size(struct gl_framebuffer *fb)
       struct gl_renderbuffer_attachment *att = &fb->Attachment[i];
       const struct gl_renderbuffer *rb = att->Renderbuffer;
       if (rb) {
-         if (haveSize) {
-            if (rb->Width != fb->Width && rb->Height != fb->Height) {
-               /* size mismatch! */
-               fb->Width = 0;
-               fb->Height = 0;
-               return;
-            }
-         }
-         else {
-            fb->Width = rb->Width;
-            fb->Height = rb->Height;
-            haveSize = GL_TRUE;
-         }
+         minWidth = MIN2(minWidth, rb->Width);
+         minHeight = MIN2(minHeight, rb->Height);
       }
    }
+
+   if (minWidth != ~0) {
+      fb->Width = minWidth;
+      fb->Height = minHeight;
+   }
+   else {
+      fb->Width = 0;
+      fb->Height = 0;
+   }
 }
 
 
@@ -470,7 +456,7 @@ _mesa_update_draw_buffer_bounds(GLcontext *ctx)
 
    if (buffer->Name) {
       /* user-created framebuffer size depends on the renderbuffers */
-      update_framebuffer_size(buffer);
+      update_framebuffer_size(ctx, buffer);
    }
 
    buffer->_Xmin = 0;
@@ -545,6 +531,7 @@ _mesa_update_framebuffer_visual(struct gl_framebuffer *fb)
             fb->Visual.rgbBits = fb->Visual.redBits
                + fb->Visual.greenBits + fb->Visual.blueBits;
             fb->Visual.floatMode = GL_FALSE;
+            fb->Visual.samples = rb->NumSamples;
             break;
          }
          else if (rb->_BaseFormat == GL_COLOR_INDEX) {
@@ -570,13 +557,13 @@ _mesa_update_framebuffer_visual(struct gl_framebuffer *fb)
    if (fb->Attachment[BUFFER_ACCUM].Renderbuffer) {
       fb->Visual.haveAccumBuffer = GL_TRUE;
       fb->Visual.accumRedBits
-         = fb->Attachment[BUFFER_DEPTH].Renderbuffer->RedBits;
+         = fb->Attachment[BUFFER_ACCUM].Renderbuffer->RedBits;
       fb->Visual.accumGreenBits
-         = fb->Attachment[BUFFER_DEPTH].Renderbuffer->GreenBits;
+         = fb->Attachment[BUFFER_ACCUM].Renderbuffer->GreenBits;
       fb->Visual.accumBlueBits
-         = fb->Attachment[BUFFER_DEPTH].Renderbuffer->BlueBits;
+         = fb->Attachment[BUFFER_ACCUM].Renderbuffer->BlueBits;
       fb->Visual.accumAlphaBits
-         = fb->Attachment[BUFFER_DEPTH].Renderbuffer->AlphaBits;
+         = fb->Attachment[BUFFER_ACCUM].Renderbuffer->AlphaBits;
    }
 
    compute_depth_max(fb);
@@ -665,8 +652,53 @@ _mesa_update_stencil_buffer(GLcontext *ctx,
 }
 
 
+/*
+ * Example DrawBuffers scenarios:
+ *
+ * 1. glDrawBuffer(GL_FRONT_AND_BACK), fixed-func or shader writes to
+ * "gl_FragColor" or program writes to the "result.color" register:
+ *
+ *   fragment color output   renderbuffer
+ *   ---------------------   ---------------
+ *   color[0]                Front, Back
+ *
+ *
+ * 2. glDrawBuffers(3, [GL_FRONT, GL_AUX0, GL_AUX1]), shader writes to
+ * gl_FragData[i] or program writes to result.color[i] registers:
+ *
+ *   fragment color output   renderbuffer
+ *   ---------------------   ---------------
+ *   color[0]                Front
+ *   color[1]                Aux0
+ *   color[3]                Aux1
+ *
+ *
+ * 3. glDrawBuffers(3, [GL_FRONT, GL_AUX0, GL_AUX1]) and shader writes to
+ * gl_FragColor, or fixed function:
+ *
+ *   fragment color output   renderbuffer
+ *   ---------------------   ---------------
+ *   color[0]                Front, Aux0, Aux1
+ *
+ *
+ * In either case, the list of renderbuffers is stored in the
+ * framebuffer->_ColorDrawBuffers[] array and
+ * framebuffer->_NumColorDrawBuffers indicates the number of buffers.
+ * The renderer (like swrast) has to look at the current fragment shader
+ * to see if it writes to gl_FragColor vs. gl_FragData[i] to determine
+ * how to map color outputs to renderbuffers.
+ *
+ * Note that these two calls are equivalent (for fixed function fragment
+ * shading anyway):
+ *   a)  glDrawBuffer(GL_FRONT_AND_BACK);  (assuming non-stereo framebuffer)
+ *   b)  glDrawBuffers(2, [GL_FRONT_LEFT, GL_BACK_LEFT]);
+ */
+
+
+
+
 /**
- * Update the list of color drawing renderbuffer pointers.
+ * Update the (derived) list of color drawing renderbuffer pointers.
  * Later, when we're rendering we'll loop from 0 to _NumColorDrawBuffers
  * writing colors.
  */
@@ -675,42 +707,23 @@ update_color_draw_buffers(GLcontext *ctx, struct gl_framebuffer *fb)
 {
    GLuint output;
 
-   /*
-    * Fragment programs can write to multiple colorbuffers with
-    * the GL_ARB_draw_buffers extension.
-    */
-   for (output = 0; output < ctx->Const.MaxDrawBuffers; output++) {
-      GLbitfield bufferMask = fb->_ColorDrawBufferMask[output];
-      GLuint count = 0;
-      GLuint i;
-      if (!fb->DeletePending) {
-         /* We need the inner loop here because glDrawBuffer(GL_FRONT_AND_BACK)
-          * can specify writing to two or four color buffers (for example).
-          */
-         for (i = 0; bufferMask && i < BUFFER_COUNT; i++) {
-            const GLuint bufferBit = 1 << i;
-            if (bufferBit & bufferMask) {
-               struct gl_renderbuffer *rb = fb->Attachment[i].Renderbuffer;
-               if (rb && rb->Width > 0 && rb->Height > 0) {
-                  fb->_ColorDrawBuffers[output][count] = rb;
-                  count++;
-               }
-               else {
-                  /*
-                  _mesa_warning(ctx, "DrawBuffer names a missing buffer!\n");
-                  */
-               }
-               bufferMask &= ~bufferBit;
-            }
-         }
+   /* set 0th buffer to NULL now in case _NumColorDrawBuffers is zero */
+   fb->_ColorDrawBuffers[0] = NULL;
+
+   for (output = 0; output < fb->_NumColorDrawBuffers; output++) {
+      GLint buf = fb->_ColorDrawBufferIndexes[output];
+      if (buf >= 0) {
+         fb->_ColorDrawBuffers[output] = fb->Attachment[buf].Renderbuffer;
+      }
+      else {
+         fb->_ColorDrawBuffers[output] = NULL;
       }
-      fb->_NumColorDrawBuffers[output] = count;
    }
 }
 
 
 /**
- * Update the color read renderbuffer pointer.
+ * Update the (derived) color read renderbuffer pointer.
  * Unlike the DrawBuffer, we can only read from one (or zero) color buffers.
  */
 static void
@@ -732,19 +745,52 @@ update_color_read_buffer(GLcontext *ctx, struct gl_framebuffer *fb)
 }
 
 
+/**
+ * Update a gl_framebuffer's derived state.
+ *
+ * Specifically, update these framebuffer fields:
+ *    _ColorDrawBuffers
+ *    _NumColorDrawBuffers
+ *    _ColorReadBuffer
+ *    _DepthBuffer
+ *    _StencilBuffer
+ *
+ * If the framebuffer is user-created, make sure it's complete.
+ *
+ * The following functions (at least) can effect framebuffer state:
+ * glReadBuffer, glDrawBuffer, glDrawBuffersARB, glFramebufferRenderbufferEXT,
+ * glRenderbufferStorageEXT.
+ */
 static void
 update_framebuffer(GLcontext *ctx, struct gl_framebuffer *fb)
 {
-   /* Completeness only matters for user-created framebuffers */
-   if (fb->Name != 0) {
-      /* XXX: EXT_framebuffer_blit:
-         framebuffer must still be complete wrt read/draw? */
-      _mesa_test_framebuffer_completeness(ctx, fb);
-      _mesa_update_framebuffer_visual(fb);
+   if (fb->Name == 0) {
+      /* This is a window-system framebuffer */
+      /* Need to update the FB's GL_DRAW_BUFFER state to match the
+       * context state (GL_READ_BUFFER too).
+       */
+      if (fb->ColorDrawBuffer[0] != ctx->Color.DrawBuffer[0]) {
+         _mesa_drawbuffers(ctx, ctx->Const.MaxDrawBuffers,
+                           ctx->Color.DrawBuffer, NULL);
+      }
+      if (fb->ColorReadBuffer != ctx->Pixel.ReadBuffer) {
+         
+      }
+   }
+   else {
+      /* This is a user-created framebuffer.
+       * Completeness only matters for user-created framebuffers.
+       */
+      if (fb->_Status != GL_FRAMEBUFFER_COMPLETE) {
+         _mesa_test_framebuffer_completeness(ctx, fb);
+      }
    }
 
-   /* update_color_draw/read_buffers not needed for
-      read/draw only fb, but shouldn't hurt ??? */
+   /* Strictly speaking, we don't need to update the draw-state
+    * if this FB is bound as ctx->ReadBuffer (and conversely, the
+    * read-state if this FB is bound as ctx->DrawBuffer), but no
+    * harm.
+    */
    update_color_draw_buffers(ctx, fb);
    update_color_read_buffer(ctx, fb);
    _mesa_update_depth_buffer(ctx, fb, BUFFER_DEPTH);
@@ -753,34 +799,25 @@ update_framebuffer(GLcontext *ctx, struct gl_framebuffer *fb)
    compute_depth_max(fb);
 }
 
+
 /**
  * Update state related to the current draw/read framebuffers.
- * Specifically, update these framebuffer fields:
- *    _ColorDrawBuffers
- *    _NumColorDrawBuffers
- *    _ColorReadBuffer
- *    _DepthBuffer
- *    _StencilBuffer
- * If the current framebuffer is user-created, make sure it's complete.
- * The following functions can effect this state:  glReadBuffer,
- * glDrawBuffer, glDrawBuffersARB, glFramebufferRenderbufferEXT,
- * glRenderbufferStorageEXT.
  */
 void
 _mesa_update_framebuffer(GLcontext *ctx)
 {
-   struct gl_framebuffer *fb = ctx->DrawBuffer;
-   struct gl_framebuffer *fbread = ctx->ReadBuffer;
+   struct gl_framebuffer *drawFb = ctx->DrawBuffer;
+   struct gl_framebuffer *readFb = ctx->ReadBuffer;
 
-   update_framebuffer(ctx, fb);
-   if (fbread != fb)
-      update_framebuffer(ctx, fbread);
+   update_framebuffer(ctx, drawFb);
+   if (readFb != drawFb)
+      update_framebuffer(ctx, readFb);
 }
 
 
 /**
  * Check if the renderbuffer for a read operation (glReadPixels, glCopyPixels,
- * glCopyTex[Sub]Image, etc. exists.
+ * glCopyTex[Sub]Image, etc) exists.
  * \param format  a basic image format such as GL_RGB, GL_RGBA, GL_ALPHA,
  *                GL_DEPTH_COMPONENT, etc. or GL_COLOR, GL_DEPTH, GL_STENCIL.
  * \return GL_TRUE if buffer exists, GL_FALSE otherwise
@@ -788,8 +825,12 @@ _mesa_update_framebuffer(GLcontext *ctx)
 GLboolean
 _mesa_source_buffer_exists(GLcontext *ctx, GLenum format)
 {
-   const struct gl_renderbuffer_attachment *att
-      = ctx->ReadBuffer->Attachment;
+   const struct gl_renderbuffer_attachment *att = ctx->ReadBuffer->Attachment;
+
+   /* If we don't know the framebuffer status, update it now */
+   if (ctx->ReadBuffer->_Status == 0) {
+      _mesa_test_framebuffer_completeness(ctx, ctx->ReadBuffer);
+   }
 
    if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
       return GL_FALSE;
@@ -813,10 +854,8 @@ _mesa_source_buffer_exists(GLcontext *ctx, GLenum format)
       if (ctx->ReadBuffer->_ColorReadBuffer == NULL) {
          return GL_FALSE;
       }
-      /* XXX enable this post 6.5 release:
       ASSERT(ctx->ReadBuffer->_ColorReadBuffer->RedBits > 0 ||
              ctx->ReadBuffer->_ColorReadBuffer->IndexBits > 0);
-      */
       break;
    case GL_DEPTH:
    case GL_DEPTH_COMPONENT:
@@ -854,13 +893,17 @@ _mesa_source_buffer_exists(GLcontext *ctx, GLenum format)
 
 /**
  * As above, but for drawing operations.
- * XXX code do some code merging w/ above function.
+ * XXX could do some code merging w/ above function.
  */
 GLboolean
 _mesa_dest_buffer_exists(GLcontext *ctx, GLenum format)
 {
-   const struct gl_renderbuffer_attachment *att
-      = ctx->ReadBuffer->Attachment;
+   const struct gl_renderbuffer_attachment *att = ctx->DrawBuffer->Attachment;
+
+   /* If we don't know the framebuffer status, update it now */
+   if (ctx->DrawBuffer->_Status == 0) {
+      _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer);
+   }
 
    if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
       return GL_FALSE;
@@ -881,7 +924,7 @@ _mesa_dest_buffer_exists(GLcontext *ctx, GLenum format)
    case GL_BGRA:
    case GL_ABGR_EXT:
    case GL_COLOR_INDEX:
-      /* nothing special */
+      /* Nothing special since GL_DRAW_BUFFER could be GL_NONE. */
       /* Could assert that colorbuffer has RedBits > 0 */
       break;
    case GL_DEPTH:
@@ -908,7 +951,7 @@ _mesa_dest_buffer_exists(GLcontext *ctx, GLenum format)
       break;
    default:
       _mesa_problem(ctx,
-                    "Unexpected format 0x%x in _mesa_source_buffer_exists",
+                    "Unexpected format 0x%x in _mesa_dest_buffer_exists",
                     format);
       return GL_FALSE;
    }