mesa: add some comments in copyimage.c
[mesa.git] / src / mesa / main / copyimage.c
index df7d7c2727d1a5783f0a9d0d5358ec6e7afa08fb..e8732c6175b63b35457e34080c869fa1084a11e7 100644 (file)
 #include "texobj.h"
 #include "fbobject.h"
 #include "textureview.h"
-
+#include "glformats.h"
+
+enum mesa_block_class {
+   BLOCK_CLASS_128_BITS,
+   BLOCK_CLASS_64_BITS
+};
+
+/**
+ * Prepare the source or destination resource, including:
+ * - Error checking
+ * - Creating texture wrappers for renderbuffers
+ * \param name  the texture or renderbuffer name
+ * \param target  GL_TEXTURE target or GL_RENDERBUFFER.  For the later, will
+ *                be changed to a compatible GL_TEXTURE target.
+ * \param level  mipmap level
+ * \param tex_obj  returns a pointer to a texture object
+ * \param tex_image  returns a pointer to a texture image
+ * \param tmp_tex  returns temporary texture object name
+ * \return true if success, false if error
+ */
 static bool
 prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level,
                struct gl_texture_object **tex_obj,
                struct gl_texture_image **tex_image, GLuint *tmp_tex,
                const char *dbg_prefix)
 {
-   struct gl_renderbuffer *rb;
-
    if (name == 0) {
       _mesa_error(ctx, GL_INVALID_VALUE,
                   "glCopyImageSubData(%sName = %d)", dbg_prefix, name);
@@ -81,7 +98,7 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level,
    }
 
    if (*target == GL_RENDERBUFFER) {
-      rb = _mesa_lookup_renderbuffer(ctx, name);
+      struct gl_renderbuffer *rb = _mesa_lookup_renderbuffer(ctx, name);
       if (!rb) {
          _mesa_error(ctx, GL_INVALID_VALUE,
                      "glCopyImageSubData(%sName = %u)", dbg_prefix, name);
@@ -152,7 +169,7 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level,
          return false;
       }
 
-      *tex_image = _mesa_select_tex_image(ctx, *tex_obj, *target, level);
+      *tex_image = _mesa_select_tex_image(*tex_obj, *target, level);
       if (!*tex_image) {
          _mesa_error(ctx, GL_INVALID_VALUE,
                      "glCopyImageSubData(%sLevel = %u)", dbg_prefix, level);
@@ -163,8 +180,15 @@ prepare_target(struct gl_context *ctx, GLuint name, GLenum *target, int level,
    return true;
 }
 
+
+/**
+ * Check that the x,y,z,width,height,region is within the texture image
+ * dimensions.
+ * \return true if bounds OK, false if regions is out of bounds
+ */
 static bool
-check_region_bounds(struct gl_context *ctx, struct gl_texture_image *tex_image,
+check_region_bounds(struct gl_context *ctx,
+                    const struct gl_texture_image *tex_image,
                     int x, int y, int z, int width, int height, int depth,
                     const char *dbg_prefix)
 {
@@ -182,6 +206,7 @@ check_region_bounds(struct gl_context *ctx, struct gl_texture_image *tex_image,
       return false;
    }
 
+   /* Check X direction */
    if (x + width > tex_image->Width) {
       _mesa_error(ctx, GL_INVALID_VALUE,
                   "glCopyImageSubData(%sX or %sWidth exceeds image bounds)",
@@ -189,6 +214,7 @@ check_region_bounds(struct gl_context *ctx, struct gl_texture_image *tex_image,
       return false;
    }
 
+   /* Check Y direction */
    switch (tex_image->TexObject->Target) {
    case GL_TEXTURE_1D:
    case GL_TEXTURE_1D_ARRAY:
@@ -209,6 +235,7 @@ check_region_bounds(struct gl_context *ctx, struct gl_texture_image *tex_image,
       break;
    }
 
+   /* Check Z direction */
    switch (tex_image->TexObject->Target) {
    case GL_TEXTURE_1D:
    case GL_TEXTURE_2D:
@@ -253,6 +280,124 @@ check_region_bounds(struct gl_context *ctx, struct gl_texture_image *tex_image,
    return true;
 }
 
+static bool
+compressed_format_compatible(const struct gl_context *ctx,
+                             GLenum compressedFormat, GLenum otherFormat)
+{
+   enum mesa_block_class compressedClass, otherClass;
+
+   /* Two view-incompatible compressed formats are never compatible. */
+   if (_mesa_is_compressed_format(ctx, otherFormat)) {
+      return false;
+   }
+
+   /*
+    * From ARB_copy_image spec:
+    *    Table 4.X.1 (Compatible internal formats for copying between
+    *                 compressed and uncompressed internal formats)
+    *    ---------------------------------------------------------------------
+    *    | Texel / | Uncompressed      |                                     |
+    *    | Block   | internal format   | Compressed internal format          |
+    *    | size    |                   |                                     |
+    *    ---------------------------------------------------------------------
+    *    | 128-bit | RGBA32UI,         | COMPRESSED_RGBA_S3TC_DXT3_EXT,      |
+    *    |         | RGBA32I,          | COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT,|
+    *    |         | RGBA32F           | COMPRESSED_RGBA_S3TC_DXT5_EXT,      |
+    *    |         |                   | COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT,|
+    *    |         |                   | COMPRESSED_RG_RGTC2,                |
+    *    |         |                   | COMPRESSED_SIGNED_RG_RGTC2,         |
+    *    |         |                   | COMPRESSED_RGBA_BPTC_UNORM,         |
+    *    |         |                   | COMPRESSED_SRGB_ALPHA_BPTC_UNORM,   |
+    *    |         |                   | COMPRESSED_RGB_BPTC_SIGNED_FLOAT,   |
+    *    |         |                   | COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT  |
+    *    ---------------------------------------------------------------------
+    *    | 64-bit  | RGBA16F, RG32F,   | COMPRESSED_RGB_S3TC_DXT1_EXT,       |
+    *    |         | RGBA16UI, RG32UI, | COMPRESSED_SRGB_S3TC_DXT1_EXT,      |
+    *    |         | RGBA16I, RG32I,   | COMPRESSED_RGBA_S3TC_DXT1_EXT,      |
+    *    |         | RGBA16,           | COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT,|
+    *    |         | RGBA16_SNORM      | COMPRESSED_RED_RGTC1,               |
+    *    |         |                   | COMPRESSED_SIGNED_RED_RGTC1         |
+    *    ---------------------------------------------------------------------
+    */
+
+   switch (compressedFormat) {
+      case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+      case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
+      case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+      case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
+      case GL_COMPRESSED_RG_RGTC2:
+      case GL_COMPRESSED_SIGNED_RG_RGTC2:
+      case GL_COMPRESSED_RGBA_BPTC_UNORM:
+      case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
+      case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
+      case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
+         compressedClass = BLOCK_CLASS_128_BITS;
+         break;
+      case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+      case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
+      case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+      case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
+      case GL_COMPRESSED_RED_RGTC1:
+      case GL_COMPRESSED_SIGNED_RED_RGTC1:
+         compressedClass = BLOCK_CLASS_64_BITS;
+         break;
+      default:
+         return false;
+   }
+
+   switch (otherFormat) {
+      case GL_RGBA32UI:
+      case GL_RGBA32I:
+      case GL_RGBA32F:
+         otherClass = BLOCK_CLASS_128_BITS;
+         break;
+      case GL_RGBA16F:
+      case GL_RG32F:
+      case GL_RGBA16UI:
+      case GL_RG32UI:
+      case GL_RGBA16I:
+      case GL_RG32I:
+      case GL_RGBA16:
+      case GL_RGBA16_SNORM:
+         otherClass = BLOCK_CLASS_64_BITS;
+         break;
+      default:
+         return false;
+   }
+
+   return compressedClass == otherClass;
+}
+
+static bool
+copy_format_compatible(const struct gl_context *ctx,
+                       GLenum srcFormat, GLenum dstFormat)
+{
+   /*
+    * From ARB_copy_image spec:
+    *    For the purposes of CopyImageSubData, two internal formats
+    *    are considered compatible if any of the following conditions are
+    *    met:
+    *    * the formats are the same,
+    *    * the formats are considered compatible according to the
+    *      compatibility rules used for texture views as defined in
+    *      section 3.9.X. In particular, if both internal formats are listed
+    *      in the same entry of Table 3.X.2, they are considered compatible, or
+    *    * one format is compressed and the other is uncompressed and
+    *      Table 4.X.1 lists the two formats in the same row.
+    */
+
+   if (_mesa_texture_view_compatible_format(ctx, srcFormat, dstFormat)) {
+      /* Also checks if formats are equal. */
+      return true;
+   } else if (_mesa_is_compressed_format(ctx, srcFormat)) {
+      return compressed_format_compatible(ctx, srcFormat, dstFormat);
+   } else if (_mesa_is_compressed_format(ctx, dstFormat)) {
+      return compressed_format_compatible(ctx, dstFormat, srcFormat);
+   }
+
+   return false;
+}
+
 void GLAPIENTRY
 _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel,
                        GLint srcX, GLint srcY, GLint srcZ,
@@ -265,7 +410,7 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel,
    struct gl_texture_object *srcTexObj, *dstTexObj;
    struct gl_texture_image *srcTexImage, *dstTexImage;
    GLuint src_bw, src_bh, dst_bw, dst_bh;
-   int i, srcNewZ, dstNewZ, Bpt;
+   int i;
 
    if (MESA_VERBOSE & VERBOSE_API)
       _mesa_debug(ctx, "glCopyImageSubData(%u, %s, %d, %d, %d, %d, "
@@ -306,15 +451,6 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel,
       goto cleanup;
    }
 
-   /* Very simple sanity check.  This is sufficient if one of the textures
-    * is compressed. */
-   Bpt = _mesa_get_format_bytes(srcTexImage->TexFormat);
-   if (_mesa_get_format_bytes(dstTexImage->TexFormat) != Bpt) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glCopyImageSubData(internalFormat mismatch)");
-      goto cleanup;
-   }
-
    if (!check_region_bounds(ctx, srcTexImage, srcX, srcY, srcZ,
                             srcWidth, srcHeight, srcDepth, "src"))
       goto cleanup;
@@ -324,20 +460,16 @@ _mesa_CopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLevel,
                             (srcHeight / src_bh) * dst_bh, srcDepth, "dst"))
       goto cleanup;
 
-   if (_mesa_is_format_compressed(srcTexImage->TexFormat)) {
-      /* XXX: Technically, we should probaby do some more specific checking
-       * here.  However, this should be sufficient for all compressed
-       * formats that mesa supports since it is a direct memory copy.
-       */
-   } else if (_mesa_is_format_compressed(dstTexImage->TexFormat)) {
-   } else if (_mesa_texture_view_compatible_format(ctx,
-                                                   srcTexImage->InternalFormat,
-                                                   dstTexImage->InternalFormat)) {
-   } else {
-      return; /* Error logged by _mesa_texture_view_compatible_format */
+   if (!copy_format_compatible(ctx, srcTexImage->InternalFormat,
+                               dstTexImage->InternalFormat)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glCopyImageSubData(internalFormat mismatch)");
+      goto cleanup;
    }
 
    for (i = 0; i < srcDepth; ++i) {
+      int srcNewZ, dstNewZ;
+
       if (srcTexObj->Target == GL_TEXTURE_CUBE_MAP) {
          srcTexImage = srcTexObj->Image[i + srcZ][srcLevel];
          srcNewZ = 0;