Mesa (master): zink: defer pipe_context::clear calls when not currently in a renderpass

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Wed Feb 24 02:08:19 UTC 2021


Module: Mesa
Branch: master
Commit: 5c629e9ff24de6014ffa88034c9435e470c926ac
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=5c629e9ff24de6014ffa88034c9435e470c926ac

Author: Mike Blumenkrantz <michael.blumenkrantz at gmail.com>
Date:   Wed Sep 16 17:47:17 2020 -0400

zink: defer pipe_context::clear calls when not currently in a renderpass

instead, we can attach the clear to the next renderpass start and even add it to
the renderpass cache for reuse

also add handling for flushing clears on map or fb switching to avoid brekaing behavior

this should save us a lot of time with potentially beginning/ending renderpasses as well
as allowing drivers to do better batching of clears by passing in all the buffers at
once

this doesn't handle deferring conditional renders yet in a futile attempt to try and keep
the size of the patch down

Reviewed-by: Dave Airlie <airlied at redhat.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/9206>

---

 src/gallium/drivers/zink/zink_blit.c        |   5 +
 src/gallium/drivers/zink/zink_clear.c       | 275 +++++++++++++++++++++++-----
 src/gallium/drivers/zink/zink_context.c     |  85 ++++++++-
 src/gallium/drivers/zink/zink_context.h     |  53 ++++++
 src/gallium/drivers/zink/zink_render_pass.c |   6 +-
 src/gallium/drivers/zink/zink_render_pass.h |   5 +
 src/gallium/drivers/zink/zink_resource.c    |   1 +
 7 files changed, 380 insertions(+), 50 deletions(-)

diff --git a/src/gallium/drivers/zink/zink_blit.c b/src/gallium/drivers/zink/zink_blit.c
index bef9765220e..6b1f7234ba5 100644
--- a/src/gallium/drivers/zink/zink_blit.c
+++ b/src/gallium/drivers/zink/zink_blit.c
@@ -36,6 +36,9 @@ blit_resolve(struct zink_context *ctx, const struct pipe_blit_info *info)
    if (info->dst.resource->target == PIPE_BUFFER)
       util_range_add(info->dst.resource, &dst->valid_buffer_range,
                      info->dst.box.x, info->dst.box.x + info->dst.box.width);
+
+   zink_fb_clears_apply(ctx, info->dst.resource);
+   zink_fb_clears_apply(ctx, info->src.resource);
    struct zink_batch *batch = zink_batch_no_rp(ctx);
 
    zink_batch_reference_resource_rw(batch, src, false);
@@ -116,6 +119,8 @@ blit_native(struct zink_context *ctx, const struct pipe_blit_info *info)
        dst->format != zink_get_format(screen, info->dst.format))
       return false;
 
+   zink_fb_clears_apply(ctx, info->dst.resource);
+   zink_fb_clears_apply(ctx, info->src.resource);
    struct zink_batch *batch = zink_batch_no_rp(ctx);
    zink_batch_reference_resource_rw(batch, src, false);
    zink_batch_reference_resource_rw(batch, dst, true);
diff --git a/src/gallium/drivers/zink/zink_clear.c b/src/gallium/drivers/zink/zink_clear.c
index 597daa9b6d2..0638b569ff8 100644
--- a/src/gallium/drivers/zink/zink_clear.c
+++ b/src/gallium/drivers/zink/zink_clear.c
@@ -26,6 +26,7 @@
 #include "zink_screen.h"
 
 #include "util/u_blitter.h"
+#include "util/u_dynarray.h"
 #include "util/format/u_format.h"
 #include "util/format_srgb.h"
 #include "util/u_framebuffer.h"
@@ -36,6 +37,8 @@
 static inline bool
 check_3d_layers(struct pipe_surface *psurf)
 {
+   if (psurf->texture->target != PIPE_TEXTURE_3D)
+      return true;
    /* SPEC PROBLEM:
     * though the vk spec doesn't seem to explicitly address this, currently drivers
     * are claiming that all 3D images have a single "3D" layer regardless of layercount,
@@ -49,6 +52,12 @@ check_3d_layers(struct pipe_surface *psurf)
    return true;
 }
 
+static inline bool
+scissor_states_equal(const struct pipe_scissor_state *a, const struct pipe_scissor_state *b)
+{
+   return a->minx == b->minx && a->miny == b->miny && a->maxx == b->maxx && a->maxy == b->maxy;
+}
+
 static void
 clear_in_rp(struct pipe_context *pctx,
            unsigned buffers,
@@ -58,8 +67,6 @@ clear_in_rp(struct pipe_context *pctx,
 {
    struct zink_context *ctx = zink_context(pctx);
    struct pipe_framebuffer_state *fb = &ctx->fb_state;
-   struct zink_resource *resources[PIPE_MAX_COLOR_BUFS + 1] = {};
-   int res_count = 0;
 
    VkClearAttachment attachments[1 + PIPE_MAX_COLOR_BUFS];
    int num_attachments = 0;
@@ -79,9 +86,6 @@ clear_in_rp(struct pipe_context *pctx,
          attachments[num_attachments].colorAttachment = i;
          attachments[num_attachments].clearValue.color = color;
          ++num_attachments;
-         struct zink_resource *res = (struct zink_resource*)fb->cbufs[i]->texture;
-         zink_resource_image_barrier(ctx, NULL, res, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, 0);
-         resources[res_count++] = res;
       }
    }
 
@@ -96,9 +100,6 @@ clear_in_rp(struct pipe_context *pctx,
       attachments[num_attachments].clearValue.depthStencil.depth = depth;
       attachments[num_attachments].clearValue.depthStencil.stencil = stencil;
       ++num_attachments;
-      struct zink_resource *res = (struct zink_resource*)fb->zsbuf->texture;
-      zink_resource_image_barrier(ctx, NULL, res, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, 0, 0);
-      resources[res_count++] = res;
    }
 
    VkClearRect cr = {};
@@ -114,8 +115,6 @@ clear_in_rp(struct pipe_context *pctx,
    cr.baseArrayLayer = 0;
    cr.layerCount = util_framebuffer_get_num_layers(fb);
    struct zink_batch *batch = zink_batch_rp(ctx);
-   for (int i = 0; i < res_count; i++)
-      zink_batch_reference_resource_rw(batch, resources[i], true);
    vkCmdClearAttachments(batch->cmdbuf, num_attachments, attachments, 1, &cr);
 }
 
@@ -168,11 +167,6 @@ clear_needs_rp(unsigned width, unsigned height, struct u_rect *region)
 {
    struct u_rect intersect = {0, width, 0, height};
 
-   /* FIXME: this is very inefficient; if no renderpass has been started yet,
-    * we should record the clear if it's full-screen, and apply it as we
-    * start the render-pass. Otherwise we can do a partial out-of-renderpass
-    * clear.
-    */
    if (!u_rect_test_intersection(region, &intersect))
       /* is this even a thing? */
       return true;
@@ -185,6 +179,25 @@ clear_needs_rp(unsigned width, unsigned height, struct u_rect *region)
    return false;
 }
 
+static struct zink_framebuffer_clear_data *
+get_clear_data(struct zink_context *ctx, struct zink_framebuffer_clear *fb_clear, const struct pipe_scissor_state *scissor_state)
+{
+   struct zink_framebuffer_clear_data *clear = NULL;
+   unsigned num_clears = zink_fb_clear_count(fb_clear);
+   if (num_clears) {
+      struct zink_framebuffer_clear_data *last_clear = zink_fb_clear_element(fb_clear, num_clears - 1);
+      /* if we're completely overwriting the previous clear, merge this into the previous clear */
+      if (!scissor_state || (last_clear->has_scissor && scissor_states_equal(&last_clear->scissor, scissor_state)))
+         clear = last_clear;
+   }
+   if (!clear) {
+      struct zink_framebuffer_clear_data cd = {};
+      util_dynarray_append(&fb_clear->clears, struct zink_framebuffer_clear_data, cd);
+      clear = zink_fb_clear_element(fb_clear, zink_fb_clear_count(fb_clear) - 1);
+   }
+   return clear;
+}
+
 void
 zink_clear(struct pipe_context *pctx,
            unsigned buffers,
@@ -203,7 +216,7 @@ zink_clear(struct pipe_context *pctx,
    }
 
 
-   if (needs_rp || batch->in_rp || ctx->render_condition_active) {
+   if (batch->in_rp || ctx->render_condition_active) {
       clear_in_rp(pctx, buffers, scissor_state, pcolor, depth, stencil);
       return;
    }
@@ -212,43 +225,127 @@ zink_clear(struct pipe_context *pctx,
       for (unsigned i = 0; i < fb->nr_cbufs; i++) {
          if ((buffers & (PIPE_CLEAR_COLOR0 << i)) && fb->cbufs[i]) {
             struct pipe_surface *psurf = fb->cbufs[i];
-
-            if (psurf->texture->target == PIPE_TEXTURE_3D && !check_3d_layers(psurf)) {
-               clear_in_rp(pctx, buffers, scissor_state, pcolor, depth, stencil);
-               return;
-            }
-            struct zink_resource *res = zink_resource(psurf->texture);
-            union pipe_color_union color = *pcolor;
-            if (psurf->format != res->base.format &&
-                !util_format_is_srgb(psurf->format) && util_format_is_srgb(res->base.format)) {
-               /* if SRGB mode is disabled for the fb with a backing srgb image then we have to
-                * convert this to srgb color
-                */
-               color.f[0] = util_format_srgb_to_linear_float(pcolor->f[0]);
-               color.f[1] = util_format_srgb_to_linear_float(pcolor->f[1]);
-               color.f[2] = util_format_srgb_to_linear_float(pcolor->f[2]);
-            }
-            clear_color_no_rp(ctx, zink_resource(fb->cbufs[i]->texture), &color,
-                              psurf->u.tex.level, psurf->u.tex.first_layer,
-                              psurf->u.tex.last_layer - psurf->u.tex.first_layer + 1);
+            struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[i];
+            struct zink_framebuffer_clear_data *clear = get_clear_data(ctx, fb_clear, needs_rp ? scissor_state : NULL);
+
+            fb_clear->enabled = true;
+            clear->has_scissor = needs_rp;
+            if (scissor_state && needs_rp)
+               clear->scissor = *scissor_state;
+            clear->color.color = *pcolor;
+            clear->color.srgb = psurf->format != psurf->texture->format &&
+                                !util_format_is_srgb(psurf->format) && util_format_is_srgb(psurf->texture->format);
          }
       }
    }
 
    if (buffers & PIPE_CLEAR_DEPTHSTENCIL && fb->zsbuf) {
-      if (fb->zsbuf->texture->target == PIPE_TEXTURE_3D && !check_3d_layers(fb->zsbuf)) {
-         clear_in_rp(pctx, buffers, scissor_state, pcolor, depth, stencil);
-         return;
-      }
-      VkImageAspectFlags aspects = 0;
+      struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[PIPE_MAX_COLOR_BUFS];
+      struct zink_framebuffer_clear_data *clear = get_clear_data(ctx, fb_clear, needs_rp ? scissor_state : NULL);
+      fb_clear->enabled = true;
+      clear->has_scissor = needs_rp;
+      if (scissor_state && needs_rp)
+         clear->scissor = *scissor_state;
       if (buffers & PIPE_CLEAR_DEPTH)
-         aspects |= VK_IMAGE_ASPECT_DEPTH_BIT;
+         clear->zs.depth = depth;
       if (buffers & PIPE_CLEAR_STENCIL)
-         aspects |= VK_IMAGE_ASPECT_STENCIL_BIT;
-      clear_zs_no_rp(ctx, zink_resource(fb->zsbuf->texture), aspects,
-                     depth, stencil, fb->zsbuf->u.tex.level, fb->zsbuf->u.tex.first_layer,
-                     fb->zsbuf->u.tex.last_layer - fb->zsbuf->u.tex.first_layer + 1);
+         clear->zs.stencil = stencil;
+      clear->zs.bits |= (buffers & PIPE_CLEAR_DEPTHSTENCIL);
+   }
+}
+
+static inline bool
+colors_equal(union pipe_color_union *a, union pipe_color_union *b)
+{
+   return a->ui[0] == b->ui[0] && a->ui[1] == b->ui[1] && a->ui[2] == b->ui[2] && a->ui[3] == b->ui[3];
+}
+
+void
+zink_clear_framebuffer(struct zink_context *ctx, unsigned clear_buffers)
+{
+   unsigned to_clear = 0;
+   struct pipe_framebuffer_state *fb_state = &ctx->fb_state;
+   while (clear_buffers) {
+      struct zink_framebuffer_clear *color_clear = NULL;
+      struct zink_framebuffer_clear *zs_clear = NULL;
+      unsigned num_clears = 0;
+      for (int i = 0; i < fb_state->nr_cbufs && clear_buffers >= PIPE_CLEAR_COLOR0; i++) {
+         struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[i];
+         /* these need actual clear calls inside the rp */
+         if (!(clear_buffers & (PIPE_CLEAR_COLOR0 << i)))
+            continue;
+         if (color_clear) {
+            /* different number of clears -> do another clear */
+            //XXX: could potentially merge "some" of the clears into this one for a very, very small optimization
+            if (num_clears != zink_fb_clear_count(fb_clear))
+               goto out;
+            /* compare all the clears to determine if we can batch these buffers together */
+            for (int j = 0; j < num_clears; j++) {
+               struct zink_framebuffer_clear_data *a = zink_fb_clear_element(color_clear, j);
+               struct zink_framebuffer_clear_data *b = zink_fb_clear_element(fb_clear, j);
+               /* scissors don't match, fire this one off */
+               if (a->has_scissor != b->has_scissor || (a->has_scissor && !scissor_states_equal(&a->scissor, &b->scissor)))
+                  goto out;
+
+               /* colors don't match, fire this one off */
+               if (!colors_equal(&a->color.color, &b->color.color))
+                  goto out;
+            }
+         } else {
+            color_clear = fb_clear;
+            num_clears = zink_fb_clear_count(fb_clear);
+         }
+
+         clear_buffers &= ~(PIPE_CLEAR_COLOR0 << i);
+         to_clear |= (PIPE_CLEAR_COLOR0 << i);
+      }
+      if (clear_buffers & PIPE_CLEAR_DEPTHSTENCIL) {
+         struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[PIPE_MAX_COLOR_BUFS];
+         if (color_clear) {
+            if (num_clears != zink_fb_clear_count(fb_clear))
+               goto out;
+            /* compare all the clears to determine if we can batch these buffers together */
+            for (int j = 0; j < zink_fb_clear_count(color_clear); j++) {
+               struct zink_framebuffer_clear_data *a = zink_fb_clear_element(color_clear, j);
+               struct zink_framebuffer_clear_data *b = zink_fb_clear_element(fb_clear, j);
+               /* scissors don't match, fire this one off */
+               if (a->has_scissor != b->has_scissor || (a->has_scissor && !scissor_states_equal(&a->scissor, &b->scissor)))
+                  goto out;
+            }
+         }
+         zs_clear = fb_clear;
+         to_clear |= (clear_buffers & PIPE_CLEAR_DEPTHSTENCIL);
+         clear_buffers &= ~PIPE_CLEAR_DEPTHSTENCIL;
+      }
+out:
+      if (to_clear) {
+         if (num_clears) {
+            for (int j = 0; j < num_clears; j++) {
+               struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(color_clear, j);
+               struct zink_framebuffer_clear_data *zsclear = NULL;
+               if (zs_clear)
+                  zsclear = zink_fb_clear_element(zs_clear, j);
+               zink_clear(&ctx->base, to_clear,
+                          clear->has_scissor ? &clear->scissor : NULL,
+                          &clear->color.color,
+                          zsclear ? zsclear->zs.depth : 0,
+                          zsclear ? zsclear->zs.stencil : 0);
+            }
+         } else {
+            for (int j = 0; j < zink_fb_clear_count(zs_clear); j++) {
+               struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(zs_clear, j);
+               zink_clear(&ctx->base, to_clear,
+                          clear->has_scissor ? &clear->scissor : NULL,
+                          NULL,
+                          clear->zs.depth,
+                          clear->zs.stencil);
+            }
+         }
+      }
+      to_clear = 0;
    }
+   for (int i = 0; i < ARRAY_SIZE(ctx->fb_clears); i++)
+       zink_fb_clear_reset(&ctx->fb_clears[i]);
 }
 
 static struct pipe_surface *
@@ -318,3 +415,91 @@ zink_clear_texture(struct pipe_context *pctx,
    }
    pipe_surface_reference(&surf, NULL);
 }
+
+bool
+zink_fb_clear_needs_explicit(struct zink_framebuffer_clear *fb_clear)
+{
+   if (zink_fb_clear_count(fb_clear) != 1)
+      return true;
+   struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(fb_clear, 0);
+   return clear->has_scissor;
+}
+
+void
+zink_fb_clears_apply(struct zink_context *ctx, struct pipe_resource *pres)
+{
+   if (zink_resource(pres)->aspect == VK_IMAGE_ASPECT_COLOR_BIT) {
+      for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
+         if (ctx->fb_state.cbufs[i] && ctx->fb_state.cbufs[i]->texture == pres) {
+            struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[i];
+            if (fb_clear->enabled) {
+               assert(!zink_curr_batch(ctx)->in_rp);
+               if (zink_fb_clear_needs_explicit(fb_clear) || !check_3d_layers(ctx->fb_state.cbufs[i]))
+                  /* this will automatically trigger all the clears */
+                  zink_batch_rp(ctx);
+               else {
+                  struct pipe_surface *psurf = ctx->fb_state.cbufs[i];
+                  struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(fb_clear, 0);
+                  union pipe_color_union color = clear->color.color;
+                  if (clear->color.srgb) {
+                     /* if SRGB mode is disabled for the fb with a backing srgb image then we have to
+                      * convert this to srgb color
+                      */
+                     color.f[0] = util_format_srgb_to_linear_float(clear->color.color.f[0]);
+                     color.f[1] = util_format_srgb_to_linear_float(clear->color.color.f[1]);
+                     color.f[2] = util_format_srgb_to_linear_float(clear->color.color.f[2]);
+                  }
+
+                  clear_color_no_rp(ctx, zink_resource(pres), &color,
+                                         psurf->u.tex.level, psurf->u.tex.first_layer,
+                                         psurf->u.tex.last_layer - psurf->u.tex.first_layer + 1);
+               }
+               zink_fb_clear_reset(&ctx->fb_clears[i]);
+            }
+            return;
+         }
+      }
+   } else {
+      struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[PIPE_MAX_COLOR_BUFS];
+      if (fb_clear->enabled && ctx->fb_state.zsbuf && ctx->fb_state.zsbuf->texture == pres) {
+          assert(!zink_curr_batch(ctx)->in_rp);
+          if (zink_fb_clear_needs_explicit(fb_clear) || !check_3d_layers(ctx->fb_state.zsbuf))
+             /* this will automatically trigger all the clears */
+             zink_batch_rp(ctx);
+          else {
+            struct pipe_surface *psurf = ctx->fb_state.zsbuf;
+            struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(fb_clear, 0);
+            VkImageAspectFlags aspects = 0;
+            if (clear->zs.bits & PIPE_CLEAR_DEPTH)
+               aspects |= VK_IMAGE_ASPECT_DEPTH_BIT;
+            if (clear->zs.bits & PIPE_CLEAR_STENCIL)
+               aspects |= VK_IMAGE_ASPECT_STENCIL_BIT;
+            clear_zs_no_rp(ctx, zink_resource(pres), aspects, clear->zs.depth, clear->zs.stencil,
+                                psurf->u.tex.level, psurf->u.tex.first_layer,
+                                psurf->u.tex.last_layer - psurf->u.tex.first_layer + 1);
+            zink_fb_clear_reset(fb_clear);
+         }
+      }
+   }
+}
+
+void
+zink_fb_clears_discard(struct zink_context *ctx, struct pipe_resource *pres)
+{
+   if (zink_resource(pres)->aspect == VK_IMAGE_ASPECT_COLOR_BIT) {
+      for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
+         if (ctx->fb_state.cbufs[i] && ctx->fb_state.cbufs[i]->texture == pres) {
+            if (ctx->fb_clears[i].enabled) {
+               zink_fb_clear_reset(&ctx->fb_clears[i]);
+               return;
+            }
+         }
+      }
+   } else {
+      if (ctx->fb_clears[PIPE_MAX_COLOR_BUFS].enabled && ctx->fb_state.zsbuf && ctx->fb_state.zsbuf->texture == pres) {
+         int i = PIPE_MAX_COLOR_BUFS;
+         zink_fb_clear_reset(&ctx->fb_clears[i]);
+      }
+   }
+}
+
diff --git a/src/gallium/drivers/zink/zink_context.c b/src/gallium/drivers/zink/zink_context.c
index 8d525f8f7dd..a0df27d7d5f 100644
--- a/src/gallium/drivers/zink/zink_context.c
+++ b/src/gallium/drivers/zink/zink_context.c
@@ -40,6 +40,7 @@
 #include "indices/u_primconvert.h"
 #include "util/u_blitter.h"
 #include "util/u_debug.h"
+#include "util/format_srgb.h"
 #include "util/format/u_format.h"
 #include "util/u_framebuffer.h"
 #include "util/u_helpers.h"
@@ -731,6 +732,7 @@ get_render_pass(struct zink_context *ctx)
    struct zink_screen *screen = zink_screen(ctx->base.screen);
    const struct pipe_framebuffer_state *fb = &ctx->fb_state;
    struct zink_render_pass_state state = { 0 };
+   uint32_t clears = 0;
 
    for (int i = 0; i < fb->nr_cbufs; i++) {
       struct pipe_surface *surf = fb->cbufs[i];
@@ -738,6 +740,8 @@ get_render_pass(struct zink_context *ctx)
          state.rts[i].format = zink_get_format(screen, surf->format);
          state.rts[i].samples = surf->texture->nr_samples > 0 ? surf->texture->nr_samples :
                                                        VK_SAMPLE_COUNT_1_BIT;
+         state.rts[i].clear_color = ctx->fb_clears[i].enabled && !zink_fb_clear_needs_explicit(&ctx->fb_clears[i]);
+         clears |= !!state.rts[i].clear_color ? BITFIELD_BIT(i) : 0;
       } else {
          state.rts[i].format = VK_FORMAT_R8_UINT;
          state.rts[i].samples = MAX2(fb->samples, 1);
@@ -748,11 +752,22 @@ get_render_pass(struct zink_context *ctx)
 
    if (fb->zsbuf) {
       struct zink_resource *zsbuf = zink_resource(fb->zsbuf->texture);
+      struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[PIPE_MAX_COLOR_BUFS];
       state.rts[fb->nr_cbufs].format = zsbuf->format;
       state.rts[fb->nr_cbufs].samples = zsbuf->base.nr_samples > 0 ? zsbuf->base.nr_samples : VK_SAMPLE_COUNT_1_BIT;
+      state.rts[fb->nr_cbufs].clear_color = fb_clear->enabled &&
+                                            !zink_fb_clear_needs_explicit(fb_clear) &&
+                                            (zink_fb_clear_element(fb_clear, 0)->zs.bits & PIPE_CLEAR_DEPTH);
+      state.rts[fb->nr_cbufs].clear_stencil = fb_clear->enabled &&
+                                              !zink_fb_clear_needs_explicit(fb_clear) &&
+                                              (zink_fb_clear_element(fb_clear, 0)->zs.bits & PIPE_CLEAR_STENCIL);
+      clears |= state.rts[fb->nr_cbufs].clear_color || state.rts[fb->nr_cbufs].clear_stencil ? BITFIELD_BIT(fb->nr_cbufs) : 0;;
       state.num_rts++;
    }
    state.have_zsbuf = fb->zsbuf != NULL;
+#ifndef NDEBUG
+   state.clears = clears;
+#endif
 
    uint32_t hash = hash_render_pass_state(&state);
    struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(ctx->render_pass_cache, hash,
@@ -845,8 +860,51 @@ zink_begin_render_pass(struct zink_context *ctx, struct zink_batch *batch)
    rpbi.renderArea.offset.y = 0;
    rpbi.renderArea.extent.width = fb_state->width;
    rpbi.renderArea.extent.height = fb_state->height;
-   rpbi.clearValueCount = 0;
-   rpbi.pClearValues = NULL;
+
+   VkClearValue clears[PIPE_MAX_COLOR_BUFS + 1] = {};
+   unsigned clear_buffers = 0;
+   uint32_t clear_validate = 0;
+   for (int i = 0; i < fb_state->nr_cbufs; i++) {
+      /* these are no-ops */
+      if (!fb_state->cbufs[i] || !ctx->fb_clears[i].enabled)
+         continue;
+      /* these need actual clear calls inside the rp */
+      if (zink_fb_clear_needs_explicit(&ctx->fb_clears[i])) {
+         clear_buffers |= (PIPE_CLEAR_COLOR0 << i);
+         continue;
+      }
+      /* we now know there's only one clear */
+      struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(&ctx->fb_clears[i], 0);
+      if (clear->color.srgb) {
+         clears[i].color.float32[0] = util_format_srgb_to_linear_float(clear->color.color.f[0]);
+         clears[i].color.float32[1] = util_format_srgb_to_linear_float(clear->color.color.f[1]);
+         clears[i].color.float32[2] = util_format_srgb_to_linear_float(clear->color.color.f[2]);
+      } else {
+         clears[i].color.float32[0] = clear->color.color.f[0];
+         clears[i].color.float32[1] = clear->color.color.f[1];
+         clears[i].color.float32[2] = clear->color.color.f[2];
+      }
+      clears[i].color.float32[3] = clear->color.color.f[3];
+      rpbi.clearValueCount = i + 1;
+      clear_validate |= BITFIELD_BIT(i);
+      assert(ctx->framebuffer->rp->state.clears);
+   }
+   if (fb_state->zsbuf && ctx->fb_clears[PIPE_MAX_COLOR_BUFS].enabled) {
+      struct zink_framebuffer_clear *fb_clear = &ctx->fb_clears[PIPE_MAX_COLOR_BUFS];
+      if (zink_fb_clear_needs_explicit(fb_clear)) {
+         for (int j = 0; j < zink_fb_clear_count(fb_clear); j++)
+           clear_buffers |= zink_fb_clear_element(fb_clear, j)->zs.bits;
+      } else {
+         struct zink_framebuffer_clear_data *clear = zink_fb_clear_element(fb_clear, 0);
+         clears[fb_state->nr_cbufs].depthStencil.depth = clear->zs.depth;
+         clears[fb_state->nr_cbufs].depthStencil.stencil = clear->zs.stencil;
+         rpbi.clearValueCount = fb_state->nr_cbufs + 1;
+         clear_validate |= BITFIELD_BIT(fb_state->nr_cbufs);
+         assert(ctx->framebuffer->rp->state.clears);
+      }
+   }
+   assert(clear_validate == ctx->framebuffer->rp->state.clears);
+   rpbi.pClearValues = &clears[0];
    rpbi.framebuffer = ctx->framebuffer->fb;
 
    assert(ctx->gfx_pipeline_state.render_pass && ctx->framebuffer);
@@ -859,6 +917,8 @@ zink_begin_render_pass(struct zink_context *ctx, struct zink_batch *batch)
 
    vkCmdBeginRenderPass(batch->cmdbuf, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
    batch->in_rp = true;
+
+   zink_clear_framebuffer(ctx, clear_buffers);
 }
 
 static void
@@ -916,6 +976,22 @@ zink_set_framebuffer_state(struct pipe_context *pctx,
 {
    struct zink_context *ctx = zink_context(pctx);
 
+   for (int i = 0; i < ctx->fb_state.nr_cbufs; i++) {
+      struct pipe_surface *surf = ctx->fb_state.cbufs[i];
+      if (surf &&
+          (!state->cbufs[i] || i >= state->nr_cbufs ||
+           surf->texture != state->cbufs[i]->texture ||
+           surf->format != state->cbufs[i]->format ||
+           memcmp(&surf->u, &state->cbufs[i]->u, sizeof(union pipe_surface_desc))))
+         zink_fb_clears_apply(ctx, surf->texture);
+   }
+   if (ctx->fb_state.zsbuf) {
+      struct pipe_surface *surf = ctx->fb_state.zsbuf;
+      if (!state->zsbuf || surf->texture != state->zsbuf->texture ||
+          memcmp(&surf->u, &state->zsbuf->u, sizeof(union pipe_surface_desc)))
+      zink_fb_clears_apply(ctx, ctx->fb_state.zsbuf->texture);
+   }
+
    util_copy_framebuffer_state(&ctx->fb_state, state);
 
    uint8_t rast_samples = util_framebuffer_get_num_samples(state);
@@ -1515,6 +1591,9 @@ zink_resource_copy_region(struct pipe_context *pctx,
       } else
          unreachable("planar formats not yet handled");
 
+      zink_fb_clears_apply(ctx, pdst);
+      zink_fb_clears_apply(ctx, psrc);
+
       region.srcSubresource.aspectMask = src->aspect;
       region.srcSubresource.mipLevel = src_level;
       region.srcSubresource.layerCount = 1;
@@ -1767,6 +1846,8 @@ zink_context_create(struct pipe_screen *pscreen, void *priv, unsigned flags)
 
    ctx->base.stream_uploader = u_upload_create_default(&ctx->base);
    ctx->base.const_uploader = ctx->base.stream_uploader;
+   for (int i = 0; i < ARRAY_SIZE(ctx->fb_clears); i++)
+      util_dynarray_init(&ctx->fb_clears[i].clears, ctx);
 
    int prim_hwsupport = 1 << PIPE_PRIM_POINTS |
                         1 << PIPE_PRIM_LINES |
diff --git a/src/gallium/drivers/zink/zink_context.h b/src/gallium/drivers/zink/zink_context.h
index 30e7df16586..b0c5b6df29b 100644
--- a/src/gallium/drivers/zink/zink_context.h
+++ b/src/gallium/drivers/zink/zink_context.h
@@ -96,6 +96,27 @@ struct zink_viewport_state {
    uint8_t num_viewports;
 };
 
+struct zink_framebuffer_clear_data {
+   union {
+      struct {
+         union pipe_color_union color;
+         bool srgb;
+      } color;
+      struct {
+         float depth;
+         unsigned stencil;
+         uint8_t bits : 2; // PIPE_CLEAR_DEPTH, PIPE_CLEAR_STENCIL
+      } zs;
+   };
+   struct pipe_scissor_state scissor;
+   bool has_scissor;
+};
+
+struct zink_framebuffer_clear {
+   struct util_dynarray clears;
+   bool enabled;
+};
+
 #define ZINK_SHADER_COUNT (PIPE_SHADER_TYPES - 1)
 #define ZINK_NUM_GFX_BATCHES 4
 #define ZINK_COMPUTE_BATCH_ID ZINK_NUM_GFX_BATCHES
@@ -145,6 +166,7 @@ struct zink_context {
    struct primconvert_context *primconvert;
 
    struct zink_framebuffer *framebuffer;
+   struct zink_framebuffer_clear fb_clears[PIPE_MAX_COLOR_BUFS + 1];
 
    struct pipe_vertex_buffer buffers[PIPE_MAX_ATTRIBS];
    uint32_t buffers_enabled_mask;
@@ -270,6 +292,37 @@ zink_clear_texture(struct pipe_context *ctx,
                    const struct pipe_box *box,
                    const void *data);
 
+bool
+zink_fb_clear_needs_explicit(struct zink_framebuffer_clear *fb_clear);
+
+void
+zink_clear_framebuffer(struct zink_context *ctx, unsigned clear_buffers);
+
+static inline struct zink_framebuffer_clear_data *
+zink_fb_clear_element(struct zink_framebuffer_clear *fb_clear, int idx)
+{
+   return util_dynarray_element(&fb_clear->clears, struct zink_framebuffer_clear_data, idx);
+}
+
+static inline unsigned
+zink_fb_clear_count(struct zink_framebuffer_clear *fb_clear)
+{
+   return util_dynarray_num_elements(&fb_clear->clears, struct zink_framebuffer_clear_data);
+}
+
+static inline void
+zink_fb_clear_reset(struct zink_framebuffer_clear *fb_clear)
+{
+   util_dynarray_fini(&fb_clear->clears);
+   fb_clear->enabled = false;
+}
+
+void
+zink_fb_clears_apply(struct zink_context *ctx, struct pipe_resource *pres);
+
+void
+zink_fb_clears_discard(struct zink_context *ctx, struct pipe_resource *pres);
+
 void
 zink_draw_vbo(struct pipe_context *pctx,
               const struct pipe_draw_info *dinfo,
diff --git a/src/gallium/drivers/zink/zink_render_pass.c b/src/gallium/drivers/zink/zink_render_pass.c
index b1eb9015bd8..80565d43d1e 100644
--- a/src/gallium/drivers/zink/zink_render_pass.c
+++ b/src/gallium/drivers/zink/zink_render_pass.c
@@ -40,7 +40,7 @@ create_render_pass(VkDevice dev, struct zink_render_pass_state *state)
       attachments[i].flags = 0;
       attachments[i].format = rt->format;
       attachments[i].samples = rt->samples;
-      attachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+      attachments[i].loadOp = rt->clear_color ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
       attachments[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
       attachments[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
       attachments[i].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
@@ -56,9 +56,9 @@ create_render_pass(VkDevice dev, struct zink_render_pass_state *state)
       attachments[num_attachments].flags = 0;
       attachments[num_attachments].format = rt->format;
       attachments[num_attachments].samples = rt->samples;
-      attachments[num_attachments].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+      attachments[num_attachments].loadOp = rt->clear_color ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
       attachments[num_attachments].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
-      attachments[num_attachments].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
+      attachments[num_attachments].stencilLoadOp = rt->clear_stencil ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD;
       attachments[num_attachments].stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
       attachments[num_attachments].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
       attachments[num_attachments].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
diff --git a/src/gallium/drivers/zink/zink_render_pass.h b/src/gallium/drivers/zink/zink_render_pass.h
index 577b92c9065..02dc2119439 100644
--- a/src/gallium/drivers/zink/zink_render_pass.h
+++ b/src/gallium/drivers/zink/zink_render_pass.h
@@ -34,6 +34,8 @@ struct zink_screen;
 struct zink_rt_attrib {
   VkFormat format;
   VkSampleCountFlagBits samples;
+  bool clear_color;
+  bool clear_stencil;
 };
 
 struct zink_render_pass_state {
@@ -41,6 +43,9 @@ struct zink_render_pass_state {
    uint8_t have_zsbuf : 1;
    struct zink_rt_attrib rts[PIPE_MAX_COLOR_BUFS + 1];
    unsigned num_rts;
+#ifndef NDEBUG
+   uint32_t clears; //for extra verification
+#endif
 };
 
 struct zink_render_pass {
diff --git a/src/gallium/drivers/zink/zink_resource.c b/src/gallium/drivers/zink/zink_resource.c
index 7930a281518..2ec1f84a9b1 100644
--- a/src/gallium/drivers/zink/zink_resource.c
+++ b/src/gallium/drivers/zink/zink_resource.c
@@ -619,6 +619,7 @@ zink_transfer_map(struct pipe_context *pctx,
       if (usage & PIPE_MAP_WRITE)
          util_range_add(&res->base, &res->valid_buffer_range, box->x, box->x + box->width);
    } else {
+      zink_fb_clears_apply(ctx, pres);
       if (res->optimal_tiling || !res->host_visible) {
          enum pipe_format format = pres->format;
          if (usage & PIPE_MAP_DEPTH_ONLY)



More information about the mesa-commit mailing list