From d702c67ba51f55c04be670c0ac618b687f7d2127 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marek=20Ol=C5=A1=C3=A1k?= Date: Thu, 14 Mar 2013 15:20:27 +0100 Subject: [PATCH] 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 Tested-by: Brian Paul --- 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, -- 2.7.4