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

Laura Ekstrand laura at jlekstrand.net
Wed Dec 17 17:34:55 PST 2014


On Sat, Dec 13, 2014 at 6:42 AM, Brian Paul <brianp at vmware.com> wrote:
>
> ---
>  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
>
You didn't use the new variable caller in dimensions_error_check.

>  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.
> + */
>
Again, pass in target, too.

> +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;
> +   }
> +
>
It seems like you could get NULL in a really weird edge case, so maybe you
should remove the assert.

> +   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 {
>
Hope this doesn't segfault somewhere else.  It might be better if you
return here.

> +      /* 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);
>
Checking for texObj->Target == 0 here is kind of awkward.  It would be
better to do that in a legal_texgetimage_target check.

> +   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;
> +   }
> +

I'd do the legal_tex_target check for consistency.

> +   target = texObj->Target;
>
Again, this doesn't allow the user to get more than one face.  But it may
be easier to do that when it is merged with my DSA GetTextureImage.

> +   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
>
> _______________________________________________
> mesa-dev mailing list
> mesa-dev at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/mesa-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/mesa-dev/attachments/20141217/c9e7fe27/attachment-0001.html>


More information about the mesa-dev mailing list