[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