Mesa (master): mesa: add common format-independent memcpy-based ReadPixels path

Marek Olšák mareko at kemper.freedesktop.org
Sat Mar 23 13:01:57 UTC 2013


Module: Mesa
Branch: master
Commit: d702c67ba51f55c04be670c0ac618b687f7d2127
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=d702c67ba51f55c04be670c0ac618b687f7d2127

Author: Marek Olšák <maraeo at gmail.com>
Date:   Thu Mar 14 15:20:27 2013 +0100

mesa: add common format-independent memcpy-based ReadPixels path

I'll need the _mesa_readpixels_needs_slow_path function for the blit-based
version, but it's also useful to have this memcpy-based path in one place
and not scattered across several functions.

v2: add "const" to function parameters

Reviewed-by: Brian Paul <brianp at vmware.com>
Tested-by: Brian Paul <brianp at vmware.com>

---

 src/mesa/main/framebuffer.c |    4 +-
 src/mesa/main/framebuffer.h |    2 +-
 src/mesa/main/readpix.c     |  194 +++++++++++++++++++++++++++++++++++--------
 src/mesa/main/readpix.h     |    4 +
 4 files changed, 167 insertions(+), 37 deletions(-)

diff --git a/src/mesa/main/framebuffer.c b/src/mesa/main/framebuffer.c
index d3abc2b..619aaa3 100644
--- a/src/mesa/main/framebuffer.c
+++ b/src/mesa/main/framebuffer.c
@@ -923,10 +923,10 @@ _mesa_get_color_read_type(struct gl_context *ctx)
  * Returns the read renderbuffer for the specified format.
  */
 struct gl_renderbuffer *
-_mesa_get_read_renderbuffer_for_format(struct gl_context *ctx,
+_mesa_get_read_renderbuffer_for_format(const struct gl_context *ctx,
                                        GLenum format)
 {
-   struct gl_framebuffer *rfb = ctx->ReadBuffer;
+   const struct gl_framebuffer *rfb = ctx->ReadBuffer;
 
    if (_mesa_is_color_format(format)) {
       return rfb->Attachment[rfb->_ColorReadBufferIndex].Renderbuffer;
diff --git a/src/mesa/main/framebuffer.h b/src/mesa/main/framebuffer.h
index 06db049..9b94452 100644
--- a/src/mesa/main/framebuffer.h
+++ b/src/mesa/main/framebuffer.h
@@ -98,7 +98,7 @@ extern GLenum
 _mesa_get_color_read_format(struct gl_context *ctx);
 
 extern struct gl_renderbuffer *
-_mesa_get_read_renderbuffer_for_format(struct gl_context *ctx,
+_mesa_get_read_renderbuffer_for_format(const struct gl_context *ctx,
                                        GLenum format);
 
 extern void
diff --git a/src/mesa/main/readpix.c b/src/mesa/main/readpix.c
index 1b3b31e..d3d09de 100644
--- a/src/mesa/main/readpix.c
+++ b/src/mesa/main/readpix.c
@@ -108,11 +108,145 @@ get_readpixels_transfer_ops(const struct gl_context *ctx, gl_format texFormat,
 
 
 /**
- * Tries to implement glReadPixels() of GL_DEPTH_COMPONENT using memcpy of the
- * mapping.
+ * Return true if memcpy cannot be used for ReadPixels.
+ *
+ * If uses_blit is true, the function returns true if a simple 3D engine blit
+ * cannot be used for ReadPixels packing.
+ *
+ * NOTE: This doesn't take swizzling and format conversions between
+ *       the readbuffer and the pixel pack buffer into account.
  */
+GLboolean
+_mesa_readpixels_needs_slow_path(const struct gl_context *ctx, GLenum format,
+                                 GLenum type, GLboolean uses_blit)
+{
+   struct gl_renderbuffer *rb =
+         _mesa_get_read_renderbuffer_for_format(ctx, format);
+   GLenum srcType;
+
+   ASSERT(rb);
+
+   /* There are different rules depending on the base format. */
+   switch (format) {
+   case GL_DEPTH_STENCIL:
+      return !_mesa_has_depthstencil_combined(ctx->ReadBuffer) ||
+             ctx->Pixel.DepthScale != 1.0f || ctx->Pixel.DepthBias != 0.0f ||
+             ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset ||
+             ctx->Pixel.MapStencilFlag;
+
+   case GL_DEPTH_COMPONENT:
+      return ctx->Pixel.DepthScale != 1.0f || ctx->Pixel.DepthBias != 0.0f;
+
+   case GL_STENCIL_INDEX:
+      return ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset ||
+             ctx->Pixel.MapStencilFlag;
+
+   default:
+      /* Color formats. */
+      if (need_rgb_to_luminance_conversion(rb->Format, format)) {
+         return GL_TRUE;
+      }
+
+      /* Conversion between signed and unsigned integers needs masking
+       * (it isn't just memcpy). */
+      srcType = _mesa_get_format_datatype(rb->Format);
+
+      if ((srcType == GL_INT &&
+           (type == GL_UNSIGNED_INT ||
+            type == GL_UNSIGNED_SHORT ||
+            type == GL_UNSIGNED_BYTE)) ||
+          (srcType == GL_UNSIGNED_INT &&
+           (type == GL_INT ||
+            type == GL_SHORT ||
+            type == GL_BYTE))) {
+         return GL_TRUE;
+      }
+
+      /* And finally, see if there are any transfer ops. */
+      return get_readpixels_transfer_ops(ctx, rb->Format, format, type,
+                                         uses_blit) != 0;
+   }
+   return GL_FALSE;
+}
+
+
+static GLboolean
+readpixels_can_use_memcpy(const struct gl_context *ctx, GLenum format, GLenum type,
+                          const struct gl_pixelstore_attrib *packing)
+{
+   struct gl_renderbuffer *rb =
+         _mesa_get_read_renderbuffer_for_format(ctx, format);
+
+   ASSERT(rb);
+
+   if (_mesa_readpixels_needs_slow_path(ctx, format, type, GL_FALSE)) {
+      return GL_FALSE;
+   }
+
+   /* The base internal format and the base Mesa format must match. */
+   if (rb->_BaseFormat != _mesa_get_format_base_format(rb->Format)) {
+      return GL_FALSE;
+   }
+
+   /* The Mesa format must match the input format and type. */
+   if (!_mesa_format_matches_format_and_type(rb->Format, format, type,
+                                             packing->SwapBytes)) {
+      return GL_FALSE;
+   }
+
+   return GL_TRUE;
+}
+
+
 static GLboolean
-fast_read_depth_pixels( struct gl_context *ctx,
+readpixels_memcpy(struct gl_context *ctx,
+                  GLint x, GLint y,
+                  GLsizei width, GLsizei height,
+                  GLenum format, GLenum type,
+                  GLvoid *pixels,
+                  const struct gl_pixelstore_attrib *packing)
+{
+   struct gl_renderbuffer *rb =
+         _mesa_get_read_renderbuffer_for_format(ctx, format);
+   GLubyte *dst, *map;
+   int dstStride, stride, j, texelBytes;
+
+   /* Fail if memcpy cannot be used. */
+   if (!readpixels_can_use_memcpy(ctx, format, type, packing)) {
+      return GL_FALSE;
+   }
+
+   dstStride = _mesa_image_row_stride(packing, width, format, type);
+   dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
+					   format, type, 0, 0);
+
+   ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
+			       &map, &stride);
+   if (!map) {
+      _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
+      return GL_TRUE;  /* don't bother trying the slow path */
+   }
+
+   texelBytes = _mesa_get_format_bytes(rb->Format);
+
+   /* memcpy*/
+   for (j = 0; j < height; j++) {
+      memcpy(dst, map, width * texelBytes);
+      dst += dstStride;
+      map += stride;
+   }
+
+   ctx->Driver.UnmapRenderbuffer(ctx, rb);
+   return GL_TRUE;
+}
+
+
+/**
+ * Optimized path for conversion of depth values to GL_DEPTH_COMPONENT,
+ * GL_UNSIGNED_INT.
+ */
+static GLboolean
+read_uint_depth_pixels( struct gl_context *ctx,
 			GLint x, GLint y,
 			GLsizei width, GLsizei height,
 			GLenum type, GLvoid *pixels,
@@ -132,10 +266,6 @@ fast_read_depth_pixels( struct gl_context *ctx,
    if (_mesa_get_format_datatype(rb->Format) != GL_UNSIGNED_NORMALIZED)
       return GL_FALSE;
 
-   if (!((type == GL_UNSIGNED_SHORT && rb->Format == MESA_FORMAT_Z16) ||
-	 type == GL_UNSIGNED_INT))
-      return GL_FALSE;
-
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
 			       &map, &stride);
 
@@ -149,12 +279,7 @@ fast_read_depth_pixels( struct gl_context *ctx,
 					   GL_DEPTH_COMPONENT, type, 0, 0);
 
    for (j = 0; j < height; j++) {
-      if (type == GL_UNSIGNED_INT) {
-	 _mesa_unpack_uint_z_row(rb->Format, width, map, (GLuint *)dst);
-      } else {
-	 ASSERT(type == GL_UNSIGNED_SHORT && rb->Format == MESA_FORMAT_Z16);
-	 memcpy(dst, map, width * 2);
-      }
+      _mesa_unpack_uint_z_row(rb->Format, width, map, (GLuint *)dst);
 
       map += stride;
       dst += dstStride;
@@ -190,8 +315,10 @@ read_depth_pixels( struct gl_context *ctx,
    ASSERT(x + width <= (GLint) rb->Width);
    ASSERT(y + height <= (GLint) rb->Height);
 
-   if (fast_read_depth_pixels(ctx, x, y, width, height, type, pixels, packing))
+   if (type == GL_UNSIGNED_INT &&
+       read_uint_depth_pixels(ctx, x, y, width, height, type, pixels, packing)) {
       return;
+   }
 
    dstStride = _mesa_image_row_stride(packing, width, GL_DEPTH_COMPONENT, type);
    dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
@@ -279,20 +406,20 @@ read_stencil_pixels( struct gl_context *ctx,
 
 
 /**
- * Try to do glReadPixels of RGBA data using a simple memcpy or swizzle.
+ * Try to do glReadPixels of RGBA data using swizzle.
  * \return GL_TRUE if successful, GL_FALSE otherwise (use the slow path)
  */
 static GLboolean
-fast_read_rgba_pixels_memcpy( struct gl_context *ctx,
-			      GLint x, GLint y,
-			      GLsizei width, GLsizei height,
-			      GLenum format, GLenum type,
-			      GLvoid *pixels,
-			      const struct gl_pixelstore_attrib *packing)
+read_rgba_pixels_swizzle(struct gl_context *ctx,
+                         GLint x, GLint y,
+                         GLsizei width, GLsizei height,
+                         GLenum format, GLenum type,
+                         GLvoid *pixels,
+                         const struct gl_pixelstore_attrib *packing)
 {
    struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer;
    GLubyte *dst, *map;
-   int dstStride, stride, j, texelBytes;
+   int dstStride, stride, j;
    GLboolean swizzle_rb = GL_FALSE, copy_xrgb = GL_FALSE;
 
    /* XXX we could check for other swizzle/special cases here as needed */
@@ -308,9 +435,9 @@ fast_read_rgba_pixels_memcpy( struct gl_context *ctx,
        !ctx->Pack.SwapBytes) {
       copy_xrgb = GL_TRUE;
    }
-   else if (!_mesa_format_matches_format_and_type(rb->Format, format, type,
-                                                  ctx->Pack.SwapBytes))
+   else {
       return GL_FALSE;
+   }
 
    dstStride = _mesa_image_row_stride(packing, width, format, type);
    dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
@@ -323,8 +450,6 @@ fast_read_rgba_pixels_memcpy( struct gl_context *ctx,
       return GL_TRUE;  /* don't bother trying the slow path */
    }
 
-   texelBytes = _mesa_get_format_bytes(rb->Format);
-
    if (swizzle_rb) {
       /* swap R/B */
       for (j = 0; j < height; j++) {
@@ -350,13 +475,6 @@ fast_read_rgba_pixels_memcpy( struct gl_context *ctx,
          dst += dstStride;
          map += stride;
       }
-   } else {
-      /* just memcpy */
-      for (j = 0; j < height; j++) {
-         memcpy(dst, map, width * texelBytes);
-         dst += dstStride;
-         map += stride;
-      }
    }
 
    ctx->Driver.UnmapRenderbuffer(ctx, rb);
@@ -447,7 +565,7 @@ read_rgba_pixels( struct gl_context *ctx,
 
    /* Try the optimized paths first. */
    if (!transferOps &&
-       fast_read_rgba_pixels_memcpy(ctx, x, y, width, height,
+       read_rgba_pixels_swizzle(ctx, x, y, width, height,
                                     format, type, pixels, packing)) {
       return;
    }
@@ -703,6 +821,14 @@ _mesa_readpixels(struct gl_context *ctx,
       pixels = _mesa_map_pbo_dest(ctx, &clippedPacking, pixels);
 
       if (pixels) {
+         /* Try memcpy first. */
+         if (readpixels_memcpy(ctx, x, y, width, height, format, type,
+                               pixels, packing)) {
+            _mesa_unmap_pbo_dest(ctx, &clippedPacking);
+            return;
+         }
+
+         /* Otherwise take the slow path. */
          switch (format) {
          case GL_STENCIL_INDEX:
             read_stencil_pixels(ctx, x, y, width, height, type, pixels,
diff --git a/src/mesa/main/readpix.h b/src/mesa/main/readpix.h
index 5a5f73f..7491c22 100644
--- a/src/mesa/main/readpix.h
+++ b/src/mesa/main/readpix.h
@@ -33,6 +33,10 @@ struct gl_context;
 struct gl_pixelstore_attrib;
 
 
+extern GLboolean
+_mesa_readpixels_needs_slow_path(const struct gl_context *ctx, GLenum format,
+                                 GLenum type, GLboolean uses_blit);
+
 extern void
 _mesa_readpixels(struct gl_context *ctx,
                  GLint x, GLint y, GLsizei width, GLsizei height,




More information about the mesa-commit mailing list