[Mesa-dev] [PATCH 07/11] mesa: implement glGetCompressedTextureSubImage()

Brian Paul brianp at vmware.com
Sat Dec 13 06:42:50 PST 2014


---
 src/mesa/main/texgetimage.c | 440 +++++++++++++++++++++++++++++---------------
 src/mesa/main/texgetimage.h |   7 +
 2 files changed, 297 insertions(+), 150 deletions(-)

diff --git a/src/mesa/main/texgetimage.c b/src/mesa/main/texgetimage.c
index ced6a34..ac0be35 100644
--- a/src/mesa/main/texgetimage.c
+++ b/src/mesa/main/texgetimage.c
@@ -25,7 +25,8 @@
 
 
 /**
- * Code for glGetTexImage() and glGetCompressedTexImage().
+ * Code for glGetTexImage(), glGetCompressedTexImage(),
+ * glGetTextureSubImage() and glGetCompressedTextureSubImage(),
  */
 
 
@@ -717,10 +718,8 @@ _mesa_get_compressed_texsubimage(struct gl_context *ctx,
    GLubyte *dest;
 
    _mesa_compute_compressed_pixelstore(dimensions, texImage->TexFormat,
-                                       texImage->Width, texImage->Height,
-                                       texImage->Depth,
-                                       &ctx->Pack,
-                                       &store);
+                                       width, height, depth,
+                                       &ctx->Pack, &store);
 
    if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
       /* pack texture image into a PBO */
@@ -731,7 +730,7 @@ _mesa_get_compressed_texsubimage(struct gl_context *ctx,
       if (!dest) {
          /* out of memory or other unexpected error */
          _mesa_error(ctx, GL_OUT_OF_MEMORY,
-                     "glGetCompresssedTexImage(map PBO failed)");
+                "glGetCompresssedTexImage/TextureSubImage(map PBO failed)");
          return;
       }
       dest = ADD_POINTERS(dest, img);
@@ -746,25 +745,27 @@ _mesa_get_compressed_texsubimage(struct gl_context *ctx,
       GLubyte *src;
 
       /* map src texture buffer */
-      ctx->Driver.MapTextureImage(ctx, texImage, 0,
-                                  0, 0, texImage->Width, texImage->Height,
+      ctx->Driver.MapTextureImage(ctx, texImage, zoffset + slice,
+                                  xoffset, yoffset, width, height,
                                   GL_MAP_READ_BIT, &src, &srcRowStride);
 
       if (src) {
-
+         /* Copy a row of blocks */
          for (i = 0; i < store.CopyRowsPerSlice; i++) {
             memcpy(dest, src, store.CopyBytesPerRow);
             dest += store.TotalBytesPerRow;
             src += srcRowStride;
          }
 
-         ctx->Driver.UnmapTextureImage(ctx, texImage, 0);
+         ctx->Driver.UnmapTextureImage(ctx, texImage, zoffset + slice);
 
          /* Advance to next slice */
          dest += store.TotalBytesPerRow * (store.TotalRowsPerSlice - store.CopyRowsPerSlice);
 
       } else {
-         _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetCompresssedTexImage");
+         _mesa_error(ctx, GL_OUT_OF_MEMORY,
+                     "glGetCompresssedTexImage/TextureSubImage");
+         break; /* don't try the remaining slices */
       }
    }
 
@@ -821,6 +822,12 @@ getteximage_error_check(struct gl_context *ctx,
    const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
    GLenum baseFormat, err;
 
+   if (maxLevels == 0) {
+      /* invalid texture (gen'd but never defined) */
+      _mesa_error(ctx, GL_INVALID_OPERATION, "%s(invalid texture)", caller);
+      return GL_TRUE;
+   }  
+
    assert(maxLevels != 0);
    if (level < 0 || level >= maxLevels) {
       _mesa_error(ctx, GL_INVALID_VALUE, "%s(level)", caller);
@@ -888,7 +895,9 @@ getteximage_error_check(struct gl_context *ctx,
 
 
 /**
- * Do error checking related to the PBO and image size.
+ * Do error checking related to the PBO and image size (for returning
+ * uncompressed images only).
+ * \return true if error is found, false otherwise.
  */
 static bool
 pbo_error_check(struct gl_context *ctx, GLenum target,
@@ -1006,145 +1015,36 @@ _mesa_GetTexImage( GLenum target, GLint level, GLenum format,
 
 
 /**
- * Do error checking for a glGetCompressedTexImage() call.
- * \return GL_TRUE if any error, GL_FALSE if no errors.
+ * Compute the number of bytes that will be read/written from/to a buffer
+ * when packing/unpacking a compressed image.  This observes the given GL
+ * pixel store state which may specify non-default GL_SKIP_PIXEL,
+ * GL_ROW_LENGTH, etc. values for compressed images.
+ * Basically, if the value we compute here is larger than the dest/src buffer
+ * size, we'll have to raise an GL_INVALID_OPERATION (out of bounds) error.
+ *
+ * \return number of bytes which will be read/written.
  */
-static GLboolean
-getcompressedteximage_error_check(struct gl_context *ctx, GLenum target,
-                                  GLint level, GLsizei clientMemSize, GLvoid *img)
+static GLsizei
+packed_compressed_size(GLuint dimensions, mesa_format format,
+                       GLsizei width, GLsizei height, GLsizei depth,
+                       const struct gl_pixelstore_attrib *packing)
 {
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
-   const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
-   GLuint compressedSize, dimensions;
-
-   if (!legal_getteximage_target(ctx, target)) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImage(target=0x%x)",
-                  target);
-      return GL_TRUE;
-   }
-
-   assert(maxLevels != 0);
-   if (level < 0 || level >= maxLevels) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glGetCompressedTexImageARB(bad level = %d)", level);
-      return GL_TRUE;
-   }
-
-   texObj = _mesa_get_current_tex_object(ctx, target);
-   if (!texObj) {
-      _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImageARB(target)");
-      return GL_TRUE;
-   }
-
-   texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-
-   if (!texImage) {
-      /* probably invalid mipmap level */
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glGetCompressedTexImageARB(level)");
-      return GL_TRUE;
-   }
-
-   if (!_mesa_is_format_compressed(texImage->TexFormat)) {
-      _mesa_error(ctx, GL_INVALID_OPERATION,
-                  "glGetCompressedTexImageARB(texture is not compressed)");
-      return GL_TRUE;
-   }
-
-   compressedSize = _mesa_format_image_size(texImage->TexFormat,
-                                            texImage->Width,
-                                            texImage->Height,
-                                            texImage->Depth);
-
-   /* Check for invalid pixel storage modes */
-   dimensions = _mesa_get_texture_dimensions(texImage->TexObject->Target);
-   if (!_mesa_compressed_pixel_storage_error_check(ctx, dimensions,
-                                              &ctx->Pack,
-                                              "glGetCompressedTexImageARB")) {
-      return GL_TRUE;
-   }
-
-   if (!_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
-      /* do bounds checking on writing to client memory */
-      if (clientMemSize < (GLsizei) compressedSize) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glGetnCompressedTexImageARB(out of bounds access:"
-                     " bufSize (%d) is too small)", clientMemSize);
-         return GL_TRUE;
-      }
-   } else {
-      /* do bounds checking on PBO write */
-      if ((const GLubyte *) img + compressedSize >
-          (const GLubyte *) ctx->Pack.BufferObj->Size) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glGetCompressedTexImage(out of bounds PBO access)");
-         return GL_TRUE;
-      }
-
-      /* make sure PBO is not mapped */
-      if (_mesa_check_disallowed_mapping(ctx->Pack.BufferObj)) {
-         _mesa_error(ctx, GL_INVALID_OPERATION,
-                     "glGetCompressedTexImage(PBO is mapped)");
-         return GL_TRUE;
-      }
-   }
-
-   return GL_FALSE;
+   struct compressed_pixelstore st;
+   GLsizei totalBytes;
+
+   _mesa_compute_compressed_pixelstore(dimensions, format,
+                                       width, height, depth,
+                                       packing, &st);
+   totalBytes =
+      (st.CopySlices - 1) * st.TotalRowsPerSlice * st.TotalBytesPerRow +
+      st.SkipBytes +
+      (st.CopyRowsPerSlice - 1) * st.TotalBytesPerRow +
+      st.CopyBytesPerRow;
+
+   return totalBytes;
 }
 
 
-void GLAPIENTRY
-_mesa_GetnCompressedTexImageARB(GLenum target, GLint level, GLsizei bufSize,
-                                GLvoid *img)
-{
-   struct gl_texture_object *texObj;
-   struct gl_texture_image *texImage;
-   GET_CURRENT_CONTEXT(ctx);
-
-   FLUSH_VERTICES(ctx, 0);
-
-   if (getcompressedteximage_error_check(ctx, target, level, bufSize, img)) {
-      return;
-   }
-
-   if (!_mesa_is_bufferobj(ctx->Pack.BufferObj) && !img) {
-      /* not an error, do nothing */
-      return;
-   }
-
-   texObj = _mesa_get_current_tex_object(ctx, target);
-   texImage = _mesa_select_tex_image(ctx, texObj, target, level);
-
-   if (_mesa_is_zero_size_texture(texImage))
-      return;
-
-   if (MESA_VERBOSE & (VERBOSE_API | VERBOSE_TEXTURE)) {
-      _mesa_debug(ctx,
-                  "glGetCompressedTexImage(tex %u) format = %s, w=%d, h=%d\n",
-                  texObj->Name,
-                  _mesa_get_format_name(texImage->TexFormat),
-                  texImage->Width, texImage->Height);
-   }
-
-   _mesa_lock_texture(ctx, texObj);
-   {
-      ctx->Driver.GetCompressedTexSubImage(ctx, texImage,
-                                           0, 0, 0,
-                                           texImage->Width, texImage->Height,
-                                           texImage->Depth, img);
-   }
-   _mesa_unlock_texture(ctx, texObj);
-}
-
-void GLAPIENTRY
-_mesa_GetCompressedTexImage(GLenum target, GLint level, GLvoid *img)
-{
-   _mesa_GetnCompressedTexImageARB(target, level, INT_MAX, img);
-}
-
-
-
 /**
  * Error-check the offset and size arguments to
  * glGet[Compressed]TextureSubImage().
@@ -1154,7 +1054,8 @@ static bool
 dimensions_error_check(struct gl_context *ctx,
                        struct gl_texture_image *texImage,
                        GLint xoffset, GLint yoffset, GLint zoffset,
-                       GLsizei width, GLsizei height, GLsizei depth)
+                       GLsizei width, GLsizei height, GLsizei depth,
+                       const char *caller)
 {
    const GLenum target = texImage->TexObject->Target;
 
@@ -1277,6 +1178,169 @@ dimensions_error_check(struct gl_context *ctx,
 }
 
 
+/**
+ * Do error checking for glGetCompressedTexImage() and
+ * glGetCompressedTextureSubImage().
+ * \return true if any error, false if no errors.
+ */
+static bool
+getcompressedteximage_error_check(struct gl_context *ctx,
+                                  struct gl_texture_object *texObj,
+                                  GLint level,
+                                  GLint xoffset, GLint yoffset, GLint zoffset,
+                                  GLsizei width, GLint height, GLint depth,
+                                  GLsizei clientMemSize, GLvoid *img,
+                                  const char *caller)
+{
+   GLenum target = texObj->Target;
+   struct gl_texture_image *texImage;
+   const GLint maxLevels = _mesa_max_texture_levels(ctx, target);
+   const GLuint dimensions = _mesa_get_texture_dimensions(texObj->Target);
+   GLsizei totalBytes;
+
+   assert(maxLevels != 0);
+   if (level < 0 || level >= maxLevels) {
+      _mesa_error(ctx, GL_INVALID_VALUE,
+                  "%s(bad level = %d)", caller, level);
+      return true;
+   }
+
+   texImage = _mesa_select_tex_image(ctx, texObj, target, level);
+
+   if (!texImage) {
+      /* probably invalid mipmap level */
+      _mesa_error(ctx, GL_INVALID_VALUE, "%s(level)", caller);
+      return true;
+   }
+
+   if (!_mesa_is_format_compressed(texImage->TexFormat)) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "%s(texture is not compressed)", caller);
+      return true;
+   }
+
+   /* check offset, size against texture and block size */
+   if (dimensions_error_check(ctx, texImage, xoffset, yoffset, zoffset,
+                              width, height, depth, caller)) {
+      return true;
+   }
+
+   /* Check for invalid pixel storage modes */
+   if (!_mesa_compressed_pixel_storage_error_check(ctx, dimensions,
+                                                   &ctx->Pack,
+                                                   caller)) {
+      return true;
+   }
+
+   /* Compute number of bytes that may be touched in the dest buffer */
+   totalBytes = packed_compressed_size(dimensions, texImage->TexFormat,
+                                       width, height, depth,
+                                       &ctx->Pack);
+
+   /* Do dest buffer bounds checking */
+   if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) {
+      /* do bounds checking on PBO write */
+      if ((GLubyte *) img + totalBytes >
+          (GLubyte *) ctx->Pack.BufferObj->Size) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "%s(out of bounds PBO access)", caller);
+         return true;
+      }
+
+      /* make sure PBO is not mapped */
+      if (_mesa_check_disallowed_mapping(ctx->Pack.BufferObj)) {
+         _mesa_error(ctx, GL_INVALID_OPERATION, "%s(PBO is mapped)", caller);
+         return true;
+      }
+   }
+   else {
+      /* do bounds checking on writing to client memory */
+      if (totalBytes > clientMemSize) {
+         _mesa_error(ctx, GL_INVALID_OPERATION,
+                     "%s(out of bounds access: bufSize (%d) is too small)",
+                     caller, clientMemSize);
+         return true;
+      }
+   }
+
+   return false;
+}
+
+
+void GLAPIENTRY
+_mesa_GetnCompressedTexImageARB(GLenum target, GLint level, GLsizei bufSize,
+                                GLvoid *img)
+{
+   struct gl_texture_object *texObj;
+   struct gl_texture_image *texImage;
+   GLsizei width, height, depth;
+   GET_CURRENT_CONTEXT(ctx);
+
+   FLUSH_VERTICES(ctx, 0);
+
+   if (!legal_getteximage_target(ctx, target)) {
+      _mesa_error(ctx, GL_INVALID_ENUM, "glGetCompressedTexImage(target=0x%x)",
+                  target);
+      return;
+   }
+
+   texObj = _mesa_get_current_tex_object(ctx, target);
+   assert(texObj);
+   if (!texObj) {
+      return; /* should never happen */
+   }
+
+   texImage = _mesa_select_tex_image(ctx, texObj, target, level);
+   if (texImage) {
+      width = texImage->Width;
+      height = texImage->Height;
+      depth = texImage->Depth;
+   }
+   else {
+      /* some error, we'll find it later */
+      width = height = depth = 0;
+   }
+
+   if (getcompressedteximage_error_check(ctx, texObj, level,
+                                         0, 0, 0, width, height, depth,
+                                         bufSize, img,
+                                         "glGet[n]CompressedTexImage")) {
+      return;
+   }
+
+   if (!_mesa_is_bufferobj(ctx->Pack.BufferObj) && !img) {
+      /* not an error, do nothing */
+      return;
+   }
+
+   if (_mesa_is_zero_size_texture(texImage))
+      return;
+
+   if (MESA_VERBOSE & (VERBOSE_API | VERBOSE_TEXTURE)) {
+      _mesa_debug(ctx,
+                  "glGetCompressedTexImage(tex %u) format = %s, w=%d, h=%d\n",
+                  texObj->Name,
+                  _mesa_get_format_name(texImage->TexFormat),
+                  texImage->Width, texImage->Height);
+   }
+
+   _mesa_lock_texture(ctx, texObj);
+   {
+      ctx->Driver.GetCompressedTexSubImage(ctx, texImage,
+                                           0, 0, 0,
+                                           texImage->Width, texImage->Height,
+                                           texImage->Depth, img);
+   }
+   _mesa_unlock_texture(ctx, texObj);
+}
+
+void GLAPIENTRY
+_mesa_GetCompressedTexImage(GLenum target, GLint level, GLvoid *img)
+{
+   _mesa_GetnCompressedTexImageARB(target, level, INT_MAX, img);
+}
+
+
 
 void GLAPIENTRY
 _mesa_GetTextureSubImage(GLuint texture, GLint level,
@@ -1293,8 +1357,8 @@ _mesa_GetTextureSubImage(GLuint texture, GLint level,
 
    texObj = _mesa_lookup_texture(ctx, texture);
    if (!texObj) {
-      _mesa_error(ctx, GL_INVALID_VALUE,
-                  "glGetTextureSubImage(invalid texture ID %u\n", texture);
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glGetTextureSubImage(invalid texture ID %u)\n", texture);
       return;
    }
 
@@ -1324,7 +1388,7 @@ _mesa_GetTextureSubImage(GLuint texture, GLint level,
 
    /* check dimensions */
    if (dimensions_error_check(ctx, texImage, xoffset, yoffset, zoffset,
-                              width, height, depth)) {
+                              width, height, depth, "glGetTextureSubImage")) {
       return;
    }
 
@@ -1349,3 +1413,79 @@ _mesa_GetTextureSubImage(GLuint texture, GLint level,
    }
    _mesa_unlock_texture(ctx, texObj);
 }
+
+
+void APIENTRY
+_mesa_GetCompressedTextureSubImage(GLuint texture, GLint level,
+                                   GLint xoffset, GLint yoffset,
+                                   GLint zoffset, GLsizei width,
+                                   GLsizei height, GLsizei depth,
+                                   GLsizei bufSize, void *pixels)
+{
+   struct gl_texture_object *texObj;
+   struct gl_texture_image *texImage;
+   GLenum target;
+
+   GET_CURRENT_CONTEXT(ctx);
+
+   texObj = _mesa_lookup_texture(ctx, texture);
+   if (!texObj || texObj->Target == 0) {
+      _mesa_error(ctx, GL_INVALID_OPERATION,
+                  "glGetCompressedTextureSubImage(invalid texture ID %u)\n",
+                  texture);
+      return;
+   }
+
+   /* common error checking */
+   if (getcompressedteximage_error_check(ctx, texObj, level,
+                                         xoffset, yoffset, zoffset,
+                                         width, height, depth,
+                                         bufSize, pixels,
+                                         "glGetCompressedTextureSubImage")) {
+      return;
+   }
+
+   target = texObj->Target;
+   if (target == GL_TEXTURE_CUBE_MAP) {
+      /* convert z to face/target */
+      if (zoffset >= 6) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glGetCompressedTextureSubImage(cube, zoffset = %d\n",
+                     zoffset);
+         return;
+      }
+      if (depth != 1) {
+         _mesa_error(ctx, GL_INVALID_VALUE,
+                     "glGetCompressedTextureSubImage(cube, depth = %d\n",
+                     depth);
+         return;
+      }
+      target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + zoffset;
+   }
+
+   texImage = _mesa_select_tex_image(ctx, texObj, target, level);
+
+   /* check dimensions */
+   if (dimensions_error_check(ctx, texImage, xoffset, yoffset, zoffset,
+                              width, height, depth,
+                              "glGetCompressedTextureSubImage")) {
+      return;
+   }
+
+   if (_mesa_is_zero_size_texture(texImage)) {
+      return;  /* nothing to get */
+   }
+
+   if (!_mesa_is_bufferobj(ctx->Pack.BufferObj) && pixels == NULL) {
+      return;  /* not an error, do nothing */
+   }
+
+   _mesa_lock_texture(ctx, texObj);
+   {
+      ctx->Driver.GetCompressedTexSubImage(ctx, texImage,
+                                           xoffset, yoffset, zoffset,
+                                           width, height, depth,
+                                           pixels);
+   }
+   _mesa_unlock_texture(ctx, texObj);
+}
diff --git a/src/mesa/main/texgetimage.h b/src/mesa/main/texgetimage.h
index 593d5b0..65a1ed2 100644
--- a/src/mesa/main/texgetimage.h
+++ b/src/mesa/main/texgetimage.h
@@ -75,5 +75,12 @@ _mesa_GetTextureSubImage(GLuint texture, GLint level,
                          GLenum format, GLenum type, GLsizei bufSize,
                          void *pixels);
 
+extern void APIENTRY
+_mesa_GetCompressedTextureSubImage(GLuint texture, GLint level,
+                                   GLint xoffset, GLint yoffset,
+                                   GLint zoffset, GLsizei width,
+                                   GLsizei height, GLsizei depth,
+                                   GLsizei bufSize, void *pixels);
+
 
 #endif /* TEXGETIMAGE_H */
-- 
1.9.1



More information about the mesa-dev mailing list