gallium: implement u_default_clear_texture
authorItalo Nicola <italonicola@collabora.com>
Mon, 19 Jun 2023 22:04:15 +0000 (22:04 +0000)
committerMarge Bot <emma+marge@anholt.net>
Mon, 10 Jul 2023 15:23:06 +0000 (15:23 +0000)
util_clear_texture implements clear_texture through a memset.
This patch implements u_default_clear_texture, which tries to clear the
given texture using a surface plus clear_render_target or
clear_depth_stencil.

In case this path fails, either because the formats are non-renderable
or for some other reason, we fallback to `util_clear_texture`, which is
guaranteed to work.

This will allow us to make ARB_clear_texture available to every driver,
as well as provide HW acceleration for the clear_texture operation.

If some hardware doesn't want to use it, such as llvmpipe, it can always
just directly point to the software version using pipe->clear_texture.

Signed-off-by: Italo Nicola <italonicola@collabora.com>
Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Reviewed-by: Erik Faye-Lund <erik.faye-lund@collabora.com>
Reviewed-by: Marek Olšák <marek.olsak@amd.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/23735>

src/gallium/auxiliary/util/u_surface.c
src/gallium/auxiliary/util/u_surface.h
src/util/format/u_format.h

index 5d3ac0cdd60f0729a1dd2954c7861418cff685e5..2746bdbd700f7eae1d958ee4e15b86e2dccb19e3 100644 (file)
@@ -611,6 +611,125 @@ util_clear_depth_stencil_texture(struct pipe_context *pipe,
 }
 
 
+/* Try to clear the texture as a surface, returns true if successful.
+ */
+static bool
+util_clear_texture_as_surface(struct pipe_context *pipe,
+                              struct pipe_resource *res,
+                              unsigned level,
+                              const struct pipe_box *box,
+                              const void *data)
+{
+   struct pipe_surface tmpl = {{0}}, *sf;
+
+   tmpl.format = res->format;
+   tmpl.u.tex.first_layer = box->z;
+   tmpl.u.tex.last_layer = box->z + box->depth - 1;
+   tmpl.u.tex.level = level;
+
+   if (util_format_is_depth_or_stencil(res->format)) {
+      if (!pipe->clear_depth_stencil)
+         return false;
+
+      sf = pipe->create_surface(pipe, res, &tmpl);
+      if (!sf)
+         return false;
+
+      float depth = 0;
+      uint8_t stencil = 0;
+      unsigned clear = 0;
+      const struct util_format_description *desc =
+         util_format_description(tmpl.format);
+
+      if (util_format_has_depth(desc)) {
+         clear |= PIPE_CLEAR_DEPTH;
+         util_format_unpack_z_float(tmpl.format, &depth, data, 1);
+      }
+      if (util_format_has_stencil(desc)) {
+         clear |= PIPE_CLEAR_STENCIL;
+         util_format_unpack_s_8uint(tmpl.format, &stencil, data, 1);
+      }
+      pipe->clear_depth_stencil(pipe, sf, clear, depth, stencil,
+                                box->x, box->y, box->width, box->height,
+                                false);
+
+      pipe_surface_reference(&sf, NULL);
+   } else {
+      if (!pipe->clear_render_target)
+         return false;
+
+      if (!pipe->screen->is_format_supported(pipe->screen, tmpl.format,
+                  res->target, 0, 0,
+                  PIPE_BIND_RENDER_TARGET)) {
+         tmpl.format = util_format_as_renderable(tmpl.format);
+
+         if (tmpl.format == PIPE_FORMAT_NONE)
+            return false;
+
+         if (!pipe->screen->is_format_supported(pipe->screen, tmpl.format,
+                     res->target, 0, 0,
+                     PIPE_BIND_RENDER_TARGET))
+            return false;
+      }
+
+      sf = pipe->create_surface(pipe, res, &tmpl);
+      if (!sf)
+         return false;
+
+      union pipe_color_union color;
+      util_format_unpack_rgba(sf->format, color.ui, data, 1);
+      pipe->clear_render_target(pipe, sf, &color, box->x, box->y,
+                              box->width, box->height, false);
+
+      pipe_surface_reference(&sf, NULL);
+   }
+
+   return true;
+}
+
+/* First attempt to clear using HW, fallback to SW if needed.
+ */
+void
+u_default_clear_texture(struct pipe_context *pipe,
+                        struct pipe_resource *tex,
+                        unsigned level,
+                        const struct pipe_box *box,
+                        const void *data)
+{
+   struct pipe_screen *screen = pipe->screen;
+   bool cleared = false;
+   assert(data != NULL);
+
+   bool has_layers = screen->get_param(screen, PIPE_CAP_VS_INSTANCEID) &&
+                     screen->get_param(screen, PIPE_CAP_VS_LAYER_VIEWPORT);
+
+   if (has_layers) {
+      cleared = util_clear_texture_as_surface(pipe, tex, level,
+                                              box, data);
+   } else {
+      struct pipe_box layer = *box;
+      layer.depth = 1;
+      int l;
+      for (l = box->z; l < box->z + box->depth; l++) {
+         layer.z = l;
+         cleared |= util_clear_texture_as_surface(pipe, tex, level,
+                                                  &layer, data);
+         if (!cleared) {
+            /* If one layer is cleared, all layers should also be clearable.
+             * Therefore, if we fail on any later other than the first, it
+             * is a bug somewhere.
+             */
+            assert(l == box->z);
+            break;
+         }
+      }
+   }
+
+   /* Fallback to clearing it in SW if the HW paths failed. */
+   if (!cleared)
+      util_clear_texture(pipe, tex, level, box, data);
+}
+
 void
 util_clear_texture(struct pipe_context *pipe,
                    struct pipe_resource *tex,
index c635239e8464385c90bf1ef49745e0b0676171cd..e2166e370cdfc75a29f9181d159b1cd1a09bf1af 100644 (file)
@@ -82,6 +82,13 @@ util_resource_copy_region(struct pipe_context *pipe,
                           unsigned src_level,
                           const struct pipe_box *src_box);
 
+extern void
+u_default_clear_texture(struct pipe_context *pipe,
+                        struct pipe_resource *tex,
+                        unsigned level,
+                        const struct pipe_box *box,
+                        const void *data);
+
 extern void
 util_clear_texture(struct pipe_context *pipe,
                    struct pipe_resource *tex,
index 2a9b84f6f0035f880bbcbf5a316ac1361b3ad74f..011eeb04df6999cee0c3cc3a2fa7b89c16885a13 100644 (file)
@@ -1233,6 +1233,31 @@ util_format_stencil_only(enum pipe_format format)
    }
 }
 
+static inline enum pipe_format
+util_format_as_renderable(enum pipe_format format)
+{
+   switch (util_format_get_blocksizebits(format)) {
+   case 128:
+      return PIPE_FORMAT_R32G32B32A32_UINT;
+   case 96:
+      return PIPE_FORMAT_R32G32B32_UINT;
+   case 64:
+      return PIPE_FORMAT_R32G32_UINT;
+   case 48:
+      return PIPE_FORMAT_R16G16B16_UINT;
+   case 32:
+      return PIPE_FORMAT_R32_UINT;
+   case 24:
+      return PIPE_FORMAT_R8G8B8_UINT;
+   case 16:
+      return PIPE_FORMAT_R16_UINT;
+   case 8:
+      return PIPE_FORMAT_R8_UINT;
+   default:
+      return PIPE_FORMAT_NONE;
+   }
+}
+
 /**
  * Converts PIPE_FORMAT_*I* to PIPE_FORMAT_*R*.
  * This is identity for non-intensity formats.