[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