mesa: move more swrast-related #defines out of core Mesa
[mesa.git] / src / mesa / swrast / s_blit.c
index 803ad2e899da9d1e5f95be316891528695b3e0e6..6d0b889c1950b44560e900f0f6109d8a9992585d 100644 (file)
@@ -27,6 +27,8 @@
 #include "main/condrender.h"
 #include "main/image.h"
 #include "main/macros.h"
+#include "main/format_unpack.h"
+#include "main/format_pack.h"
 #include "s_context.h"
 
 
@@ -122,10 +124,18 @@ blit_nearest(struct gl_context *ctx,
 
    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;
+   GLubyte *srcMap, *dstMap;
+   GLint srcRowStride, dstRowStride;
    GLint dstRow;
 
-   GLint comps, pixelSize;
+   GLint pixelSize;
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
@@ -138,42 +148,43 @@ blit_nearest(struct gl_context *ctx,
    case GL_COLOR_BUFFER_BIT:
       readRb = ctx->ReadBuffer->_ColorReadBuffer;
       drawRb = ctx->DrawBuffer->_ColorDrawBuffers[0];
-      comps = 4;
+
+      if (readRb->Format == drawRb->Format) {
+        mode = DIRECT;
+        pixelSize = _mesa_get_format_bytes(readRb->Format);
+      } else {
+        mode = UNPACK_RGBA_FLOAT;
+        pixelSize = 16;
+      }
+
       break;
    case GL_DEPTH_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_DepthBuffer;
-      drawRb = ctx->DrawBuffer->_DepthBuffer;
-      comps = 1;
+      readRb = ctx->ReadBuffer->Attachment[BUFFER_DEPTH].Renderbuffer;
+      drawRb = ctx->DrawBuffer->Attachment[BUFFER_DEPTH].Renderbuffer;
+
+      /* 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_Z32_FLOAT ||
+         readRb->Format == MESA_FORMAT_Z32_FLOAT_X24S8) {
+        mode = UNPACK_Z_FLOAT;
+      } else {
+        mode = UNPACK_Z_INT;
+      }
+      pixelSize = 4;
       break;
    case GL_STENCIL_BUFFER_BIT:
-      readRb = ctx->ReadBuffer->_StencilBuffer;
-      drawRb = ctx->DrawBuffer->_StencilBuffer;
-      comps = 1;
+      readRb = ctx->ReadBuffer->Attachment[BUFFER_STENCIL].Renderbuffer;
+      drawRb = ctx->DrawBuffer->Attachment[BUFFER_STENCIL].Renderbuffer;
+      mode = UNPACK_S;
+      pixelSize = 1;
       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:
@@ -197,6 +208,62 @@ blit_nearest(struct gl_context *ctx,
       return;
    }
 
+   if (readRb == drawRb) {
+      /* 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) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
+         return;
+      }
+
+      srcMap = map + srcYpos * rowStride + srcXpos * formatSize;
+      dstMap = map + dstYpos * rowStride + dstXpos * formatSize;
+
+      /* 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) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
+         return;
+      }
+      ctx->Driver.MapRenderbuffer(ctx, drawRb,
+                                 dstXpos, dstYpos,
+                                  dstWidth, dstHeight,
+                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+      if (!dstMap) {
+         ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
+         return;
+      }
+   }
+
    /* allocate the src/dst row buffers */
    srcBuffer = malloc(pixelSize * srcWidth);
    if (!srcBuffer) {
@@ -211,9 +278,8 @@ blit_nearest(struct gl_context *ctx,
    }
 
    for (dstRow = 0; dstRow < dstHeight; dstRow++) {
-      const GLint dstY = dstYpos + dstRow;
       GLint srcRow = (dstRow * srcHeight) / dstHeight;
-      GLint srcY;
+      GLubyte *dstRowStart = dstMap + dstRowStride * dstRow;
 
       ASSERT(srcRow >= 0);
       ASSERT(srcRow < srcHeight);
@@ -222,21 +288,67 @@ blit_nearest(struct gl_context *ctx,
          srcRow = srcHeight - 1 - srcRow;
       }
 
-      srcY = srcYpos + srcRow;
-
       /* get pixel row from source and resample to match dest width */
-      if (prevY != srcY) {
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY, srcBuffer);
+      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 = srcY;
+         prevY = srcRow;
       }
 
       /* store pixel row in destination */
-      drawRb->PutRow(ctx, drawRb, dstWidth, dstXpos, dstY, dstBuffer, NULL);
+      switch (mode) {
+      case DIRECT:
+        memcpy(dstRowStart, dstBuffer, pixelSize * srcWidth);
+        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;
+      }
    }
 
    free(srcBuffer);
    free(dstBuffer);
+
+   ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+   if (drawRb != readRb) {
+      ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
+   }
 }
 
 
@@ -311,9 +423,66 @@ 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;
+   const GLfloat dstWidthF = (GLfloat) dstWidth;
+   GLint dstCol;
+
+   for (dstCol = 0; dstCol < dstWidth; dstCol++) {
+      const GLfloat srcCol = (dstCol * srcWidth) / dstWidthF;
+      GLint srcCol0 = IFLOOR(srcCol);
+      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);
+
+      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,
@@ -344,23 +513,25 @@ blit_linear(struct gl_context *ctx,
    GLint srcBufferY0 = -1, srcBufferY1 = -1;
    GLvoid *dstBuffer;
 
-   switch (readRb->DataType) {
-   case GL_UNSIGNED_BYTE:
+   gl_format readFormat = _mesa_get_srgb_format_linear(readRb->Format);
+   gl_format drawFormat = _mesa_get_srgb_format_linear(drawRb->Format);
+   GLuint bpp = _mesa_get_format_bytes(readFormat);
+
+   GLenum pixelType;
+
+   GLubyte *srcMap, *dstMap;
+   GLint srcRowStride, dstRowStride;
+
+
+   /* 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);
-      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);
-      break;
-   default:
-      _mesa_problem(ctx, "unexpected buffer type (0x%x) in blit_nearest",
-                    readRb->DataType);
-      return;
    }
 
    /* Allocate the src/dst row buffers.
@@ -385,6 +556,45 @@ blit_linear(struct gl_context *ctx,
       return;
    }
 
+   /*
+    * Map src / dst renderbuffers
+    */
+   if (readRb == drawRb) {
+      /* 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) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
+         return;
+      }
+
+      dstMap = srcMap;
+      dstRowStride = srcRowStride;
+   }
+   else {
+      /* 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) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
+         return;
+      }
+      ctx->Driver.MapRenderbuffer(ctx, drawRb,
+                                  0, 0, drawRb->Width, drawRb->Height,
+                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+      if (!dstMap) {
+         ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFramebuffer");
+         return;
+      }
+   }
+
    for (dstRow = 0; dstRow < dstHeight; dstRow++) {
       const GLint dstY = dstYpos + dstRow;
       const GLfloat srcRow = (dstRow * srcHeight) / dstHeightF;
@@ -419,147 +629,73 @@ blit_linear(struct gl_context *ctx,
          srcBuffer0 = srcBuffer1;
          srcBuffer1 = tmp;
          /* get y1 row */
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY1, srcBuffer1);
+         {
+            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 */
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY0, srcBuffer0);
-         readRb->GetRow(ctx, readRb, srcWidth, srcXpos, srcY1, srcBuffer1);
+         {
+            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;
       }
 
-      if (readRb->DataType == GL_UNSIGNED_BYTE) {
+      if (pixelType == GL_UNSIGNED_BYTE) {
          resample_linear_row_ub(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
                                 dstBuffer, invertX, rowWeight);
       }
       else {
-         _mesa_problem(ctx, "Unsupported color channel type in sw blit");
-         break;
+         resample_linear_row_float(srcWidth, dstWidth, srcBuffer0, srcBuffer1,
+                                   dstBuffer, invertX, rowWeight);
       }
 
       /* store pixel row in destination */
-      drawRb->PutRow(ctx, drawRb, dstWidth, dstXpos, dstY, dstBuffer, NULL);
+      {
+         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);
+         }
+      }
    }
 
    free(srcBuffer0);
    free(srcBuffer1);
    free(dstBuffer);
-}
-
 
-/**
- * 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);
-
-   /* From the GL_ARB_framebuffer_object spec:
-    *
-    *     "If the source and destination buffers are identical, and the source
-    *      and destination rectangles overlap, the result of the blit operation
-    *      is undefined."
-    *
-    * However, we provide the expected result anyway by flipping the order of
-    * the memcpy of rows.
-    */
-   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;
+   ctx->Driver.UnmapRenderbuffer(ctx, readRb);
+   if (drawRb != readRb) {
+      ctx->Driver.UnmapRenderbuffer(ctx, drawRb);
    }
-
-   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;
-   }
-
-   ASSERT(readRb->DataType == drawRb->DataType);
-
-   /* 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;
-   }
-
-   /* allocate the row buffer */
-   rowBuffer = malloc(bytesPerRow);
-   if (!rowBuffer) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
-
-   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(rowBuffer);
 }
 
 
+
 /**
  * Software fallback for glBlitFramebufferEXT().
  */
@@ -574,6 +710,11 @@ _swrast_BlitFramebuffer(struct gl_context *ctx,
       GL_DEPTH_BUFFER_BIT,
       GL_STENCIL_BUFFER_BIT
    };
+   static const GLenum buffer_enums[3] = {
+      GL_COLOR,
+      GL_DEPTH,
+      GL_STENCIL,
+   };
    GLint i;
 
    if (!_mesa_clip_blit(ctx, &srcX0, &srcY0, &srcX1, &srcY1,
@@ -584,41 +725,45 @@ _swrast_BlitFramebuffer(struct gl_context *ctx,
    if (SWRAST_CONTEXT(ctx)->NewState)
       _swrast_validate_derived(ctx);
 
-   swrast_render_start(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) {
-      /* no stretching or flipping.
-       * filter doesn't matter.
-       */
       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,
+                                       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,  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,  srcX0, srcY0, srcX1, srcY1,
+                    dstX0, dstY0, dstX1, dstY1);
       }
    }
 
-   swrast_render_finish(ctx);
 }