Added few more stubs so that control reaches to DestroyDevice().
[mesa.git] / src / mesa / swrast / s_blit.c
index 8303e4debc75dcb5b15c18543f40232c4e2e04c3..6b7e5e4533381a077be10248ee7917716460a77e 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * Mesa 3-D graphics library
- * Version:  6.5
  *
  * 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
- * 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.
  */
 
 
 #include "main/glheader.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"
 
 
@@ -48,8 +52,8 @@ NAME(GLint srcWidth, GLint 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];                 \
@@ -69,8 +73,8 @@ NAME(GLint srcWidth, GLint 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];                 \
          }                                             \
@@ -102,12 +106,17 @@ RESAMPLE(resample_row_16, GLuint, 4)
  * Blit color, depth or stencil with GL_NEAREST filtering.
  */
 static void
-blit_nearest(GLcontext *ctx,
+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)
 {
-   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);
@@ -121,10 +130,18 @@ blit_nearest(GLcontext *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 = DIRECT;
+   GLubyte *srcMap, *dstMap;
+   GLint srcRowStride, dstRowStride;
    GLint dstRow;
 
-   GLint comps, pixelSize;
+   GLint pixelSize = 0;
    GLvoid *srcBuffer, *dstBuffer;
    GLint prevY = -1;
 
@@ -135,114 +152,238 @@ blit_nearest(GLcontext *ctx,
 
    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:
-      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:
-      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;
    }
 
-   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 */
-   srcBuffer = _mesa_malloc(pixelSize * srcWidth);
-   if (!srcBuffer) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
-   dstBuffer = _mesa_malloc(pixelSize * dstWidth);
-   if (!dstBuffer) {
-      _mesa_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) {
+         gl_buffer_index idx = drawFb->_ColorDrawBufferIndexes[i];
+         if (idx == BUFFER_NONE)
+            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, readFb->FlipY);
+         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,
+                                     readFb->FlipY);
+         if (!srcMap) {
+            goto fail_no_memory;
+         }
+         ctx->Driver.MapRenderbuffer(ctx, drawRb,
+                                     dstXpos, dstYpos,
+                                     dstWidth, dstHeight,
+                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
+                                     drawFb->FlipY);
+         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 = lroundf(srcRowF);
+         GLubyte *dstRowStart = dstMap + dstRowStride * dstRow;
+
+         assert(srcRow >= 0);
+         assert(srcRow < srcHeight);
 
-      /* 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;
+         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;
+         }
+
+         /* 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);
+      }
    }
 
-   _mesa_free(srcBuffer);
-   _mesa_free(dstBuffer);
+fail:
+   free(srcBuffer);
+   free(dstBuffer);
+   return;
+
+fail_no_memory:
+   free(srcBuffer);
+   free(dstBuffer);
+   _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBuffer");
 }
 
 
 
 #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)
 {
@@ -264,19 +405,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 GLfloat dstWidthF = (GLfloat) dstWidth;
    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, util_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);
+      assert(srcCol0 < srcWidth);
+      assert(srcCol1 <= srcWidth);
 
       if (srcCol1 == srcWidth) {
          /* last column fudge */
@@ -301,32 +440,89 @@ resample_linear_row_ub(GLint srcWidth, GLint dstWidth,
       alpha = lerp_2d(colWeight, rowWeight,
                     srcColor0[srcCol0][ACOMP], srcColor0[srcCol1][ACOMP],
                     srcColor1[srcCol0][ACOMP], srcColor1[srcCol1][ACOMP]);
-      
-      dstColor[dstCol][RCOMP] = IFLOOR(red);
-      dstColor[dstCol][GCOMP] = IFLOOR(green);
-      dstColor[dstCol][BCOMP] = IFLOOR(blue);
-      dstColor[dstCol][ACOMP] = IFLOOR(alpha);
+
+      dstColor[dstCol][RCOMP] = util_ifloor(red);
+      dstColor[dstCol][GCOMP] = util_ifloor(green);
+      dstColor[dstCol][BCOMP] = util_ifloor(blue);
+      dstColor[dstCol][ACOMP] = util_ifloor(alpha);
+   }
+}
+
+
+/**
+ * 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, util_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(GLcontext *ctx,
+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)
 {
-   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 GLfloat dstHeightF = (GLfloat) dstHeight;
 
    const GLint srcXpos = MIN2(srcX0, srcX1);
    const GLint srcYpos = MIN2(srcY0, srcY1);
@@ -343,219 +539,207 @@ blit_linear(GLcontext *ctx,
    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);
-      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.
     * Keep two adjacent src rows around for bilinear sampling.
     */
-   srcBuffer0 = _mesa_malloc(pixelSize * srcWidth);
-   if (!srcBuffer0) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
-   srcBuffer1 = _mesa_malloc(pixelSize * srcWidth);
-   if (!srcBuffer1) {
-      _mesa_free(srcBuffer0);
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
-   }
-   dstBuffer = _mesa_malloc(pixelSize * dstWidth);
-   if (!dstBuffer) {
-      _mesa_free(srcBuffer0);
-      _mesa_free(srcBuffer1);
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBlitFrameBufferEXT");
-      return;
+   srcBuffer0 = malloc(pixelSize * srcWidth);
+   srcBuffer1 = malloc(pixelSize * srcWidth);
+   dstBuffer = malloc(pixelSize * dstWidth);
+   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 */
+   for (i = 0; i < drawFb->_NumColorDrawBuffers; i++) {
+      gl_buffer_index idx = drawFb->_ColorDrawBufferIndexes[i];
+      struct gl_renderbuffer_attachment *drawAtt;
+      struct gl_renderbuffer *drawRb;
+      mesa_format drawFormat;
 
-      ASSERT(srcRow >= 0);
-      ASSERT(srcRow < srcHeight);
+      if (idx == BUFFER_NONE)
+         continue;
 
-      if (srcRow1 == srcHeight) {
-         /* last row fudge */
-         srcRow1 = srcRow0;
-         rowWeight = 0.0;
-      }
+      drawAtt = &drawFb->Attachment[idx];
+      drawRb = drawAtt->Renderbuffer;
+      if (!drawRb)
+         continue;
 
-      if (invertY) {
-         srcRow0 = srcHeight - 1 - srcRow0;
-         srcRow1 = srcHeight - 1 - srcRow1;
-      }
-
-      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,
+                                     readFb->FlipY);
+         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 {
-         _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,
+                                     readFb->FlipY);
+         if (!srcMap) {
+            goto fail_no_memory;
+         }
+         ctx->Driver.MapRenderbuffer(ctx, drawRb,
+                                     0, 0, drawRb->Width, drawRb->Height,
+                                     GL_MAP_WRITE_BIT, &dstMap, &dstRowStride,
+                                     drawFb->FlipY);
+         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);
-   }
-
-   _mesa_free(srcBuffer0);
-   _mesa_free(srcBuffer1);
-   _mesa_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, util_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(GLcontext *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 = _mesa_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;
 
-   _mesa_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(GLcontext *ctx,
+_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)
@@ -565,51 +749,68 @@ _swrast_BlitFramebuffer(GLcontext *ctx,
       GL_DEPTH_BUFFER_BIT,
       GL_STENCIL_BUFFER_BIT
    };
+   static const GLenum buffer_enums[3] = {
+      GL_COLOR,
+      GL_DEPTH,
+      GL_STENCIL,
+   };
    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;
    }
 
-   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) {
-      /* 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,
+                                        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);
 }