mesa: remove _mesa_tex_target_is_array
[mesa.git] / src / mesa / swrast / s_blit.c
index 3516a41bf41a84e73d3f66505ca8ad67f4375ef7..3e838a41d44b08149d0ee261a28fa8201d70ce1b 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * Mesa 3-D graphics library
 /*
  * Mesa 3-D graphics library
- * Version:  6.5
  *
  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
  *
  *
  * Copyright (C) 1999-2006  Brian Paul   All Rights Reserved.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
- * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
- * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
  */
 
 
  */
 
 
@@ -27,6 +27,9 @@
 #include "main/condrender.h"
 #include "main/image.h"
 #include "main/macros.h"
 #include "main/condrender.h"
 #include "main/image.h"
 #include "main/macros.h"
+#include "main/format_unpack.h"
+#include "main/format_pack.h"
+#include "main/condrender.h"
 #include "s_context.h"
 
 
 #include "s_context.h"
 
 
@@ -49,8 +52,8 @@ NAME(GLint srcWidth, GLint dstWidth,                  \
    if (flip) {                                         \
       for (dstCol = 0; dstCol < dstWidth; dstCol++) {  \
          GLint srcCol = (dstCol * srcWidth) / dstWidth;        \
    if (flip) {                                         \
       for (dstCol = 0; dstCol < dstWidth; dstCol++) {  \
          GLint srcCol = (dstCol * srcWidth) / dstWidth;        \
-         ASSERT(srcCol >= 0);                          \
-         ASSERT(srcCol < srcWidth);                    \
+         assert(srcCol >= 0);                          \
+         assert(srcCol < srcWidth);                    \
          srcCol = srcWidth - 1 - srcCol; /* flip */    \
          if (SIZE == 1) {                              \
             dst[dstCol] = src[srcCol];                 \
          srcCol = srcWidth - 1 - srcCol; /* flip */    \
          if (SIZE == 1) {                              \
             dst[dstCol] = src[srcCol];                 \
@@ -70,8 +73,8 @@ NAME(GLint srcWidth, GLint dstWidth,                  \
    else {                                              \
       for (dstCol = 0; dstCol < dstWidth; dstCol++) {  \
          GLint srcCol = (dstCol * srcWidth) / dstWidth;        \
    else {                                              \
       for (dstCol = 0; dstCol < dstWidth; dstCol++) {  \
          GLint srcCol = (dstCol * srcWidth) / dstWidth;        \
-         ASSERT(srcCol >= 0);                          \
-         ASSERT(srcCol < srcWidth);                    \
+         assert(srcCol >= 0);                          \
+         assert(srcCol < srcWidth);                    \
          if (SIZE == 1) {                              \
             dst[dstCol] = src[srcCol];                 \
          }                                             \
          if (SIZE == 1) {                              \
             dst[dstCol] = src[srcCol];                 \
          }                                             \
@@ -104,11 +107,16 @@ RESAMPLE(resample_row_16, GLuint, 4)
  */
 static void
 blit_nearest(struct gl_context *ctx,
  */
 static void
 blit_nearest(struct gl_context *ctx,
+             struct gl_framebuffer *readFb,
+             struct gl_framebuffer *drawFb,
              GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
              GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
              GLbitfield buffer)
 {
              GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
              GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
              GLbitfield buffer)
 {
-   struct gl_renderbuffer *readRb, *drawRb;
+   struct gl_renderbuffer *readRb, *drawRb = NULL;
+   struct gl_renderbuffer_attachment *readAtt = NULL, *drawAtt = NULL;
+   GLuint numDrawBuffers = 0;
+   GLuint i;
 
    const GLint srcWidth = ABS(srcX1 - srcX0);
    const GLint dstWidth = ABS(dstX1 - dstX0);
 
    const GLint srcWidth = ABS(srcX1 - srcX0);
    const GLint dstWidth = ABS(dstX1 - dstX0);
@@ -122,10 +130,18 @@ blit_nearest(struct gl_context *ctx,
 
    const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0);
    const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0);
 
    const GLboolean invertX = (srcX1 < srcX0) ^ (dstX1 < dstX0);
    const GLboolean invertY = (srcY1 < srcY0) ^ (dstY1 < dstY0);
-
+   enum mode {
+      DIRECT,
+      UNPACK_RGBA_FLOAT,
+      UNPACK_Z_FLOAT,
+      UNPACK_Z_INT,
+      UNPACK_S,
+   } mode = DIRECT;
+   GLubyte *srcMap, *dstMap;
+   GLint srcRowStride, dstRowStride;
    GLint dstRow;
 
    GLint dstRow;
 
-   GLint comps, pixelSize;
+   GLint pixelSize = 0;
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
@@ -136,114 +152,236 @@ blit_nearest(struct gl_context *ctx,
 
    switch (buffer) {
    case GL_COLOR_BUFFER_BIT:
 
    switch (buffer) {
    case GL_COLOR_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_ColorReadBuffer;
-      drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
-      comps = 4;
+      readAtt = &readFb->Attachment[readFb->_ColorReadBufferIndex];
+      readRb = readFb->_ColorReadBuffer;
+      numDrawBuffers = drawFb->_NumColorDrawBuffers;
       break;
    case GL_DEPTH_BUFFER_BIT:
       break;
    case GL_DEPTH_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_DepthBuffer;
-      drawRb = ctx->DrawBuffer->_DepthBuffer;
-      comps = 1;
+      readAtt = &readFb->Attachment[BUFFER_DEPTH];
+      drawAtt = &drawFb->Attachment[BUFFER_DEPTH];
+      readRb = readAtt->Renderbuffer;
+      drawRb = drawAtt->Renderbuffer;
+      numDrawBuffers = 1;
+
+      /* Note that for depth/stencil, the formats of src/dst must match.  By
+       * using the core helpers for pack/unpack, we avoid needing to handle
+       * masking for things like DEPTH copies of Z24S8.
+       */
+      if (readRb->Format == MESA_FORMAT_Z_FLOAT32 ||
+         readRb->Format == MESA_FORMAT_Z32_FLOAT_S8X24_UINT) {
+        mode = UNPACK_Z_FLOAT;
+      } else {
+        mode = UNPACK_Z_INT;
+      }
+      pixelSize = 4;
       break;
    case GL_STENCIL_BUFFER_BIT:
       break;
    case GL_STENCIL_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_StencilBuffer;
-      drawRb = ctx->DrawBuffer->_StencilBuffer;
-      comps = 1;
+      readAtt = &readFb->Attachment[BUFFER_STENCIL];
+      drawAtt = &drawFb->Attachment[BUFFER_STENCIL];
+      readRb = readAtt->Renderbuffer;
+      drawRb = drawAtt->Renderbuffer;
+      numDrawBuffers = 1;
+      mode = UNPACK_S;
+      pixelSize = 1;
       break;
    default:
       _mesa_problem(ctx, "unexpected buffer in blit_nearest()");
       return;
    }
 
       break;
    default:
       _mesa_problem(ctx, "unexpected buffer in blit_nearest()");
       return;
    }
 
-   switch (readRb->DataType) {
-   case GL_UNSIGNED_BYTE:
-      pixelSize = comps * sizeof(GLubyte);
-      break;
-   case GL_UNSIGNED_SHORT:
-      pixelSize = comps * sizeof(GLushort);
-      break;
-   case GL_UNSIGNED_INT:
-      pixelSize = comps * sizeof(GLuint);
-      break;
-   case GL_FLOAT:
-      pixelSize = comps * sizeof(GLfloat);
-      break;
-   default:
-      _mesa_problem(ctx, "unexpected buffer type (0x%x) in blit_nearest",
-                    readRb->DataType);
-      return;
-   }
-
-   /* choose row resampler */
-   switch (pixelSize) {
-   case 1:
-      resampleRow = resample_row_1;
-      break;
-   case 2:
-      resampleRow = resample_row_2;
-      break;
-   case 4:
-      resampleRow = resample_row_4;
-      break;
-   case 8:
-      resampleRow = resample_row_8;
-      break;
-   case 16:
-      resampleRow = resample_row_16;
-      break;
-   default:
-      _mesa_problem(ctx, "unexpected pixel size (%d) in blit_nearest",
-                    pixelSize);
-      return;
-   }
-
    /* allocate the src/dst row buffers */
    /* allocate the src/dst row buffers */
-   srcBuffer = malloc(pixelSize * srcWidth);
-   if (!srcBuffer) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
-   dstBuffer = malloc(pixelSize * dstWidth);
-   if (!dstBuffer) {
-      free(srcBuffer);
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
+   srcBuffer = malloc(MAX_PIXEL_BYTES * srcWidth);
+   dstBuffer = malloc(MAX_PIXEL_BYTES * dstWidth);
+   if (!srcBuffer || !dstBuffer)
+      goto fail_no_memory;
+
+   /* Blit to all the draw buffers */
+   for (i = 0; i < numDrawBuffers; i++) {
+      if (buffer == GL_COLOR_BUFFER_BIT) {
+         int idx = drawFb->_ColorDrawBufferIndexes[i];
+         if (idx == -1)
+            continue;
+         drawAtt = &drawFb->Attachment[idx];
+         drawRb = drawAtt->Renderbuffer;
+
+         if (!drawRb)
+            continue;
+
+         if (readRb->Format == drawRb->Format) {
+            mode = DIRECT;
+            pixelSize = _mesa_get_format_bytes(readRb->Format);
+         } else {
+            mode = UNPACK_RGBA_FLOAT;
+            pixelSize = 16;
+         }
+      }
+
+      /* choose row resampler */
+      switch (pixelSize) {
+      case 1:
+         resampleRow = resample_row_1;
+         break;
+      case 2:
+         resampleRow = resample_row_2;
+         break;
+      case 4:
+         resampleRow = resample_row_4;
+         break;
+      case 8:
+         resampleRow = resample_row_8;
+         break;
+      case 16:
+         resampleRow = resample_row_16;
+         break;
+      default:
+         _mesa_problem(ctx, "unexpected pixel size (%d) in blit_nearest",
+                       pixelSize);
+         goto fail;
+      }
 
 
-   for (dstRow = 0; dstRow < dstHeight; dstRow++) {
-      const GLint dstY = dstYpos + dstRow;
-      GLint srcRow = (dstRow * srcHeight) / dstHeight;
-      GLint srcY;
+      if ((readRb == drawRb) ||
+          (readAtt->Texture && drawAtt->Texture &&
+           (readAtt->Texture == drawAtt->Texture))) {
+         /* map whole buffer for read/write */
+         /* XXX we could be clever and just map the union region of the
+          * source and dest rects.
+          */
+         GLubyte *map;
+         GLint rowStride;
+         GLint formatSize = _mesa_get_format_bytes(readRb->Format);
+
+         ctx->Driver.MapRenderbuffer(ctx, readRb, 0, 0,
+                                     readRb->Width, readRb->Height,
+                                     GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
+                                     &map, &rowStride);
+         if (!map) {
+            goto fail_no_memory;
+         }
 
 
-      ASSERT(srcRow >= 0);
-      ASSERT(srcRow < srcHeight);
+         srcMap = map + srcYpos * rowStride + srcXpos * formatSize;
+         dstMap = map + dstYpos * rowStride + dstXpos * formatSize;
 
 
-      if (invertY) {
-         srcRow = srcHeight - 1 - srcRow;
+         /* this handles overlapping copies */
+         if (srcY0 < dstY0) {
+            /* copy in reverse (top->down) order */
+            srcMap += rowStride * (readRb->Height - 1);
+            dstMap += rowStride * (readRb->Height - 1);
+            srcRowStride = -rowStride;
+            dstRowStride = -rowStride;
+         }
+         else {
+            /* copy in normal (bottom->up) order */
+            srcRowStride = rowStride;
+            dstRowStride = rowStride;
+         }
+      }
+      else {
+         /* different src/dst buffers */
+         ctx->Driver.MapRenderbuffer(ctx, readRb,
+                                     srcXpos, srcYpos,
+                                     srcWidth, srcHeight,
+                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride);
+         if (!srcMap) {
+            goto fail_no_memory;
+         }
+         ctx->Driver.MapRenderbuffer(ctx, drawRb,
+                                     dstXpos, dstYpos,
+                                     dstWidth, dstHeight,
+                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+         if (!dstMap) {
+            ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+            goto fail_no_memory;
+         }
       }
 
       }
 
-      srcY = srcYpos + srcRow;
+      for (dstRow = 0; dstRow < dstHeight; dstRow++) {
+         GLfloat srcRowF = (dstRow + 0.5F) / dstHeight * srcHeight - 0.5F;
+         GLint srcRow = IROUND(srcRowF);
+         GLubyte *dstRowStart = dstMap + dstRowStride * dstRow;
+
+         assert(srcRow >= 0);
+         assert(srcRow < srcHeight);
+
+         if (invertY) {
+            srcRow = srcHeight - 1 - srcRow;
+         }
+
+         /* get pixel row from source and resample to match dest width */
+         if (prevY != srcRow) {
+            GLubyte *srcRowStart = srcMap + srcRowStride * srcRow;
+
+            switch (mode) {
+            case DIRECT:
+               memcpy(srcBuffer, srcRowStart, pixelSize * srcWidth);
+               break;
+            case UNPACK_RGBA_FLOAT:
+               _mesa_unpack_rgba_row(readRb->Format, srcWidth, srcRowStart,
+                                     srcBuffer);
+               break;
+            case UNPACK_Z_FLOAT:
+               _mesa_unpack_float_z_row(readRb->Format, srcWidth, srcRowStart,
+                                        srcBuffer);
+               break;
+            case UNPACK_Z_INT:
+               _mesa_unpack_uint_z_row(readRb->Format, srcWidth, srcRowStart,
+                                       srcBuffer);
+               break;
+            case UNPACK_S:
+               _mesa_unpack_ubyte_stencil_row(readRb->Format, srcWidth,
+                                              srcRowStart, srcBuffer);
+               break;
+            }
+
+            (*resampleRow)(srcWidth, dstWidth, srcBuffer, dstBuffer, invertX);
+            prevY = srcRow;
+         }
 
 
-      /* get pixel row from source and resample to match dest width */
-      if (prevY != srcY) {
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY, srcBuffer);
-         (*resampleRow)(srcWidth, dstWidth, srcBuffer, dstBuffer, invertX);
-         prevY = srcY;
+         /* store pixel row in destination */
+         switch (mode) {
+         case DIRECT:
+            memcpy(dstRowStart, dstBuffer, pixelSize * dstWidth);
+            break;
+         case UNPACK_RGBA_FLOAT:
+            _mesa_pack_float_rgba_row(drawRb->Format, dstWidth, dstBuffer,
+                                      dstRowStart);
+            break;
+         case UNPACK_Z_FLOAT:
+            _mesa_pack_float_z_row(drawRb->Format, dstWidth, dstBuffer,
+                                   dstRowStart);
+            break;
+         case UNPACK_Z_INT:
+            _mesa_pack_uint_z_row(drawRb->Format, dstWidth, dstBuffer,
+                                  dstRowStart);
+            break;
+         case UNPACK_S:
+            _mesa_pack_ubyte_stencil_row(drawRb->Format, dstWidth, dstBuffer,
+                                         dstRowStart);
+            break;
+         }
       }
 
       }
 
-      /* store pixel row in destination */
-      drawRb->PutRow(ctx, drawRb, dstWidth, dstXpos, dstY, dstBuffer, NULL);
+      ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+      if (drawRb != readRb) {
+         ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
+      }
    }
 
    }
 
+fail:
+   free(srcBuffer);
+   free(dstBuffer);
+   return;
+
+fail_no_memory:
    free(srcBuffer);
    free(dstBuffer);
    free(srcBuffer);
    free(dstBuffer);
+   _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBuffer");
 }
 
 
 
 #define LERP(T, A, B)  ( (A) + (T) * ((B) - (A)) )
 
 }
 
 
 
 #define LERP(T, A, B)  ( (A) + (T) * ((B) - (A)) )
 
-static INLINE GLfloat
+static inline GLfloat
 lerp_2d(GLfloat a, GLfloat b,
         GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
 {
 lerp_2d(GLfloat a, GLfloat b,
         GLfloat v00, GLfloat v10, GLfloat v01, GLfloat v11)
 {
@@ -265,19 +403,17 @@ resample_linear_row_ub(GLint srcWidth, GLint dstWidth,
    const GLubyte (*srcColor0)[4] = (const GLubyte (*)[4]) srcBuffer0;
    const GLubyte (*srcColor1)[4] = (const GLubyte (*)[4]) srcBuffer1;
    GLubyte (*dstColor)[4] = (GLubyte (*)[4]) dstBuffer;
    const GLubyte (*srcColor0)[4] = (const GLubyte (*)[4]) srcBuffer0;
    const GLubyte (*srcColor1)[4] = (const GLubyte (*)[4]) srcBuffer1;
    GLubyte (*dstColor)[4] = (GLubyte (*)[4]) dstBuffer;
-   const GLfloat dstWidthF = (GLfloat) dstWidth;
    GLint dstCol;
 
    for (dstCol = 0; dstCol < dstWidth; dstCol++) {
    GLint dstCol;
 
    for (dstCol = 0; dstCol < dstWidth; dstCol++) {
-      const GLfloat srcCol = (dstCol * srcWidth) / dstWidthF;
-      GLint srcCol0 = IFLOOR(srcCol);
+      const GLfloat srcCol = (dstCol + 0.5F) / dstWidth * srcWidth - 0.5F;
+      GLint srcCol0 = MAX2(0, IFLOOR(srcCol));
       GLint srcCol1 = srcCol0 + 1;
       GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
       GLfloat red, green, blue, alpha;
 
       GLint srcCol1 = srcCol0 + 1;
       GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
       GLfloat red, green, blue, alpha;
 
-      ASSERT(srcCol0 >= 0);
-      ASSERT(srcCol0 < srcWidth);
-      ASSERT(srcCol1 <= srcWidth);
+      assert(srcCol0 < srcWidth);
+      assert(srcCol1 <= srcWidth);
 
       if (srcCol1 == srcWidth) {
          /* last column fudge */
 
       if (srcCol1 == srcWidth) {
          /* last column fudge */
@@ -311,23 +447,80 @@ resample_linear_row_ub(GLint srcWidth, GLint dstWidth,
 }
 
 
 }
 
 
+/**
+ * Bilinear interpolation of two source rows.  floating point pixels.
+ */
+static void
+resample_linear_row_float(GLint srcWidth, GLint dstWidth,
+                          const GLvoid *srcBuffer0, const GLvoid *srcBuffer1,
+                          GLvoid *dstBuffer, GLboolean flip, GLfloat rowWeight)
+{
+   const GLfloat (*srcColor0)[4] = (const GLfloat (*)[4]) srcBuffer0;
+   const GLfloat (*srcColor1)[4] = (const GLfloat (*)[4]) srcBuffer1;
+   GLfloat (*dstColor)[4] = (GLfloat (*)[4]) dstBuffer;
+   GLint dstCol;
+
+   for (dstCol = 0; dstCol < dstWidth; dstCol++) {
+      const GLfloat srcCol = (dstCol + 0.5F) / dstWidth * srcWidth - 0.5F;
+      GLint srcCol0 = MAX2(0, IFLOOR(srcCol));
+      GLint srcCol1 = srcCol0 + 1;
+      GLfloat colWeight = srcCol - srcCol0; /* fractional part of srcCol */
+      GLfloat red, green, blue, alpha;
+
+      assert(srcCol0 < srcWidth);
+      assert(srcCol1 <= srcWidth);
+
+      if (srcCol1 == srcWidth) {
+         /* last column fudge */
+         srcCol1--;
+         colWeight = 0.0;
+      }
+
+      if (flip) {
+         srcCol0 = srcWidth - 1 - srcCol0;
+         srcCol1 = srcWidth - 1 - srcCol1;
+      }
+
+      red = lerp_2d(colWeight, rowWeight,
+                    srcColor0[srcCol0][RCOMP], srcColor0[srcCol1][RCOMP],
+                    srcColor1[srcCol0][RCOMP], srcColor1[srcCol1][RCOMP]);
+      green = lerp_2d(colWeight, rowWeight,
+                    srcColor0[srcCol0][GCOMP], srcColor0[srcCol1][GCOMP],
+                    srcColor1[srcCol0][GCOMP], srcColor1[srcCol1][GCOMP]);
+      blue = lerp_2d(colWeight, rowWeight,
+                    srcColor0[srcCol0][BCOMP], srcColor0[srcCol1][BCOMP],
+                    srcColor1[srcCol0][BCOMP], srcColor1[srcCol1][BCOMP]);
+      alpha = lerp_2d(colWeight, rowWeight,
+                    srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP],
+                    srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]);
+      
+      dstColor[dstCol][RCOMP] = red;
+      dstColor[dstCol][GCOMP] = green;
+      dstColor[dstCol][BCOMP] = blue;
+      dstColor[dstCol][ACOMP] = alpha;
+   }
+}
+
+
 
 /**
 
 /**
- * Bilinear filtered blit (color only).
+ * Bilinear filtered blit (color only, non-integer values).
  */
 static void
 blit_linear(struct gl_context *ctx,
  */
 static void
 blit_linear(struct gl_context *ctx,
+            struct gl_framebuffer *readFb,
+            struct gl_framebuffer *drawFb,
             GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
             GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1)
 {
             GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
             GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1)
 {
-   struct gl_renderbuffer *readRb = ctx->ReadBuffer->_ColorReadBuffer;
-   struct gl_renderbuffer *drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
+   struct gl_renderbuffer *readRb = readFb->_ColorReadBuffer;
+   struct gl_renderbuffer_attachment *readAtt =
+      &readFb->Attachment[readFb->_ColorReadBufferIndex];
 
    const GLint srcWidth = ABS(srcX1 - srcX0);
    const GLint dstWidth = ABS(dstX1 - dstX0);
    const GLint srcHeight = ABS(srcY1 - srcY0);
    const GLint dstHeight = ABS(dstY1 - dstY0);
 
    const GLint srcWidth = ABS(srcX1 - srcX0);
    const GLint dstWidth = ABS(dstX1 - dstX0);
    const GLint srcHeight = ABS(srcY1 - srcY0);
    const GLint dstHeight = ABS(dstY1 - dstY0);
-   const GLfloat dstHeightF = (GLfloat) dstHeight;
 
    const GLint srcXpos = MIN2(srcX0, srcX1);
    const GLint srcYpos = MIN2(srcY0, srcY1);
 
    const GLint srcXpos = MIN2(srcX0, srcX1);
    const GLint srcYpos = MIN2(srcY0, srcY1);
@@ -344,219 +537,204 @@ blit_linear(struct gl_context *ctx,
    GLint srcBufferY0 = -1, srcBufferY1 = -1;
    GLvoid *dstBuffer;
 
    GLint srcBufferY0 = -1, srcBufferY1 = -1;
    GLvoid *dstBuffer;
 
-   switch (readRb->DataType) {
-   case GL_UNSIGNED_BYTE:
+   mesa_format readFormat = _mesa_get_srgb_format_linear(readRb->Format);
+   GLuint bpp = _mesa_get_format_bytes(readFormat);
+
+   GLenum pixelType;
+
+   GLubyte *srcMap, *dstMap;
+   GLint srcRowStride, dstRowStride;
+   GLuint i;
+
+
+   /* Determine datatype for resampling */
+   if (_mesa_get_format_max_bits(readFormat) == 8 &&
+       _mesa_get_format_datatype(readFormat) == GL_UNSIGNED_NORMALIZED) {
+      pixelType = GL_UNSIGNED_BYTE;
       pixelSize = 4 * sizeof(GLubyte);
       pixelSize = 4 * sizeof(GLubyte);
-      break;
-   case GL_UNSIGNED_SHORT:
-      pixelSize = 4 * sizeof(GLushort);
-      break;
-   case GL_UNSIGNED_INT:
-      pixelSize = 4 * sizeof(GLuint);
-      break;
-   case GL_FLOAT:
+   }
+   else {
+      pixelType = GL_FLOAT;
       pixelSize = 4 * sizeof(GLfloat);
       pixelSize = 4 * sizeof(GLfloat);
-      break;
-   default:
-      _mesa_problem(ctx, "unexpected buffer type (0x%x) in blit_nearest",
-                    readRb->DataType);
-      return;
    }
 
    /* Allocate the src/dst row buffers.
     * Keep two adjacent src rows around for bilinear sampling.
     */
    srcBuffer0 = malloc(pixelSize * srcWidth);
    }
 
    /* Allocate the src/dst row buffers.
     * Keep two adjacent src rows around for bilinear sampling.
     */
    srcBuffer0 = malloc(pixelSize * srcWidth);
-   if (!srcBuffer0) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
    srcBuffer1 = malloc(pixelSize * srcWidth);
    srcBuffer1 = malloc(pixelSize * srcWidth);
-   if (!srcBuffer1) {
-      free(srcBuffer0);
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
    dstBuffer = malloc(pixelSize * dstWidth);
    dstBuffer = malloc(pixelSize * dstWidth);
-   if (!dstBuffer) {
-      free(srcBuffer0);
-      free(srcBuffer1);
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
+   if (!srcBuffer0 || !srcBuffer1 || !dstBuffer) {
+      goto fail_no_memory;
    }
 
    }
 
-   for (dstRow = 0; dstRow < dstHeight; dstRow++) {
-      const GLint dstY = dstYpos + dstRow;
-      const GLfloat srcRow = (dstRow * srcHeight) / dstHeightF;
-      GLint srcRow0 = IFLOOR(srcRow);
-      GLint srcRow1 = srcRow0 + 1;
-      GLfloat rowWeight = srcRow - srcRow0; /* fractional part of srcRow */
-
-      ASSERT(srcRow >= 0);
-      ASSERT(srcRow < srcHeight);
+   for (i = 0; i < drawFb->_NumColorDrawBuffers; i++) {
+      GLint idx = drawFb->_ColorDrawBufferIndexes[i];
+      struct gl_renderbuffer_attachment *drawAtt;
+      struct gl_renderbuffer *drawRb;
+      mesa_format drawFormat;
 
 
-      if (srcRow1 == srcHeight) {
-         /* last row fudge */
-         srcRow1 = srcRow0;
-         rowWeight = 0.0;
-      }
+      if (idx == -1)
+         continue;
 
 
-      if (invertY) {
-         srcRow0 = srcHeight - 1 - srcRow0;
-         srcRow1 = srcHeight - 1 - srcRow1;
-      }
+      drawAtt = &drawFb->Attachment[idx];
+      drawRb = drawAtt->Renderbuffer;
+      if (!drawRb)
+         continue;
 
 
-      srcY0 = srcYpos + srcRow0;
-      srcY1 = srcYpos + srcRow1;
+      drawFormat = _mesa_get_srgb_format_linear(drawRb->Format);
 
 
-      /* get the two source rows */
-      if (srcY0 == srcBufferY0 && srcY1 == srcBufferY1) {
-         /* use same source row buffers again */
-      }
-      else if (srcY0 == srcBufferY1) {
-         /* move buffer1 into buffer0 by swapping pointers */
-         GLvoid *tmp = srcBuffer0;
-         srcBuffer0 = srcBuffer1;
-         srcBuffer1 = tmp;
-         /* get y1 row */
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY1, srcBuffer1);
-         srcBufferY0 = srcY0;
-         srcBufferY1 = srcY1;
-      }
-      else {
-         /* get both new rows */
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY0, srcBuffer0);
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY1, srcBuffer1);
-         srcBufferY0 = srcY0;
-         srcBufferY1 = srcY1;
-      }
+      /*
+       * Map src / dst renderbuffers
+       */
+      if ((readRb == drawRb) ||
+          (readAtt->Texture && drawAtt->Texture &&
+           (readAtt->Texture == drawAtt->Texture))) {
+         /* map whole buffer for read/write */
+         ctx->Driver.MapRenderbuffer(ctx, readRb,
+                                     0, 0, readRb->Width, readRb->Height,
+                                     GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
+                                     &srcMap, &srcRowStride);
+         if (!srcMap) {
+            goto fail_no_memory;
+         }
 
 
-      if (readRb->DataType == GL_UNSIGNED_BYTE) {
-         resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
-                                dstBuffer, invertX, rowWeight);
+         dstMap = srcMap;
+         dstRowStride = srcRowStride;
       }
       else {
       }
       else {
-         _mesa_problem(ctx, "Unsupported color channel type in sw blit");
-         break;
+         /* different src/dst buffers */
+         /* XXX with a bit of work we could just map the regions to be
+          * read/written instead of the whole buffers.
+          */
+         ctx->Driver.MapRenderbuffer(ctx, readRb,
+                                     0, 0, readRb->Width, readRb->Height,
+                                     GL_MAP_READ_BIT, &srcMap, &srcRowStride);
+         if (!srcMap) {
+            goto fail_no_memory;
+         }
+         ctx->Driver.MapRenderbuffer(ctx, drawRb,
+                                     0, 0, drawRb->Width, drawRb->Height,
+                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+         if (!dstMap) {
+            ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+            goto fail_no_memory;
+         }
       }
 
       }
 
-      /* store pixel row in destination */
-      drawRb->PutRow(ctx, drawRb, dstWidth, dstXpos, dstY, dstBuffer, NULL);
-   }
-
-   free(srcBuffer0);
-   free(srcBuffer1);
-   free(dstBuffer);
-}
+      for (dstRow = 0; dstRow < dstHeight; dstRow++) {
+         const GLint dstY = dstYpos + dstRow;
+         GLfloat srcRow = (dstRow + 0.5F) / dstHeight * srcHeight - 0.5F;
+         GLint srcRow0 = MAX2(0, IFLOOR(srcRow));
+         GLint srcRow1 = srcRow0 + 1;
+         GLfloat rowWeight = srcRow - srcRow0; /* fractional part of srcRow */
+
+         if (srcRow1 == srcHeight) {
+            /* last row fudge */
+            srcRow1 = srcRow0;
+            rowWeight = 0.0;
+         }
 
 
+         if (invertY) {
+            srcRow0 = srcHeight - 1 - srcRow0;
+            srcRow1 = srcHeight - 1 - srcRow1;
+         }
 
 
-/**
- * Simple case:  Blit color, depth or stencil with no scaling or flipping.
- * XXX we could easily support vertical flipping here.
- */
-static void
-simple_blit(struct gl_context *ctx,
-            GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
-            GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
-            GLbitfield buffer)
-{
-   struct gl_renderbuffer *readRb, *drawRb;
-   const GLint width = srcX1 - srcX0;
-   const GLint height = srcY1 - srcY0;
-   GLint row, srcY, dstY, yStep;
-   GLint comps, bytesPerRow;
-   void *rowBuffer;
-
-   /* only one buffer */
-   ASSERT(_mesa_bitcount(buffer) == 1);
-   /* no flipping checks */
-   ASSERT(srcX0 < srcX1);
-   ASSERT(srcY0 < srcY1);
-   ASSERT(dstX0 < dstX1);
-   ASSERT(dstY0 < dstY1);
-   /* size checks */
-   ASSERT(srcX1 - srcX0 == dstX1 - dstX0);
-   ASSERT(srcY1 - srcY0 == dstY1 - dstY0);
-
-   /* determine if copy should be bottom-to-top or top-to-bottom */
-   if (srcY0 > dstY0) {
-      /* src above dst: copy bottom-to-top */
-      yStep = 1;
-      srcY = srcY0;
-      dstY = dstY0;
-   }
-   else {
-      /* src below dst: copy top-to-bottom */
-      yStep = -1;
-      srcY = srcY1 - 1;
-      dstY = dstY1 - 1;
-   }
+         srcY0 = srcYpos + srcRow0;
+         srcY1 = srcYpos + srcRow1;
 
 
-   switch (buffer) {
-   case GL_COLOR_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_ColorReadBuffer;
-      drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
-      comps = 4;
-      break;
-   case GL_DEPTH_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_DepthBuffer;
-      drawRb = ctx->DrawBuffer->_DepthBuffer;
-      comps = 1;
-      break;
-   case GL_STENCIL_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_StencilBuffer;
-      drawRb = ctx->DrawBuffer->_StencilBuffer;
-      comps = 1;
-      break;
-   default:
-      _mesa_problem(ctx, "unexpected buffer in simple_blit()");
-      return;
-   }
+         /* get the two source rows */
+         if (srcY0 == srcBufferY0 && srcY1 == srcBufferY1) {
+            /* use same source row buffers again */
+         }
+         else if (srcY0 == srcBufferY1) {
+            /* move buffer1 into buffer0 by swapping pointers */
+            GLvoid *tmp = srcBuffer0;
+            srcBuffer0 = srcBuffer1;
+            srcBuffer1 = tmp;
+            /* get y1 row */
+            {
+               GLubyte *src = srcMap + srcY1 * srcRowStride + srcXpos * bpp;
+               if (pixelType == GL_UNSIGNED_BYTE) {
+                  _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
+                                              src, srcBuffer1);
+               }
+               else {
+                  _mesa_unpack_rgba_row(readFormat, srcWidth,
+                                        src, srcBuffer1);
+               }
+            }
+            srcBufferY0 = srcY0;
+            srcBufferY1 = srcY1;
+         }
+         else {
+            /* get both new rows */
+            {
+               GLubyte *src0 = srcMap + srcY0 * srcRowStride + srcXpos * bpp;
+               GLubyte *src1 = srcMap + srcY1 * srcRowStride + srcXpos * bpp;
+               if (pixelType == GL_UNSIGNED_BYTE) {
+                  _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
+                                              src0, srcBuffer0);
+                  _mesa_unpack_ubyte_rgba_row(readFormat, srcWidth,
+                                              src1, srcBuffer1);
+               }
+               else {
+                  _mesa_unpack_rgba_row(readFormat, srcWidth, src0, srcBuffer0);
+                  _mesa_unpack_rgba_row(readFormat, srcWidth, src1, srcBuffer1);
+               }
+            }
+            srcBufferY0 = srcY0;
+            srcBufferY1 = srcY1;
+         }
 
 
-   ASSERT(readRb->DataType == drawRb->DataType);
+         if (pixelType == GL_UNSIGNED_BYTE) {
+            resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
+                                   dstBuffer, invertX, rowWeight);
+         }
+         else {
+            resample_linear_row_float(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
+                                      dstBuffer, invertX, rowWeight);
+         }
 
 
-   /* compute bytes per row */
-   switch (readRb->DataType) {
-   case GL_UNSIGNED_BYTE:
-      bytesPerRow = comps * width * sizeof(GLubyte);
-      break;
-   case GL_UNSIGNED_SHORT:
-      bytesPerRow = comps * width * sizeof(GLushort);
-      break;
-   case GL_UNSIGNED_INT:
-      bytesPerRow = comps * width * sizeof(GLuint);
-      break;
-   case GL_FLOAT:
-      bytesPerRow = comps * width * sizeof(GLfloat);
-      break;
-   default:
-      _mesa_problem(ctx, "unexpected buffer type in simple_blit");
-      return;
-   }
+         /* store pixel row in destination */
+         {
+            GLubyte *dst = dstMap + dstY * dstRowStride + dstXpos * bpp;
+            if (pixelType == GL_UNSIGNED_BYTE) {
+               _mesa_pack_ubyte_rgba_row(drawFormat, dstWidth, dstBuffer, dst);
+            }
+            else {
+               _mesa_pack_float_rgba_row(drawFormat, dstWidth, dstBuffer, dst);
+            }
+         }
+      }
 
 
-   /* allocate the row buffer */
-   rowBuffer = malloc(bytesPerRow);
-   if (!rowBuffer) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
+      ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+      if (drawRb != readRb) {
+         ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
+      }
    }
 
    }
 
-   for (row = 0; row < height; row++) {
-      readRb->GetRow(ctx, readRb, width, srcX0, srcY, rowBuffer);
-      drawRb->PutRow(ctx, drawRb, width, dstX0, dstY, rowBuffer, NULL);
-      srcY += yStep;
-      dstY += yStep;
-   }
+   free(srcBuffer0);
+   free(srcBuffer1);
+   free(dstBuffer);
+   return;
 
 
-   free(rowBuffer);
+fail_no_memory:
+   free(srcBuffer0);
+   free(srcBuffer1);
+   free(dstBuffer);
+   _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
 }
 
 
 }
 
 
+
 /**
  * Software fallback for glBlitFramebufferEXT().
  */
 void
 _swrast_BlitFramebuffer(struct gl_context *ctx,
 /**
  * Software fallback for glBlitFramebufferEXT().
  */
 void
 _swrast_BlitFramebuffer(struct gl_context *ctx,
+                        struct gl_framebuffer *readFb,
+                        struct gl_framebuffer *drawFb,
                         GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                         GLbitfield mask, GLenum filter)
                         GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                         GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                         GLbitfield mask, GLenum filter)
@@ -566,51 +744,68 @@ _swrast_BlitFramebuffer(struct gl_context *ctx,
       GL_DEPTH_BUFFER_BIT,
       GL_STENCIL_BUFFER_BIT
    };
       GL_DEPTH_BUFFER_BIT,
       GL_STENCIL_BUFFER_BIT
    };
+   static const GLenum buffer_enums[3] = {
+      GL_COLOR,
+      GL_DEPTH,
+      GL_STENCIL,
+   };
    GLint i;
 
    GLint i;
 
-   if (!ctx->DrawBuffer->_NumColorDrawBuffers)
-      return;
+   /* Page 679 of OpenGL 4.4 spec says:
+    *    "Added BlitFramebuffer to commands affected by conditional rendering in
+    *     section 10.10 (Bug 9562)."
+    */
+   if (!_mesa_check_conditional_render(ctx))
+      return; /* Do not blit */
 
 
-   if (!_mesa_clip_blit(ctx, &srcX0, &srcY0, &srcX1, &srcY1,
+   if (!_mesa_clip_blit(ctx, readFb, drawFb, &srcX0, &srcY0, &srcX1, &srcY1,
                         &dstX0, &dstY0, &dstX1, &dstY1)) {
       return;
    }
 
                         &dstX0, &dstY0, &dstX1, &dstY1)) {
       return;
    }
 
-   swrast_render_start(ctx);
+   if (SWRAST_CONTEXT(ctx)->NewState)
+      _swrast_validate_derived(ctx);
 
 
+   /* First, try covering whatever buffers possible using the fast 1:1 copy
+    * path.
+    */
    if (srcX1 - srcX0 == dstX1 - dstX0 &&
        srcY1 - srcY0 == dstY1 - dstY0 &&
        srcX0 < srcX1 &&
        srcY0 < srcY1 &&
        dstX0 < dstX1 &&
        dstY0 < dstY1) {
    if (srcX1 - srcX0 == dstX1 - dstX0 &&
        srcY1 - srcY0 == dstY1 - dstY0 &&
        srcX0 < srcX1 &&
        srcY0 < srcY1 &&
        dstX0 < dstX1 &&
        dstY0 < dstY1) {
-      /* no stretching or flipping.
-       * filter doesn't matter.
-       */
       for (i = 0; i < 3; i++) {
          if (mask & buffers[i]) {
       for (i = 0; i < 3; i++) {
          if (mask & buffers[i]) {
-            simple_blit(ctx, srcX0, srcY0, srcX1, srcY1,
-                        dstX0, dstY0, dstX1, dstY1, buffers[i]);
+            if (swrast_fast_copy_pixels(ctx,
+                                        readFb, drawFb,
+                                        srcX0, srcY0,
+                                        srcX1 - srcX0, srcY1 - srcY0,
+                                        dstX0, dstY0,
+                                        buffer_enums[i])) {
+               mask &= ~buffers[i];
+            }
          }
       }
          }
       }
+
+      if (!mask)
+         return;
    }
    }
-   else {
-      if (filter == GL_NEAREST) {
-         for (i = 0; i < 3; i++) {
-            if (mask & buffers[i]) {
-               blit_nearest(ctx,  srcX0, srcY0, srcX1, srcY1,
-                            dstX0, dstY0, dstX1, dstY1, buffers[i]);
-            }
-         }
+
+   if (filter == GL_NEAREST) {
+      for (i = 0; i < 3; i++) {
+          if (mask & buffers[i]) {
+             blit_nearest(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1,
+                          dstX0, dstY0, dstX1, dstY1, buffers[i]);
+          }
       }
       }
-      else {
-         ASSERT(filter == GL_LINEAR);
-         if (mask & GL_COLOR_BUFFER_BIT) {  /* depth/stencil not allowed */
-            blit_linear(ctx,  srcX0, srcY0, srcX1, srcY1,
-                        dstX0, dstY0, dstX1, dstY1);
-         }
+   }
+   else {
+      assert(filter == GL_LINEAR);
+      if (mask & GL_COLOR_BUFFER_BIT) {  /* depth/stencil not allowed */
+         blit_linear(ctx, readFb, drawFb, srcX0, srcY0, srcX1, srcY1,
+                     dstX0, dstY0, dstX1, dstY1);
       }
    }
 
       }
    }
 
-   swrast_render_finish(ctx);
 }
 }