Fix pow <small> and a very stypid bug with dummy srcs(0 equals to tmp0.x)</small...
[mesa.git] / src / mesa / main / fbobject.c
index f2ab7dc9c06102fb08b71495e07b5472f6a712b6..3e425bd6724ec122357c439759dbf67c23fcee26 100644 (file)
 #include "texstore.h"
 
 
+/* XXX temporarily here */
+#define GL_READ_FRAMEBUFFER_EXT                0x90
+#define GL_DRAW_FRAMEBUFFER_EXT                0x9a
+#define GL_DRAW_FRAMEBUFFER_BINDING_EXT        GL_FRAMEBUFFER_BINDING_EXT
+#define GL_READ_FRAMEBUFFER_BINDING_EXT        0x9b
+
+
 /**
  * Notes:
  *
@@ -100,8 +107,9 @@ lookup_framebuffer(GLcontext *ctx, GLuint id)
  * Given a GL_*_ATTACHMENTn token, return a pointer to the corresponding
  * gl_renderbuffer_attachment object.
  */
-static struct gl_renderbuffer_attachment *
-get_attachment(GLcontext *ctx, struct gl_framebuffer *fb, GLenum attachment)
+struct gl_renderbuffer_attachment *
+_mesa_get_attachment(GLcontext *ctx, struct gl_framebuffer *fb,
+                     GLenum attachment)
 {
    GLuint i;
 
@@ -146,19 +154,21 @@ _mesa_remove_attachment(GLcontext *ctx, struct gl_renderbuffer_attachment *att)
 {
    if (att->Type == GL_TEXTURE) {
       ASSERT(att->Texture);
-      if (att->Renderbuffer) {
-         /* delete/remove the 'wrapper' renderbuffer */
-         /* XXX do we really want to do this??? */
-         att->Renderbuffer->Delete(att->Renderbuffer);
-         att->Renderbuffer = NULL;
-      }
       att->Texture->RefCount--;
       if (att->Texture->RefCount == 0) {
         ctx->Driver.DeleteTexture(ctx, att->Texture);
       }
+      else {
+         /* tell driver that we're done rendering to this texture. */
+         if (ctx->Driver.FinishRenderTexture) {
+            ctx->Driver.FinishRenderTexture(ctx, att->Texture,
+                                            att->CubeMapFace,
+                                            att->TextureLevel);
+         }
+      }
       att->Texture = NULL;
    }
-   else if (att->Type == GL_RENDERBUFFER_EXT) {
+   if (att->Type == GL_TEXTURE || att->Type == GL_RENDERBUFFER_EXT) {
       ASSERT(att->Renderbuffer);
       ASSERT(!att->Texture);
       att->Renderbuffer->RefCount--;
@@ -182,9 +192,19 @@ _mesa_set_texture_attachment(GLcontext *ctx,
                              struct gl_texture_object *texObj,
                              GLenum texTarget, GLuint level, GLuint zoffset)
 {
-   _mesa_remove_attachment(ctx, att);
-   att->Type = GL_TEXTURE;
-   att->Texture = texObj;
+   if (att->Texture == texObj) {
+      /* re-attaching same texture */
+      ASSERT(att->Type == GL_TEXTURE);
+   }
+   else {
+      /* new attachment */
+      _mesa_remove_attachment(ctx, att);
+      att->Type = GL_TEXTURE;
+      att->Texture = texObj;
+      texObj->RefCount++;
+   }
+
+   /* always update these fields */
    att->TextureLevel = level;
    if (IS_CUBE_FACE(texTarget)) {
       att->CubeMapFace = texTarget - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
@@ -194,13 +214,6 @@ _mesa_set_texture_attachment(GLcontext *ctx,
    }
    att->Zoffset = zoffset;
    att->Complete = GL_FALSE;
-
-   texObj->RefCount++;
-
-   /* XXX when we attach to a texture, we should probably set the
-    * att->Renderbuffer pointer to a "wrapper renderbuffer" which
-    * makes the texture image look like renderbuffer.
-    */
 }
 
 
@@ -224,15 +237,17 @@ _mesa_set_renderbuffer_attachment(GLcontext *ctx,
 
 /**
  * Fallback for ctx->Driver.FramebufferRenderbuffer()
- * Sets a framebuffer attachment to a particular renderbuffer.
- * The framebuffer in question is ctx->DrawBuffer.
- * \sa _mesa_renderbuffer_texture
+ * Attach a renderbuffer object to a framebuffer object.
  */
 void
-_mesa_framebuffer_renderbuffer(GLcontext *ctx,
-                               struct gl_renderbuffer_attachment *att,
-                               struct gl_renderbuffer *rb)
+_mesa_framebuffer_renderbuffer(GLcontext *ctx, struct gl_framebuffer *fb,
+                               GLenum attachment, struct gl_renderbuffer *rb)
 {
+   struct gl_renderbuffer_attachment *att;
+
+   att = _mesa_get_attachment(ctx, fb, attachment);
+   ASSERT(att);
+
    if (rb) {
       _mesa_set_renderbuffer_attachment(ctx, att, rb);
    }
@@ -406,9 +421,11 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
       }
 
       if (att->Type == GL_TEXTURE) {
-         w = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Width;
-         h = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Height;
-         f = att->Texture->Image[att->CubeMapFace][att->TextureLevel]->Format;
+         const struct gl_texture_image *texImg
+            = att->Texture->Image[att->CubeMapFace][att->TextureLevel];
+         w = texImg->Width;
+         h = texImg->Height;
+         f = texImg->_BaseFormat;
          numImages++;
          if (f != GL_RGB && f != GL_RGBA && f != GL_DEPTH_COMPONENT) {
             /* XXX need GL_DEPTH_STENCIL_EXT test? */
@@ -451,7 +468,7 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
    for (i = 0; i < ctx->Const.MaxDrawBuffers; i++) {
       if (fb->ColorDrawBuffer[i] != GL_NONE) {
          const struct gl_renderbuffer_attachment *att
-            = get_attachment(ctx, fb, fb->ColorDrawBuffer[i]);
+            = _mesa_get_attachment(ctx, fb, fb->ColorDrawBuffer[i]);
          assert(att);
          if (att->Type == GL_NONE) {
             fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT;
@@ -463,7 +480,7 @@ _mesa_test_framebuffer_completeness(GLcontext *ctx, struct gl_framebuffer *fb)
    /* Check that the ReadBuffer is present */
    if (fb->ColorReadBuffer != GL_NONE) {
       const struct gl_renderbuffer_attachment *att
-         = get_attachment(ctx, fb, fb->ColorReadBuffer);
+         = _mesa_get_attachment(ctx, fb, fb->ColorReadBuffer);
       assert(att);
       if (att->Type == GL_NONE) {
          fb->_Status = GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT;
@@ -525,7 +542,6 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
-   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
    if (target != GL_RENDERBUFFER_EXT) {
          _mesa_error(ctx, GL_INVALID_ENUM,
@@ -533,6 +549,8 @@ _mesa_BindRenderbufferEXT(GLenum target, GLuint renderbuffer)
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    if (renderbuffer) {
       newRb = lookup_renderbuffer(ctx, renderbuffer);
       if (newRb == &DummyRenderbuffer) {
@@ -576,6 +594,7 @@ _mesa_DeleteRenderbuffersEXT(GLsizei n, const GLuint *renderbuffers)
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
    for (i = 0; i < n; i++) {
       if (renderbuffers[i] > 0) {
@@ -698,7 +717,6 @@ _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
-   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
    if (target != GL_RENDERBUFFER_EXT) {
       _mesa_error(ctx, GL_INVALID_ENUM, "glRenderbufferStorageEXT(target)");
@@ -729,6 +747,15 @@ _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
+   if (rb->InternalFormat == internalFormat &&
+       rb->Width == width &&
+       rb->Height == height) {
+      /* no change in allocation needed */
+      return;
+   }
+
    /* Now allocate the storage */
    ASSERT(rb->AllocStorage);
    if (rb->AllocStorage(ctx, rb, internalFormat, width, height)) {
@@ -736,6 +763,8 @@ _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
       assert(rb->Width == width);
       assert(rb->Height == height);
       assert(rb->InternalFormat);
+      assert(rb->RedBits || rb->GreenBits || rb->BlueBits || rb->AlphaBits ||
+             rb->DepthBits || rb->StencilBits || rb->IndexBits);
       rb->_BaseFormat = baseFormat;
    }
    else {
@@ -744,6 +773,13 @@ _mesa_RenderbufferStorageEXT(GLenum target, GLenum internalFormat,
       rb->Height = 0;
       rb->InternalFormat = GL_NONE;
       rb->_BaseFormat = GL_NONE;
+      rb->RedBits =
+      rb->GreenBits =
+      rb->BlueBits =
+      rb->AlphaBits =
+      rb->IndexBits =
+      rb->DepthBits =
+      rb->StencilBits = 0;
    }
 
    /*
@@ -774,6 +810,8 @@ _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    switch (pname) {
    case GL_RENDERBUFFER_WIDTH_EXT:
       *params = ctx->CurrentRenderbuffer->Width;
@@ -785,60 +823,23 @@ _mesa_GetRenderbufferParameterivEXT(GLenum target, GLenum pname, GLint *params)
       *params = ctx->CurrentRenderbuffer->InternalFormat;
       return;
    case GL_RENDERBUFFER_RED_SIZE_EXT:
-      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
-          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
-         *params = ctx->CurrentRenderbuffer->RedBits;
-      }
-      else {
-         *params = 0;
-      }
+      *params = ctx->CurrentRenderbuffer->RedBits;
       break;
    case GL_RENDERBUFFER_GREEN_SIZE_EXT:
-      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
-          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
-         *params = ctx->CurrentRenderbuffer->GreenBits;
-      }
-      else {
-         *params = 0;
-      }
+      *params = ctx->CurrentRenderbuffer->GreenBits;
       break;
    case GL_RENDERBUFFER_BLUE_SIZE_EXT:
-      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
-          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
-         *params = ctx->CurrentRenderbuffer->BlueBits;
-      }
-      else {
-         *params = 0;
-      }
+      *params = ctx->CurrentRenderbuffer->BlueBits;
       break;
    case GL_RENDERBUFFER_ALPHA_SIZE_EXT:
-      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_RGB ||
-          ctx->CurrentRenderbuffer->_BaseFormat == GL_RGBA) {
-         *params = ctx->CurrentRenderbuffer->AlphaBits;
-      }
-      else {
-         *params = 0;
-      }
+      *params = ctx->CurrentRenderbuffer->AlphaBits;
       break;
    case GL_RENDERBUFFER_DEPTH_SIZE_EXT:
-      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_COMPONENT ||
-          ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
-         *params = ctx->CurrentRenderbuffer->DepthBits;
-      }
-      else {
-         *params = 0;
-      }
+      *params = ctx->CurrentRenderbuffer->DepthBits;
       break;
    case GL_RENDERBUFFER_STENCIL_SIZE_EXT:
-      if (ctx->CurrentRenderbuffer->_BaseFormat == GL_STENCIL_INDEX ||
-          ctx->CurrentRenderbuffer->_BaseFormat == GL_DEPTH_STENCIL_EXT) {
-         *params = ctx->CurrentRenderbuffer->StencilBits;
-      }
-      else {
-         *params = 0;
-      }
+      *params = ctx->CurrentRenderbuffer->StencilBits;
       break;
-
    default:
       _mesa_error(ctx, GL_INVALID_ENUM,
                   "glGetRenderbufferParameterivEXT(target)");
@@ -861,20 +862,67 @@ _mesa_IsFramebufferEXT(GLuint framebuffer)
 }
 
 
+/**
+ * Examine all the framebuffer's attachments to see if any are textures.
+ * If so, call ctx->Driver.FinishRenderTexture() for each texture to
+ * notify the device driver that the texture image may have changed.
+ */
+static void
+check_texture_render(GLcontext *ctx, struct gl_framebuffer *fb)
+{
+   if (ctx->Driver.FinishRenderTexture) {
+      GLuint i;
+      for (i = 0; i < BUFFER_COUNT; i++) {
+         struct gl_renderbuffer_attachment *att = fb->Attachment + i;
+         struct gl_texture_object *texObj = att->Texture;
+         if (texObj) {
+            ctx->Driver.FinishRenderTexture(ctx, texObj, att->CubeMapFace,
+                                            att->TextureLevel);
+         }
+      }
+   }
+}
+
+
 void GLAPIENTRY
 _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
 {
-   struct gl_framebuffer *newFb, *newReadFb, *oldFb;
+   struct gl_framebuffer *newFb, *oldFb;
+   GLboolean bindReadBuf, bindDrawBuf;
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
-   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
-   if (target != GL_FRAMEBUFFER_EXT) {
+   switch (target) {
+#if FEATURE_EXT_framebuffer_blit
+   case GL_DRAW_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
+         return;
+      }
+      bindDrawBuf = GL_TRUE;
+      bindReadBuf = GL_FALSE;
+      break;
+   case GL_READ_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
+         return;
+      }
+      bindDrawBuf = GL_FALSE;
+      bindReadBuf = GL_TRUE;
+      break;
+#endif
+   case GL_FRAMEBUFFER_EXT:
+      bindDrawBuf = GL_TRUE;
+      bindReadBuf = GL_TRUE;
+      break;
+   default:
       _mesa_error(ctx, GL_INVALID_ENUM, "glBindFramebufferEXT(target)");
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    if (framebuffer) {
       /* Binding a user-created framebuffer object */
       newFb = lookup_framebuffer(ctx, framebuffer);
@@ -891,30 +939,46 @@ _mesa_BindFramebufferEXT(GLenum target, GLuint framebuffer)
         }
          _mesa_HashInsert(ctx->Shared->FrameBuffers, framebuffer, newFb);
       }
-      newFb->RefCount++;
-      newReadFb = newFb;
+      if (bindReadBuf)
+         newFb->RefCount++;
+      if (bindDrawBuf)
+         newFb->RefCount++;
    }
    else {
       /* Binding the window system framebuffer (which was originally set
        * with MakeCurrent).
        */
       newFb = ctx->WinSysDrawBuffer;
-      newReadFb = ctx->WinSysReadBuffer;
    }
 
-   oldFb = ctx->DrawBuffer;
-   if (oldFb && oldFb->Name != 0) {
-      oldFb->RefCount--;
-      if (oldFb->RefCount == 0) {
-         oldFb->Delete(oldFb);
+   ASSERT(newFb != &DummyFramebuffer);
+
+   if (bindReadBuf) {
+      oldFb = ctx->ReadBuffer;
+      if (oldFb && oldFb->Name != 0) {
+         oldFb->RefCount--;
+         if (oldFb->RefCount == 0) {
+            oldFb->Delete(oldFb);
+         }
       }
+      ctx->ReadBuffer = newFb;
    }
 
-   ASSERT(newFb != &DummyFramebuffer);
-
-   /* Note, we set both the GL_DRAW_BUFFER and GL_READ_BUFFER state: */
-   ctx->DrawBuffer = newFb;
-   ctx->ReadBuffer = newReadFb;
+   if (bindDrawBuf) {
+      oldFb = ctx->DrawBuffer;
+      if (oldFb && oldFb->Name != 0) {
+         /* check if old FB had any texture attachments */
+         if (ctx->Driver.FinishRenderTexture) {
+            check_texture_render(ctx, oldFb);
+         }
+         /* check if time to delete this framebuffer */
+         oldFb->RefCount--;
+         if (oldFb->RefCount == 0) {
+            oldFb->Delete(oldFb);
+         }
+      }
+      ctx->DrawBuffer = newFb;
+   }
 }
 
 
@@ -925,6 +989,7 @@ _mesa_DeleteFramebuffersEXT(GLsizei n, const GLuint *framebuffers)
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
    for (i = 0; i < n; i++) {
       if (framebuffers[i] > 0) {
@@ -992,22 +1057,45 @@ _mesa_GenFramebuffersEXT(GLsizei n, GLuint *framebuffers)
 GLenum GLAPIENTRY
 _mesa_CheckFramebufferStatusEXT(GLenum target)
 {
+   struct gl_framebuffer *buffer;
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END_WITH_RETVAL(ctx, 0);
 
-   if (target != GL_FRAMEBUFFER_EXT) {
+   switch (target) {
+#if FEATURE_EXT_framebuffer_blit
+   case GL_DRAW_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
+         return 0;
+      }
+      buffer = ctx->DrawBuffer;
+      break;
+   case GL_READ_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
+         return 0;
+      }
+      buffer = ctx->ReadBuffer;
+      break;
+#endif
+   case GL_FRAMEBUFFER_EXT:
+      buffer = ctx->DrawBuffer;
+      break;
+   default:
       _mesa_error(ctx, GL_INVALID_ENUM, "glCheckFramebufferStatus(target)");
       return 0; /* formerly GL_FRAMEBUFFER_STATUS_ERROR_EXT */
    }
 
-   if (ctx->DrawBuffer->Name == 0) {
+   if (buffer->Name == 0) {
       /* The window system / default framebuffer is always complete */
       return GL_FRAMEBUFFER_COMPLETE_EXT;
    }
 
-   _mesa_test_framebuffer_completeness(ctx, ctx->DrawBuffer);
-   return ctx->DrawBuffer->_Status;
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
+   _mesa_test_framebuffer_completeness(ctx, buffer);
+   return buffer->_Status;
 }
 
 
@@ -1068,7 +1156,6 @@ _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
-   FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
 
    if (error_check_framebuffer_texture(ctx, 1, target, attachment,
                                       textarget, texture, level))
@@ -1076,13 +1163,16 @@ _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
 
    ASSERT(textarget == GL_TEXTURE_1D);
 
-   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
+   /* XXX read blit */
+   att = _mesa_get_attachment(ctx, ctx->DrawBuffer, attachment);
    if (att == NULL) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                  "glFramebufferTexture1DEXT(attachment)");
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    if (texture) {
       texObj = (struct gl_texture_object *)
         _mesa_HashLookup(ctx->Shared->TexObjects, texture);
@@ -1102,8 +1192,6 @@ _mesa_FramebufferTexture1DEXT(GLenum target, GLenum attachment,
       texObj = NULL;
    }
    ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
-
-   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
 }
 
 
@@ -1116,7 +1204,6 @@ _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
-   FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
 
    if (error_check_framebuffer_texture(ctx, 2, target, attachment,
                                       textarget, texture, level))
@@ -1126,13 +1213,15 @@ _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
          textarget == GL_TEXTURE_RECTANGLE_ARB ||
          IS_CUBE_FACE(textarget));
 
-   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
+   att = _mesa_get_attachment(ctx, ctx->DrawBuffer, attachment);
    if (att == NULL) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                  "glFramebufferTexture2DEXT(attachment)");
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    if (texture) {
       texObj = (struct gl_texture_object *)
         _mesa_HashLookup(ctx->Shared->TexObjects, texture);
@@ -1156,7 +1245,6 @@ _mesa_FramebufferTexture2DEXT(GLenum target, GLenum attachment,
       texObj = NULL;
    }
    ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget, level, 0);
-   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
 }
 
 
@@ -1170,7 +1258,6 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
-   FLUSH_VERTICES(ctx, _NEW_BUFFERS); /* XXX check */
 
    if (error_check_framebuffer_texture(ctx, 3, target, attachment,
                                       textarget, texture, level))
@@ -1178,13 +1265,15 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
 
    ASSERT(textarget == GL_TEXTURE_3D);
 
-   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
+   att = _mesa_get_attachment(ctx, ctx->DrawBuffer, attachment);
    if (att == NULL) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                  "glFramebufferTexture1DEXT(attachment)");
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    if (texture) {
       const GLint maxSize = 1 << (ctx->Const.Max3DTextureLevels - 1);
       texObj = (struct gl_texture_object *)
@@ -1211,7 +1300,6 @@ _mesa_FramebufferTexture3DEXT(GLenum target, GLenum attachment,
    }
    ctx->Driver.RenderbufferTexture(ctx, att, texObj, textarget,
                                    level, zoffset);
-   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
 }
 
 
@@ -1221,13 +1309,35 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
                                  GLuint renderbuffer)
 {
    struct gl_renderbuffer_attachment *att;
+   struct gl_framebuffer *fb;
    struct gl_renderbuffer *rb;
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
-   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
-   if (target != GL_FRAMEBUFFER_EXT) {
+   switch (target) {
+#if FEATURE_EXT_framebuffer_blit
+   case GL_DRAW_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glFramebufferRenderbufferEXT(target)");
+         return;
+      }
+      fb = ctx->DrawBuffer;
+      break;
+   case GL_READ_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glFramebufferRenderbufferEXT(target)");
+         return;
+      }
+      fb = ctx->ReadBuffer;
+      break;
+#endif
+   case GL_FRAMEBUFFER_EXT:
+      fb = ctx->DrawBuffer;
+      break;
+   default:
       _mesa_error(ctx, GL_INVALID_ENUM,
                   "glFramebufferRenderbufferEXT(target)");
       return;
@@ -1239,13 +1349,13 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
       return;
    }
 
-   if (ctx->DrawBuffer->Name == 0) {
+   if (fb->Name == 0) {
       /* Can't attach new renderbuffers to a window system framebuffer */
       _mesa_error(ctx, GL_INVALID_OPERATION, "glFramebufferRenderbufferEXT");
       return;
    }
 
-   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
+   att = _mesa_get_attachment(ctx, fb, attachment);
    if (att == NULL) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                  "glFramebufferRenderbufferEXT(attachment)");
@@ -1265,10 +1375,10 @@ _mesa_FramebufferRenderbufferEXT(GLenum target, GLenum attachment,
       rb = NULL;
    }
 
-   assert(ctx->Driver.FramebufferRenderbuffer);
-   ctx->Driver.FramebufferRenderbuffer(ctx, att, rb);
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
-   _mesa_update_framebuffer_visual(ctx->DrawBuffer);
+   assert(ctx->Driver.FramebufferRenderbuffer);
+   ctx->Driver.FramebufferRenderbuffer(ctx, fb, attachment, rb);
 }
 
 
@@ -1277,29 +1387,54 @@ _mesa_GetFramebufferAttachmentParameterivEXT(GLenum target, GLenum attachment,
                                              GLenum pname, GLint *params)
 {
    const struct gl_renderbuffer_attachment *att;
+   struct gl_framebuffer *buffer;
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
 
-   if (target != GL_FRAMEBUFFER_EXT) {
+   switch (target) {
+#if FEATURE_EXT_framebuffer_blit
+   case GL_DRAW_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glGetFramebufferAttachmentParameterivEXT(target)");
+         return;
+      }
+      buffer = ctx->DrawBuffer;
+      break;
+   case GL_READ_FRAMEBUFFER_EXT:
+      if (!ctx->Extensions.EXT_framebuffer_blit) {
+         _mesa_error(ctx, GL_INVALID_ENUM,
+                     "glGetFramebufferAttachmentParameterivEXT(target)");
+         return;
+      }
+      buffer = ctx->ReadBuffer;
+      break;
+#endif
+   case GL_FRAMEBUFFER_EXT:
+      buffer = ctx->DrawBuffer;
+      break;
+   default:
       _mesa_error(ctx, GL_INVALID_ENUM,
                   "glGetFramebufferAttachmentParameterivEXT(target)");
       return;
    }
 
-   if (ctx->DrawBuffer->Name == 0) {
+   if (buffer->Name == 0) {
       _mesa_error(ctx, GL_INVALID_OPERATION,
                   "glGetFramebufferAttachmentParameterivEXT");
       return;
    }
 
-   att = get_attachment(ctx, ctx->DrawBuffer, attachment);
+   att = _mesa_get_attachment(ctx, buffer, attachment);
    if (att == NULL) {
       _mesa_error(ctx, GL_INVALID_ENUM,
                   "glGetFramebufferAttachmentParameterivEXT(attachment)");
       return;
    }
 
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
    switch (pname) {
    case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT:
       *params = att->Type;
@@ -1359,6 +1494,7 @@ _mesa_GenerateMipmapEXT(GLenum target)
    GET_CURRENT_CONTEXT(ctx);
 
    ASSERT_OUTSIDE_BEGIN_END(ctx);
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
 
    switch (target) {
    case GL_TEXTURE_1D:
@@ -1378,3 +1514,51 @@ _mesa_GenerateMipmapEXT(GLenum target)
    /* XXX this might not handle cube maps correctly */
    _mesa_generate_mipmap(ctx, target, texUnit, texObj);
 }
+
+
+#if FEATURE_EXT_framebuffer_blit
+void GLAPIENTRY
+_mesa_BlitFramebufferEXT(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
+                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
+                         GLbitfield mask, GLenum filter)
+{
+   GET_CURRENT_CONTEXT(ctx);
+
+   ASSERT_OUTSIDE_BEGIN_END(ctx);
+   FLUSH_VERTICES(ctx, _NEW_BUFFERS);
+
+   /* check for complete framebuffers */
+   if (ctx->DrawBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT ||
+       ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+                  "glBlitFramebufferEXT(incomplete draw/read buffers)");
+      return;
+   }
+
+   /* depth/stencil must be blitted with nearest filtering */
+   if ((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
+        && filter != GL_NEAREST) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+             "glBlitFramebufferEXT(depth/stencil requires GL_NEAREST filter");
+      return;
+   }
+
+   if (mask & ~(GL_COLOR_BUFFER_BIT |
+                GL_DEPTH_BUFFER_BIT |
+                GL_STENCIL_BUFFER_BIT)) {
+      _mesa_error( ctx, GL_INVALID_VALUE, "glBlitFramebufferEXT(mask)");
+      return;
+   }
+
+   if (!ctx->Extensions.EXT_framebuffer_blit) {
+      _mesa_error(ctx, GL_INVALID_OPERATION, "glBlitFramebufferEXT");
+      return;
+   }
+
+   ASSERT(ctx->Driver.BlitFramebuffer);
+   ctx->Driver.BlitFramebuffer(ctx,
+                               srcX0, srcY0, srcX1, srcY1,
+                               dstX0, dstY0, dstX1, dstY1,
+                               mask, filter);
+}
+#endif /* FEATURE_EXT_framebuffer_blit */