main: remove extern C around #includes in ff_fragment_shader.cpp
[mesa.git] / src / mesa / main / readpix.c
index 5b80e9a8ba14741e5366394140bbbdd5670fde64..b09cf54994a638c27e29da96b07640e62cd427f1 100644 (file)
@@ -1,6 +1,5 @@
 /*
  * Mesa 3-D graphics library
- * Version:  7.1
  *
  * Copyright (C) 1999-2008  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 "glheader.h"
 #include "imports.h"
+#include "blend.h"
 #include "bufferobj.h"
 #include "context.h"
 #include "enums.h"
 
 
 /**
- * Tries to implement glReadPixels() of GL_DEPTH_COMPONENT using memcpy of the
- * mapping.
+ * Return true if the conversion L=R+G+B is needed.
  */
 static GLboolean
-fast_read_depth_pixels( struct gl_context *ctx,
+need_rgb_to_luminance_conversion(mesa_format texFormat, GLenum format)
+{
+   GLenum baseTexFormat = _mesa_get_format_base_format(texFormat);
+
+   return (baseTexFormat == GL_RG ||
+           baseTexFormat == GL_RGB ||
+           baseTexFormat == GL_RGBA) &&
+          (format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA);
+}
+
+
+/**
+ * Return transfer op flags for this ReadPixels operation.
+ */
+static GLbitfield
+get_readpixels_transfer_ops(const struct gl_context *ctx, mesa_format texFormat,
+                            GLenum format, GLenum type, GLboolean uses_blit)
+{
+   GLbitfield transferOps = ctx->_ImageTransferState;
+
+   if (format == GL_DEPTH_COMPONENT ||
+       format == GL_DEPTH_STENCIL ||
+       format == GL_STENCIL_INDEX) {
+      return 0;
+   }
+
+   /* Pixel transfer ops (scale, bias, table lookup) do not apply
+    * to integer formats.
+    */
+   if (_mesa_is_enum_format_integer(format)) {
+      return 0;
+   }
+
+   if (uses_blit) {
+      /* For blit-based ReadPixels packing, the clamping is done automatically
+       * unless the type is float. */
+      if (_mesa_get_clamp_read_color(ctx) &&
+          (type == GL_FLOAT || type == GL_HALF_FLOAT)) {
+         transferOps |= IMAGE_CLAMP_BIT;
+      }
+   }
+   else {
+      /* For CPU-based ReadPixels packing, the clamping must always be done
+       * for non-float types, */
+      if (_mesa_get_clamp_read_color(ctx) ||
+          (type != GL_FLOAT && type != GL_HALF_FLOAT)) {
+         transferOps |= IMAGE_CLAMP_BIT;
+      }
+   }
+
+   /* If the format is unsigned normalized, we can ignore clamping
+    * because the values are already in the range [0,1] so it won't
+    * have any effect anyway.
+    */
+   if (_mesa_get_format_datatype(texFormat) == GL_UNSIGNED_NORMALIZED &&
+       !need_rgb_to_luminance_conversion(texFormat, format)) {
+      transferOps &= ~IMAGE_CLAMP_BIT;
+   }
+
+   return transferOps;
+}
+
+
+/**
+ * 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
+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,
@@ -65,10 +267,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);
 
@@ -82,12 +280,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;
@@ -123,8 +316,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,
@@ -212,49 +407,38 @@ 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,
-                             GLbitfield transferOps )
+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 */
-   if (rb->Format == MESA_FORMAT_RGBA8888_REV &&
+   if (rb->Format == MESA_FORMAT_R8G8B8A8_UNORM &&
        format == GL_BGRA &&
        type == GL_UNSIGNED_INT_8_8_8_8_REV &&
        !ctx->Pack.SwapBytes) {
       swizzle_rb = GL_TRUE;
    }
-   else if (rb->Format == MESA_FORMAT_XRGB8888 &&
+   else if (rb->Format == MESA_FORMAT_B8G8R8X8_UNORM &&
        format == GL_BGRA &&
        type == GL_UNSIGNED_INT_8_8_8_8_REV &&
        !ctx->Pack.SwapBytes) {
       copy_xrgb = GL_TRUE;
    }
-   else if (!_mesa_format_matches_format_and_type(rb->Format, format, type,
-                                                  ctx->Pack.SwapBytes))
-      return GL_FALSE;
-
-   /* If the format is unsigned normalized then we can ignore clamping
-    * because the values are already in the range [0,1] so it won't
-    * have any effect anyway.
-    */
-   if (_mesa_get_format_datatype(rb->Format) == GL_UNSIGNED_NORMALIZED)
-      transferOps &= ~IMAGE_CLAMP_BIT;
-
-   if (transferOps)
+   else {
       return GL_FALSE;
+   }
 
    dstStride = _mesa_image_row_stride(packing, width, format, type);
    dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
@@ -267,8 +451,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++) {
@@ -294,13 +476,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);
@@ -318,7 +493,7 @@ slow_read_rgba_pixels( struct gl_context *ctx,
                       GLbitfield transferOps )
 {
    struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer;
-   const gl_format rbFormat = _mesa_get_srgb_format_linear(rb->Format);
+   const mesa_format rbFormat = _mesa_get_srgb_format_linear(rb->Format);
    void *rgba;
    GLubyte *dst, *map;
    int dstStride, stride, j;
@@ -379,22 +554,20 @@ read_rgba_pixels( struct gl_context *ctx,
                   GLenum format, GLenum type, GLvoid *pixels,
                   const struct gl_pixelstore_attrib *packing )
 {
-   GLbitfield transferOps = ctx->_ImageTransferState;
+   GLbitfield transferOps;
    struct gl_framebuffer *fb = ctx->ReadBuffer;
    struct gl_renderbuffer *rb = fb->_ColorReadBuffer;
 
    if (!rb)
       return;
 
-   if ((ctx->Color._ClampReadColor == GL_TRUE || type != GL_FLOAT) &&
-       !_mesa_is_enum_format_integer(format)) {
-      transferOps |= IMAGE_CLAMP_BIT;
-   }
+   transferOps = get_readpixels_transfer_ops(ctx, rb->Format, format, type,
+                                             GL_FALSE);
 
    /* Try the optimized paths first. */
-   if (fast_read_rgba_pixels_memcpy(ctx, x, y, width, height,
-                                    format, type, pixels, packing,
-                                    transferOps)) {
+   if (!transferOps &&
+       read_rgba_pixels_swizzle(ctx, x, y, width, height,
+                                    format, type, pixels, packing)) {
       return;
    }
 
@@ -421,8 +594,8 @@ fast_read_depth_stencil_pixels(struct gl_context *ctx,
    if (rb != stencilRb)
       return GL_FALSE;
 
-   if (rb->Format != MESA_FORMAT_Z24_S8 &&
-       rb->Format != MESA_FORMAT_S8_Z24)
+   if (rb->Format != MESA_FORMAT_S8_UINT_Z24_UNORM &&
+       rb->Format != MESA_FORMAT_Z24_UNORM_S8_UINT)
       return GL_FALSE;
 
    ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
@@ -649,6 +822,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,
@@ -674,16 +855,65 @@ _mesa_readpixels(struct gl_context *ctx,
 }
 
 
+static GLenum
+read_pixels_es3_error_check(GLenum format, GLenum type,
+                            const struct gl_renderbuffer *rb)
+{
+   const GLenum internalFormat = rb->InternalFormat;
+   const GLenum data_type = _mesa_get_format_datatype(rb->Format);
+   GLboolean is_unsigned_int = GL_FALSE;
+   GLboolean is_signed_int = GL_FALSE;
+
+   if (!_mesa_is_color_format(internalFormat)) {
+      return GL_INVALID_OPERATION;
+   }
+
+   is_unsigned_int = _mesa_is_enum_format_unsigned_int(internalFormat);
+   if (!is_unsigned_int) {
+      is_signed_int = _mesa_is_enum_format_signed_int(internalFormat);
+   }
+
+   switch (format) {
+   case GL_RGBA:
+      if (type == GL_FLOAT && data_type == GL_FLOAT)
+         return GL_NO_ERROR; /* EXT_color_buffer_float */
+      if (type == GL_UNSIGNED_BYTE && data_type == GL_UNSIGNED_NORMALIZED)
+         return GL_NO_ERROR;
+      if (internalFormat == GL_RGB10_A2 &&
+          type == GL_UNSIGNED_INT_2_10_10_10_REV)
+         return GL_NO_ERROR;
+      if (internalFormat == GL_RGB10_A2UI && type == GL_UNSIGNED_BYTE)
+         return GL_NO_ERROR;
+      break;
+   case GL_BGRA:
+      /* GL_EXT_read_format_bgra */
+      if (type == GL_UNSIGNED_BYTE ||
+          type == GL_UNSIGNED_SHORT_4_4_4_4_REV ||
+          type == GL_UNSIGNED_SHORT_1_5_5_5_REV)
+         return GL_NO_ERROR;
+      break;
+   case GL_RGBA_INTEGER:
+      if ((is_signed_int && type == GL_INT) ||
+          (is_unsigned_int && type == GL_UNSIGNED_INT))
+         return GL_NO_ERROR;
+      break;
+   }
+
+   return GL_INVALID_OPERATION;
+}
+
+
 void GLAPIENTRY
 _mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height,
                      GLenum format, GLenum type, GLsizei bufSize,
                       GLvoid *pixels )
 {
    GLenum err = GL_NO_ERROR;
+   struct gl_renderbuffer *rb;
 
    GET_CURRENT_CONTEXT(ctx);
-   ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
 
+   FLUSH_VERTICES(ctx, 0);
    FLUSH_CURRENT(ctx, 0);
 
    if (MESA_VERBOSE & VERBOSE_API)
@@ -699,6 +929,22 @@ _mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height,
       return;
    }
 
+   if (ctx->NewState)
+      _mesa_update_state(ctx);
+
+   if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
+      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
+                  "glReadPixels(incomplete framebuffer)" );
+      return;
+   }
+
+   rb = _mesa_get_read_renderbuffer_for_format(ctx, format);
+   if (rb == NULL) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glReadPixels(read buffer)");
+      return;
+   }
+
    /* OpenGL ES 1.x and OpenGL ES 2.0 impose additional restrictions on the
     * combinations of format and type that can be used.
     *
@@ -708,13 +954,20 @@ _mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height,
     * combination is, and Mesa can handle anything valid.  Just work instead.
     */
    if (_mesa_is_gles(ctx)) {
-      if (ctx->Version < 30) {
+      if (ctx->API == API_OPENGLES2 &&
+          _mesa_is_color_format(format) &&
+          _mesa_get_color_read_format(ctx) == format &&
+          _mesa_get_color_read_type(ctx) == type) {
+         err = GL_NO_ERROR;
+      } else if (ctx->Version < 30) {
          err = _mesa_es_error_check_format_and_type(format, type, 2);
          if (err == GL_NO_ERROR) {
             if (type == GL_FLOAT || type == GL_HALF_FLOAT_OES) {
                err = GL_INVALID_OPERATION;
             }
          }
+      } else {
+         err = read_pixels_es3_error_check(format, type, rb);
       }
 
       if (err == GL_NO_ERROR && (format == GL_DEPTH_COMPONENT
@@ -730,9 +983,6 @@ _mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height,
       }
    }
 
-   if (ctx->NewState)
-      _mesa_update_state(ctx);
-
    err = _mesa_error_check_format_and_type(ctx, format, type);
    if (err != GL_NO_ERROR) {
       _mesa_error(ctx, err, "glReadPixels(invalid format %s and/or type %s)",
@@ -741,12 +991,6 @@ _mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height,
       return;
    }
 
-   if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) {
-      _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT,
-                  "glReadPixels(incomplete framebuffer)" );
-      return;
-   }
-
    if (_mesa_is_user_fbo(ctx->ReadBuffer) &&
        ctx->ReadBuffer->Visual.samples > 0) {
       _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(multisample FBO)");
@@ -789,7 +1033,7 @@ _mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height,
    }
 
    if (_mesa_is_bufferobj(ctx->Pack.BufferObj) &&
-       _mesa_bufferobj_mapped(ctx->Pack.BufferObj)) {
+       _mesa_check_disallowed_mapping(ctx->Pack.BufferObj)) {
       /* buffer is mapped - that's an error */
       _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(PBO is mapped)");
       return;