[Mesa-dev] [PATCH] Implement the ARB_clear_texture extension

Ian Romanick idr at freedesktop.org
Wed Jun 4 15:42:33 PDT 2014


On 06/04/2014 11:12 AM, Neil Roberts wrote:
> The clear texture extension is used to clear a texture to a given value
> without having to provide a buffer for the whole texture and without having to
> create an FBO. This patch provides a generic implementation that works with
> any driver. There are two approaches, the first being in meta.c which tries to
> create a GL framebuffer to the texture and calls glClear. This can fail
> if the FBO extension is not supported or if the texture can't be used as a
> render target. In that case it will fall back to an implementation in
> texstore.c which maps a region of the texture and just directly writes in the
> values.

GL_EXT_framebuffer_object is always supported, so that shouldn't be a
concern.

> A small problem with this patch is that the fallback approach that maps the
> texture doesn't seem to work with depth-stencil textures. However I think this
> may be a general bug with mapping depth-stencil textures because I seem to get
> the same issue if I try to update the texture using glTexSubImage2D as well.
> You can replicate this if you run the Piglit test
> arb_clear_texture-depth-stencil and set MESA_EXTENSION_OVERRIDE to
> -GL_ARB_framebuffer_object.
> ---
>  src/mapi/glapi/gen/ARB_clear_texture.xml |  32 ++++
>  src/mapi/glapi/gen/gl_API.xml            |   6 +-
>  src/mesa/drivers/common/driverfuncs.c    |   1 +
>  src/mesa/drivers/common/meta.c           | 143 ++++++++++++++++++
>  src/mesa/drivers/common/meta.h           |  14 ++
>  src/mesa/main/dd.h                       |  14 ++
>  src/mesa/main/extensions.c               |   1 +
>  src/mesa/main/teximage.c                 | 251 ++++++++++++++++++++++++++++++-
>  src/mesa/main/teximage.h                 |  12 ++
>  src/mesa/main/texstore.c                 |  70 +++++++++
>  src/mesa/main/texstore.h                 |   7 +

Also need changes to src/mesa/main/tests/dispatch_sanity.cpp.  I guess
you didn't 'make check'. :)

Below I've deleted any files where I didn't have comments... otherwise I
think my comments may have gotten lost.  That's part of the reason more
small patches is better than a signle large patch.

>  11 files changed, 549 insertions(+), 2 deletions(-)
>  create mode 100644 src/mapi/glapi/gen/ARB_clear_texture.xml
> 
> diff --git a/src/mesa/main/teximage.c b/src/mesa/main/teximage.c
> index 845ba80..43c5889 100644
> --- a/src/mesa/main/teximage.c
> +++ b/src/mesa/main/teximage.c
> @@ -51,6 +51,7 @@
>  #include "textureview.h"
>  #include "mtypes.h"
>  #include "glformats.h"
> +#include "texstore.h"
>  
>  
>  /**
> @@ -4544,7 +4545,6 @@ _mesa_TexStorage2DMultisample(GLenum target, GLsizei samples,
>                         "glTexStorage2DMultisample");
>  }
>  
> -
>  void GLAPIENTRY
>  _mesa_TexStorage3DMultisample(GLenum target, GLsizei samples,
>                                GLenum internalformat, GLsizei width,
> @@ -4555,3 +4555,252 @@ _mesa_TexStorage3DMultisample(GLenum target, GLsizei samples,
>                         width, height, depth, fixedsamplelocations, GL_TRUE,
>                         "glTexStorage3DMultisample");
>  }
> +
> +static void
> +clear_tex_image(struct gl_context *ctx,
> +                struct gl_texture_image *texImage, GLint level,
> +                GLint xoffset, GLint yoffset, GLint zoffset,
> +                GLsizei width, GLsizei height, GLsizei depth,
> +                GLenum format, GLenum type,
> +                const void *data)
> +{
> +   struct gl_texture_object *texObj = texImage->TexObject;
> +   static const GLubyte zeroData[MAX_PIXEL_BYTES];
> +   GLubyte clearValue[MAX_PIXEL_BYTES];
> +   GLubyte *clearValuePtr = clearValue;
> +   GLint dimensions;
> +   GLenum err;
> +
> +   dimensions = _mesa_get_texture_dimensions(texObj->Target);
> +
> +   if (texObj->Target == GL_TEXTURE_BUFFER) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION,
> +                  "glClear*TexImage(buffer texture)");
> +      return;
> +   }
> +
> +   if (_mesa_is_compressed_format(ctx, texImage->InternalFormat)) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION,
> +                  "glClear*TexImage(compressed texture)");
> +      return;
> +   }
> +
> +   /* OpenGL ES 1.x and OpenGL ES 2.0 impose additional restrictions on the
> +    * combinations of format and type that can be used.  Formats and types
> +    * that require additional extensions (e.g., GL_FLOAT requires
> +    * GL_OES_texture_float) are filtered elsewhere.
> +    */

This extension doesn't exist in ES, so I don't think any of these ES
checks are useful.

> +   if (_mesa_is_gles(ctx) && !_mesa_is_gles3(ctx)) {
> +      err = _mesa_es_error_check_format_and_type(format, type, dimensions);
> +      if (err != GL_NO_ERROR) {
> +         _mesa_error(ctx, err,
> +                     "glClearTex*Image(format = %s, type = %s)",
> +                     _mesa_lookup_enum_by_nr(format),
> +                     _mesa_lookup_enum_by_nr(type));
> +         return;
> +      }
> +   }
> +
> +   err = _mesa_error_check_format_and_type(ctx, format, type);
> +   if (err != GL_NO_ERROR) {
> +      _mesa_error(ctx, err,
> +                  "glClearTex*Image(incompatible format = %s, type = %s)",
> +                  _mesa_lookup_enum_by_nr(format),
> +                  _mesa_lookup_enum_by_nr(type));
> +      return;
> +   }
> +
> +   if (ctx->Version >= 30 || ctx->Extensions.EXT_texture_integer) {
> +      /* both source and dest must be integer-valued, or neither */
> +      if (_mesa_is_format_integer_color(texImage->TexFormat) !=
> +          _mesa_is_enum_format_integer(format)) {
> +         _mesa_error(ctx, GL_INVALID_OPERATION,
> +                     "glClearTex*Image(integer/non-integer format mismatch)");
> +         return;
> +      }
> +   }
> +
> +   if (!_mesa_texstore(ctx,
> +                       1, /* dims */
> +                       texImage->_BaseFormat,
> +                       texImage->TexFormat,
> +                       0, /* dstRowStride */
> +                       &clearValuePtr,
> +                       1, 1, 1, /* srcWidth/Height/Depth */
> +                       format, type,
> +                       data ? data : zeroData,
> +                       &ctx->DefaultPacking)) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION, "glClearTex*Image");
> +      return;
> +   }
> +
> +   ctx->Driver.ClearTexSubImage(ctx,
> +                                texImage,
> +                                xoffset, yoffset, zoffset,
> +                                width, height, depth,
> +                                data ? clearValue : NULL);

Gallium drivers don't use meta at all, so ctx->Driver.ClearTexSubImage
will be NULL in all Gallium drivers.  Until there is a Gallium
implementation (Ilia may have done one already...), I don't think we can
enable this extension by default.

> +}
> +
> +static struct gl_texture_object *
> +get_tex_obj_for_clear(struct gl_context *ctx,
> +                      GLuint texture)
> +{
> +   struct gl_texture_object *texObj;
> +
> +   if (texture == 0) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION, "glClear*TexImage(zero texture)");
> +      return NULL;
> +   }
> +
> +   texObj = _mesa_lookup_texture(ctx, texture);
> +
> +   if (texObj == NULL) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION, "glClear*TexImage(non-gen name)");
> +      return NULL;
> +   }
> +
> +   if (texObj->Target == 0) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION, "glClear*TexImage(unbound tex)");
> +      return NULL;
> +   }
> +
> +   return texObj;
> +}
> +
> +static int
> +get_tex_images_for_clear(struct gl_context *ctx,
> +                         struct gl_texture_object *texObj,
> +                         GLint level,
> +                         struct gl_texture_image **texImages)
> +{
> +   GLenum target;
> +   int i;
> +
> +   if (level < 0 || level >= MAX_TEXTURE_LEVELS) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION, "glClear*TexImage(invalid level)");
> +      return 0;
> +   }
> +
> +   if (texObj->Target == GL_TEXTURE_CUBE_MAP) {
> +      for (i = 0; i < MAX_FACES; i++) {
> +         target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i;
> +
> +         texImages[i] = _mesa_select_tex_image(ctx, texObj, target, level);
> +         if (texImages[i] == NULL) {
> +            _mesa_error(ctx, GL_INVALID_OPERATION,
> +                        "glClear*TexImage(invalid level)");
> +            return 0;
> +         }
> +      }
> +
> +      return MAX_FACES;
> +   }
> +
> +   texImages[0] = _mesa_select_tex_image(ctx, texObj, texObj->Target, level);
> +
> +   if (texImages[0] == NULL) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION, "glClear*TexImage(invalid level)");
> +      return 0;
> +   }
> +
> +   return 1;
> +}
> +
> +void GLAPIENTRY
> +_mesa_ClearTexImage(GLuint texture, GLint level,
> +                    GLenum format, GLenum type,
> +                    const void *data)
> +{
> +   GET_CURRENT_CONTEXT(ctx);
> +   struct gl_texture_object *texObj;
> +   struct gl_texture_image *texImages[MAX_FACES];
> +   int i, numImages;
> +
> +   texObj = get_tex_obj_for_clear(ctx, texture);
> +
> +   if (texObj == NULL)
> +      return;
> +
> +   _mesa_lock_texture(ctx, texObj);
> +
> +   numImages = get_tex_images_for_clear(ctx, texObj, level, texImages);
> +   if (numImages == 0)
> +      goto out;

Is this actually necessary / better?  With the loop condition below,
this seems like extra code...

> +
> +   for (i = 0; i < numImages; i++) {
> +      clear_tex_image(ctx, texImages[i], level,
> +                      -texImages[i]->Border, /* xoffset */
> +                      -texImages[i]->Border, /* yoffset */
> +                      -texImages[i]->Border, /* zoffset */
> +                      texImages[i]->Width,
> +                      texImages[i]->Height,
> +                      texImages[i]->Depth,
> +                      format, type, data);
> +   }
> +
> + out:
> +   _mesa_unlock_texture(ctx, texObj);
> +}
> +
> +void GLAPIENTRY
> +_mesa_ClearTexSubImage(GLuint texture, GLint level,
> +                       GLint xoffset, GLint yoffset, GLint zoffset,
> +                       GLsizei width, GLsizei height, GLsizei depth,
> +                       GLenum format, GLenum type,
> +                       const void *data)
> +{
> +   GET_CURRENT_CONTEXT(ctx);
> +   struct gl_texture_object *texObj;
> +   struct gl_texture_image *texImages[MAX_FACES];
> +   int i, numImages;
> +   int minDepth, maxDepth;
> +
> +   texObj = get_tex_obj_for_clear(ctx, texture);
> +
> +   if (texObj == NULL)
> +      return;
> +
> +   _mesa_lock_texture(ctx, texObj);
> +
> +   numImages = get_tex_images_for_clear(ctx, texObj, level, texImages);
> +   if (numImages == 0)
> +      goto out;
> +
> +   if (numImages == 1) {
> +      minDepth = -texImages[0]->Border;
> +      maxDepth = texImages[0]->Depth;
> +   } else {
> +      minDepth = 0;
> +      maxDepth = numImages;
> +   }
> +
> +   if (xoffset < -texImages[0]->Border ||
> +       yoffset < -texImages[0]->Border ||
> +       zoffset < minDepth ||
> +       width < 0 ||
> +       height < 0 ||
> +       depth < 0 ||
> +       xoffset + width > texImages[0]->Width ||
> +       yoffset + height > texImages[0]->Height ||
> +       zoffset + depth > maxDepth) {
> +      _mesa_error(ctx, GL_INVALID_OPERATION,
> +                  "glClearSubTexImage(invalid dimensions)");
> +      goto out;
> +   }
> +
> +   if (numImages == 1) {
> +      clear_tex_image(ctx, texImages[0], level,
> +                      xoffset, yoffset, zoffset,
> +                      width, height, depth,
> +                      format, type, data);
> +   } else {
> +      for (i = zoffset; i < zoffset + depth; i++)
> +         clear_tex_image(ctx, texImages[i], level,
> +                         xoffset, yoffset, 0,
> +                         width, height, 1,
> +                         format, type, data);
> +   }
> +
> + out:
> +   _mesa_unlock_texture(ctx, texObj);
> +}



More information about the mesa-dev mailing list