swrast: do fast_copy_pixels() with Map/UnmapRenderbuffer()
authorBrian Paul <brianp@vmware.com>
Sat, 24 Dec 2011 15:54:26 +0000 (08:54 -0700)
committerBrian Paul <brianp@vmware.com>
Sat, 24 Dec 2011 15:54:26 +0000 (08:54 -0700)
v2: use memmove() instead of memcpy() in case of overlap

Reviewed-by: Eric Anholt <eric@anholt.net>
src/mesa/swrast/s_copypix.c

index 3bdf48b25f10da9b13c2ce1d9c4e109d9d21df76..9769a477db6e6b3f3dfb849c632cbeed44b90f3c 100644 (file)
@@ -600,6 +600,7 @@ copy_depth_stencil_pixels(struct gl_context *ctx,
 
 /**
  * Try to do a fast copy pixels.
+ * \return GL_TRUE if successful, GL_FALSE otherwise.
  */
 static GLboolean
 fast_copy_pixels(struct gl_context *ctx,
@@ -609,8 +610,10 @@ fast_copy_pixels(struct gl_context *ctx,
    struct gl_framebuffer *srcFb = ctx->ReadBuffer;
    struct gl_framebuffer *dstFb = ctx->DrawBuffer;
    struct gl_renderbuffer *srcRb, *dstRb;
-   GLint row, yStep;
-   void *temp;
+   GLint row;
+   GLuint pixelBytes, widthInBytes;
+   GLubyte *srcMap, *dstMap;
+   GLint srcRowStride, dstRowStride;
 
    if (SWRAST_CONTEXT(ctx)->_RasterMask != 0x0 ||
        ctx->Pixel.ZoomX != 1.0F ||
@@ -641,10 +644,8 @@ fast_copy_pixels(struct gl_context *ctx,
       dstRb = dstFb->Attachment[BUFFER_DEPTH].Renderbuffer;
    }
 
-   /* src and dst renderbuffers must be same format and type */
-   if (!srcRb || !dstRb ||
-       srcRb->DataType != dstRb->DataType ||
-       srcRb->_BaseFormat != dstRb->_BaseFormat) {
+   /* src and dst renderbuffers must be same format */
+   if (!srcRb || !dstRb || srcRb->Format != dstRb->Format) {
       return GL_FALSE;
    }
 
@@ -656,32 +657,73 @@ fast_copy_pixels(struct gl_context *ctx,
       return GL_FALSE;
    }
 
-   /* overlapping src/dst doesn't matter, just determine Y direction */
-   if (srcY < dstY) {
-      /* top-down  max-to-min */
-      srcY = srcY + height - 1;
-      dstY = dstY + height - 1;
-      yStep = -1;
+   pixelBytes = _mesa_get_format_bytes(srcRb->Format);
+   widthInBytes = width * pixelBytes;
+
+   if (srcRb == dstRb) {
+      /* 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;
+
+      ctx->Driver.MapRenderbuffer(ctx, srcRb, 0, 0,
+                                  srcRb->Width, srcRb->Height,
+                                  GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
+                                  &map, &rowStride);
+      if (!srcMap) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
+         return GL_TRUE; /* don't retry with slow path */
+      }
+
+      srcMap = map + srcY * rowStride + srcX * pixelBytes;
+      dstMap = map + dstY * rowStride + dstX * pixelBytes;
+
+      /* this handles overlapping copies */
+      if (srcY < dstY) {
+         /* copy in reverse (top->down) order */
+         srcMap += rowStride * (height - 1);
+         dstMap += rowStride * (height - 1);
+         srcRowStride = -rowStride;
+         dstRowStride = -rowStride;
+      }
+      else {
+         /* copy in normal (bottom->up) order */
+         srcRowStride = rowStride;
+         dstRowStride = rowStride;
+      }
    }
    else {
-      /* bottom-up  min-to-max */
-      yStep = 1;
-   }
-
-   temp = malloc(width * MAX_PIXEL_BYTES);
-   if (!temp) {
-      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
-      return GL_FALSE;
+      /* different src/dst buffers */
+      ctx->Driver.MapRenderbuffer(ctx, srcRb, srcX, srcY,
+                                  width, height,
+                                  GL_MAP_READ_BIT, &srcMap, &srcRowStride);
+      if (!srcMap) {
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
+         return GL_TRUE; /* don't retry with slow path */
+      }
+      ctx->Driver.MapRenderbuffer(ctx, dstRb, dstX, dstY,
+                                  width, height,
+                                  GL_MAP_WRITE_BIT, &dstMap, &dstRowStride);
+      if (!dstMap) {
+         ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
+         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glCopyPixels");
+         return GL_TRUE; /* don't retry with slow path */
+      }
    }
 
    for (row = 0; row < height; row++) {
-      srcRb->GetRow(ctx, srcRb, width, srcX, srcY, temp);
-      dstRb->PutRow(ctx, dstRb, width, dstX, dstY, temp, NULL);
-      srcY += yStep;
-      dstY += yStep;
+      /* memmove() in case of overlap */
+      memmove(dstMap, srcMap, widthInBytes);
+      dstMap += dstRowStride;
+      srcMap += srcRowStride;
    }
 
-   free(temp);
+   ctx->Driver.UnmapRenderbuffer(ctx, srcRb);
+   if (dstRb != srcRb) {
+      ctx->Driver.UnmapRenderbuffer(ctx, dstRb);
+   }
 
    return GL_TRUE;
 }
@@ -697,7 +739,6 @@ _swrast_CopyPixels( struct gl_context *ctx,
                    GLint destx, GLint desty, GLenum type )
 {
    SWcontext *swrast = SWRAST_CONTEXT(ctx);
-   swrast_render_start(ctx);
       
    if (!_mesa_check_conditional_render(ctx))
       return; /* don't copy */
@@ -705,23 +746,28 @@ _swrast_CopyPixels( struct gl_context *ctx,
    if (swrast->NewState)
       _swrast_validate_derived( ctx );
 
-   if (!fast_copy_pixels(ctx, srcx, srcy, width, height, destx, desty, type)) {
-      switch (type) {
-      case GL_COLOR:
-         copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
-         break;
-      case GL_DEPTH:
-         copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
-         break;
-      case GL_STENCIL:
-         copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
-         break;
-      case GL_DEPTH_STENCIL_EXT:
-         copy_depth_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
-         break;
-      default:
-         _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
-      }
+   if (fast_copy_pixels(ctx, srcx, srcy, width, height, destx, desty, type)) {
+      /* all done */
+      return;
+   }
+
+   swrast_render_start(ctx);
+
+   switch (type) {
+   case GL_COLOR:
+      copy_rgba_pixels( ctx, srcx, srcy, width, height, destx, desty );
+      break;
+   case GL_DEPTH:
+      copy_depth_pixels( ctx, srcx, srcy, width, height, destx, desty );
+      break;
+   case GL_STENCIL:
+      copy_stencil_pixels( ctx, srcx, srcy, width, height, destx, desty );
+      break;
+   case GL_DEPTH_STENCIL_EXT:
+      copy_depth_stencil_pixels(ctx, srcx, srcy, width, height, destx, desty);
+      break;
+   default:
+      _mesa_problem(ctx, "unexpected type in _swrast_CopyPixels");
    }
 
    swrast_render_finish(ctx);