Mesa (master): zink: create separate vk image/buffer objects for shader image use

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Mon Mar 29 02:27:05 UTC 2021


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

Author: Mike Blumenkrantz <michael.blumenkrantz at gmail.com>
Date:   Sat Dec 12 01:45:29 2020 -0500

zink: create separate vk image/buffer objects for shader image use

the STORAGE_TEXEL and STORAGE_IMAGE bits can't be accurately applied due
to opengl allowing all resources to be used everywhere, so instead we can
create a separate object on demand which is used only by shaders and gets
extra barriers inferred along with the base object to avoid desync whenever
it is used

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

---

 src/gallium/drivers/zink/zink_context.c  | 53 ++++++++++++++++++
 src/gallium/drivers/zink/zink_context.h  |  3 +
 src/gallium/drivers/zink/zink_resource.c | 94 ++++++++++++++++++++++++++++----
 src/gallium/drivers/zink/zink_resource.h |  6 ++
 src/gallium/drivers/zink/zink_surface.c  | 45 +++++++++++++++
 src/gallium/drivers/zink/zink_surface.h  |  5 ++
 6 files changed, 196 insertions(+), 10 deletions(-)

diff --git a/src/gallium/drivers/zink/zink_context.c b/src/gallium/drivers/zink/zink_context.c
index 2fbe9f65ed0..7d3fba19ae1 100644
--- a/src/gallium/drivers/zink/zink_context.c
+++ b/src/gallium/drivers/zink/zink_context.c
@@ -984,6 +984,10 @@ zink_set_shader_images(struct pipe_context *pctx,
       if (images && images[i].resource) {
          util_dynarray_init(&image_view->desc_set_refs.refs, NULL);
          struct zink_resource *res = zink_resource(images[i].resource);
+         if (!zink_resource_object_init_storage(ctx, res)) {
+            debug_printf("couldn't create storage image!");
+            continue;
+         }
          res->bind_history |= BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_IMAGE);
          res->bind_stages |= 1 << p_stage;
          util_copy_image_view(&image_view->base, images + i);
@@ -1055,6 +1059,12 @@ zink_set_sampler_views(struct pipe_context *pctx,
                sampler_view_buffer_clear(ctx, b);
                b->buffer_view = buffer_view;
             }
+         } else if (!res->obj->is_buffer) {
+             if (res->obj != b->image_view->obj) {
+                struct pipe_surface *psurf = &b->image_view->base;
+                zink_rebind_surface(ctx, &psurf);
+                b->image_view = zink_surface(psurf);
+             }
          }
          res->bind_history |= BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW);
          res->bind_stages |= 1 << shader_type;
@@ -1375,6 +1385,27 @@ zink_flush_queue(struct zink_context *ctx)
    flush_batch(ctx);
 }
 
+static bool
+rebind_fb_surface(struct zink_context *ctx, struct pipe_surface **surf, struct zink_resource *match_res)
+{
+   if (!*surf)
+      return false;
+   struct zink_resource *surf_res = zink_resource((*surf)->texture);
+   if ((match_res == surf_res) || surf_res->obj != zink_surface(*surf)->obj)
+      return zink_rebind_surface(ctx, surf);
+   return false;
+}
+
+static bool
+rebind_fb_state(struct zink_context *ctx, struct zink_resource *match_res)
+{
+   bool rebind = false;
+   for (int i = 0; i < ctx->fb_state.nr_cbufs; i++)
+      rebind |= rebind_fb_surface(ctx, &ctx->fb_state.cbufs[i], match_res);
+   rebind |= rebind_fb_surface(ctx, &ctx->fb_state.zsbuf, match_res);
+   return rebind;
+}
+
 static void
 zink_set_framebuffer_state(struct pipe_context *pctx,
                            const struct pipe_framebuffer_state *state)
@@ -1398,6 +1429,7 @@ zink_set_framebuffer_state(struct pipe_context *pctx,
    }
 
    util_copy_framebuffer_state(&ctx->fb_state, state);
+   rebind_fb_state(ctx, NULL);
    /* get_framebuffer adds a ref if the fb is reused or created;
     * always do get_framebuffer first to avoid deleting the same fb
     * we're about to use
@@ -1632,6 +1664,7 @@ zink_resource_image_barrier(struct zink_context *ctx, struct zink_batch *batch,
    /* only barrier if we're changing layout or doing something besides read -> read */
    batch = zink_batch_no_rp(ctx);
    assert(!batch->in_rp);
+   assert(new_layout);
    vkCmdPipelineBarrier(
       batch->state->cmdbuf,
       res->access_stage ? res->access_stage : VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
@@ -2326,6 +2359,22 @@ zink_set_stream_output_targets(struct pipe_context *pctx,
    }
 }
 
+void
+zink_rebind_framebuffer(struct zink_context *ctx, struct zink_resource *res)
+{
+   if (!ctx->framebuffer)
+      return;
+   for (unsigned i = 0; i < ctx->framebuffer->state.num_attachments; i++) {
+      if (!ctx->framebuffer->surfaces[i] ||
+          zink_resource(ctx->framebuffer->surfaces[i]->texture) != res)
+         continue;
+      zink_rebind_surface(ctx, &ctx->framebuffer->surfaces[i]);
+      zink_batch_no_rp(ctx);
+   }
+   if (rebind_fb_state(ctx, res))
+      zink_batch_no_rp(ctx);
+}
+
 void
 zink_resource_rebind(struct zink_context *ctx, struct zink_resource *res)
 {
@@ -2368,6 +2417,10 @@ zink_resource_rebind(struct zink_context *ctx, struct zink_resource *res)
                struct zink_image_view *image_view = &ctx->image_views[shader][i];
                zink_descriptor_set_refs_clear(&image_view->desc_set_refs, image_view);
                zink_buffer_view_reference(zink_screen(ctx->base.screen), &image_view->buffer_view, NULL);
+               if (!zink_resource_object_init_storage(ctx, res)) {
+                  debug_printf("couldn't create storage image!");
+                  continue;
+               }
                image_view->buffer_view = get_buffer_view(ctx, res, image_view->base.format,
                                                          image_view->base.u.buf.offset, image_view->base.u.buf.size);
                assert(image_view->buffer_view);
diff --git a/src/gallium/drivers/zink/zink_context.h b/src/gallium/drivers/zink/zink_context.h
index 81912b4263d..e2030a0aa33 100644
--- a/src/gallium/drivers/zink/zink_context.h
+++ b/src/gallium/drivers/zink/zink_context.h
@@ -323,6 +323,9 @@ zink_rect_from_box(const struct pipe_box *box)
 void
 zink_resource_rebind(struct zink_context *ctx, struct zink_resource *res);
 
+void
+zink_rebind_framebuffer(struct zink_context *ctx, struct zink_resource *res);
+
 void
 zink_draw_vbo(struct pipe_context *pctx,
               const struct pipe_draw_info *dinfo,
diff --git a/src/gallium/drivers/zink/zink_resource.c b/src/gallium/drivers/zink/zink_resource.c
index decab3bbe80..a2119813dd5 100644
--- a/src/gallium/drivers/zink/zink_resource.c
+++ b/src/gallium/drivers/zink/zink_resource.c
@@ -123,10 +123,13 @@ void
 zink_destroy_resource_object(struct zink_screen *screen, struct zink_resource_object *obj)
 {
    assert(!obj->map_count);
-   if (obj->is_buffer)
+   if (obj->is_buffer) {
+      if (obj->sbuffer)
+         vkDestroyBuffer(screen->dev, obj->sbuffer, NULL);
       vkDestroyBuffer(screen->dev, obj->buffer, NULL);
-   else
+   } else {
       vkDestroyImage(screen->dev, obj->image, NULL);
+   }
 
    zink_descriptor_set_refs_clear(&obj->desc_set_refs, obj);
    cache_or_free_mem(screen, obj);
@@ -201,9 +204,6 @@ create_bci(struct zink_screen *screen, const struct pipe_resource *templ, unsign
                    VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
                    VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
                    VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
-      VkFormatProperties props = screen->format_props[templ->format];
-      if (props.bufferFeatures & VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)
-         bci.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
    }
 
    if (bind & PIPE_BIND_VERTEX_BUFFER)
@@ -222,6 +222,9 @@ create_bci(struct zink_screen *screen, const struct pipe_resource *templ, unsign
    if (bind & PIPE_BIND_SHADER_BUFFER)
       bci.usage |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
 
+   if (bind & PIPE_BIND_SHADER_IMAGE)
+      bci.usage |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+
    if (bind & PIPE_BIND_COMMAND_ARGS_BUFFER)
       bci.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
 
@@ -294,12 +297,8 @@ create_ici(struct zink_screen *screen, const struct pipe_resource *templ, unsign
                VK_IMAGE_USAGE_SAMPLED_BIT;
 
    if ((templ->nr_samples <= 1 || screen->info.feats.features.shaderStorageImageMultisample) &&
-       (bind & PIPE_BIND_SHADER_IMAGE ||
-       (bind & PIPE_BIND_SAMPLER_VIEW && templ->flags & PIPE_RESOURCE_FLAG_TEXTURING_MORE_LIKELY))) {
+       (bind & PIPE_BIND_SHADER_IMAGE)) {
       VkFormatProperties props = screen->format_props[templ->format];
-      /* gallium doesn't provide any way to actually know whether this will be used as a shader image,
-       * so we have to just assume and set the bit if it's available
-       */
       if ((ici.tiling == VK_IMAGE_TILING_LINEAR && props.linearTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) ||
           (ici.tiling == VK_IMAGE_TILING_OPTIMAL && props.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT))
          ici.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
@@ -929,6 +928,81 @@ zink_resource_get_separate_stencil(struct pipe_resource *pres)
 
 }
 
+bool
+zink_resource_object_init_storage(struct zink_context *ctx, struct zink_resource *res)
+{
+   struct zink_screen *screen = zink_screen(ctx->base.screen);
+   /* base resource already has the cap */
+   if (res->base.bind & PIPE_BIND_SHADER_IMAGE)
+      return true;
+   if (res->obj->is_buffer) {
+      if (res->obj->sbuffer)
+         return true;
+      VkBufferCreateInfo bci = create_bci(screen, &res->base, res->base.bind | PIPE_BIND_SHADER_IMAGE);
+      VkBuffer buffer;
+      if (vkCreateBuffer(screen->dev, &bci, NULL, &buffer) != VK_SUCCESS)
+         return false;
+      vkBindBufferMemory(screen->dev, buffer, res->obj->mem, res->obj->offset);
+      res->obj->sbuffer = res->obj->buffer;
+      res->obj->buffer = buffer;
+   } else {
+      zink_fb_clears_apply_region(ctx, &res->base, (struct u_rect){0, res->base.width0, 0, res->base.height0});
+      zink_resource_image_barrier(ctx, NULL, res, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, 0, 0);
+      res->base.bind |= PIPE_BIND_SHADER_IMAGE;
+      struct zink_resource_object *old_obj = res->obj;
+      struct zink_resource_object *new_obj = resource_object_create(screen, &res->base, NULL, &res->optimal_tiling);
+      if (!new_obj) {
+         debug_printf("new backing resource alloc failed!");
+         res->base.bind &= ~PIPE_BIND_SHADER_IMAGE;
+         return false;
+      }
+      struct zink_resource staging = *res;
+      staging.obj = old_obj;
+      res->obj = new_obj;
+      zink_descriptor_set_refs_clear(&old_obj->desc_set_refs, old_obj);
+      for (unsigned i = 0; i <= res->base.last_level; i++) {
+         struct pipe_box box = {0, 0, 0,
+                                u_minify(res->base.width0, i),
+                                u_minify(res->base.height0, i), res->base.array_size};
+         box.depth = util_num_layers(&res->base, i);
+         ctx->base.resource_copy_region(&ctx->base, &res->base, i, 0, 0, 0, &staging.base, i, &box);
+      }
+      zink_resource_object_reference(screen, &old_obj, NULL);
+   }
+
+   if (res->bind_history & BITFIELD64_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW)) {
+      for (unsigned shader = 0; shader < PIPE_SHADER_TYPES; shader++) {
+         if (res->bind_stages & (1 << shader)) {
+            for (unsigned i = 0; i < ZINK_DESCRIPTOR_TYPE_IMAGE; i++) {
+               if (res->bind_history & BITFIELD64_BIT(i))
+                  zink_context_invalidate_descriptor_state(ctx, shader, i);
+            }
+         }
+      }
+   }
+   if (res->obj->is_buffer)
+      zink_resource_rebind(ctx, res);
+   else {
+      zink_rebind_framebuffer(ctx, res);
+      /* this will be cleaned up in future commits */
+      if (res->bind_history & BITFIELD_BIT(ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW)) {
+         for (unsigned i = 0; i < PIPE_SHADER_TYPES; i++) {
+            for (unsigned j = 0; j < ctx->num_sampler_views[i]; j++) {
+               struct zink_sampler_view *sv = zink_sampler_view(ctx->sampler_views[i][j]);
+               if (sv && sv->base.texture == &res->base) {
+                   struct pipe_surface *psurf = &sv->image_view->base;
+                   zink_rebind_surface(ctx, &psurf);
+                   sv->image_view = zink_surface(psurf);
+                   zink_context_invalidate_descriptor_state(ctx, i, ZINK_DESCRIPTOR_TYPE_SAMPLER_VIEW);
+               }
+            }
+         }
+      }
+   }
+
+   return true;
+}
+
 void
 zink_resource_setup_transfer_layouts(struct zink_context *ctx, struct zink_resource *src, struct zink_resource *dst)
 {
diff --git a/src/gallium/drivers/zink/zink_resource.h b/src/gallium/drivers/zink/zink_resource.h
index a8658e4d2fa..920a2537f05 100644
--- a/src/gallium/drivers/zink/zink_resource.h
+++ b/src/gallium/drivers/zink/zink_resource.h
@@ -57,6 +57,10 @@ struct zink_resource_object {
       VkBuffer buffer;
       VkImage image;
    };
+
+   VkBuffer sbuffer;
+   bool storage_init; //layout was set for image
+
    VkDeviceMemory mem;
    uint32_t mem_hash;
    struct mem_key mkey;
@@ -154,4 +158,6 @@ zink_resource_object_reference(struct zink_screen *screen,
    if (dst) *dst = src;
 }
 
+bool
+zink_resource_object_init_storage(struct zink_context *ctx, struct zink_resource *res);
 #endif
diff --git a/src/gallium/drivers/zink/zink_surface.c b/src/gallium/drivers/zink/zink_surface.c
index 2f57285e221..798911ea437 100644
--- a/src/gallium/drivers/zink/zink_surface.c
+++ b/src/gallium/drivers/zink/zink_surface.c
@@ -116,6 +116,7 @@ create_surface(struct pipe_context *pctx,
    surface->base.u.tex.level = level;
    surface->base.u.tex.first_layer = templ->u.tex.first_layer;
    surface->base.u.tex.last_layer = templ->u.tex.last_layer;
+   surface->obj = zink_resource(pres)->obj;
    util_dynarray_init(&surface->framebuffer_refs, NULL);
 
    if (vkCreateImageView(screen->dev, ivci, NULL,
@@ -218,11 +219,14 @@ zink_destroy_surface(struct zink_screen *screen, struct pipe_surface *psurface)
    simple_mtx_lock(&screen->surface_mtx);
    struct hash_entry *he = _mesa_hash_table_search_pre_hashed(&screen->surface_cache, surface->hash, &surface->ivci);
    assert(he);
+   assert(he->data == surface);
    _mesa_hash_table_remove(&screen->surface_cache, he);
    simple_mtx_unlock(&screen->surface_mtx);
    surface_clear_fb_refs(screen, psurface);
    util_dynarray_fini(&surface->framebuffer_refs);
    pipe_resource_reference(&psurface->texture, NULL);
+   if (surface->simage_view)
+      vkDestroyImageView(screen->dev, surface->simage_view, NULL);
    vkDestroyImageView(screen->dev, surface->image_view, NULL);
    FREE(surface);
 }
@@ -234,6 +238,47 @@ zink_surface_destroy(struct pipe_context *pctx,
    zink_destroy_surface(zink_screen(pctx->screen), psurface);
 }
 
+bool
+zink_rebind_surface(struct zink_context *ctx, struct pipe_surface **psurface)
+{
+   struct zink_surface *surface = zink_surface(*psurface);
+   struct zink_screen *screen = zink_screen(ctx->base.screen);
+   if (surface->simage_view)
+      return false;
+   VkImageViewCreateInfo ivci = create_ivci(screen,
+                                            zink_resource((*psurface)->texture), (*psurface));
+   uint32_t hash = hash_ivci(&ivci);
+
+   simple_mtx_lock(&screen->surface_mtx);
+   struct hash_entry *new_entry = _mesa_hash_table_search_pre_hashed(&screen->surface_cache, hash, &ivci);
+   surface_clear_fb_refs(screen, *psurface);
+   if (new_entry) {
+      /* reuse existing surface; old one will be cleaned up naturally */
+      struct zink_surface *new_surface = new_entry->data;
+      simple_mtx_unlock(&screen->surface_mtx);
+      zink_surface_reference(screen, (struct zink_surface**)psurface, new_surface);
+      return true;
+   }
+   struct hash_entry *entry = _mesa_hash_table_search_pre_hashed(&screen->surface_cache, surface->hash, &surface->ivci);
+   assert(entry);
+   _mesa_hash_table_remove(&screen->surface_cache, entry);
+   VkImageView image_view;
+   if (vkCreateImageView(screen->dev, &ivci, NULL, &image_view) != VK_SUCCESS) {
+      debug_printf("zink: failed to create new imageview");
+      simple_mtx_unlock(&screen->surface_mtx);
+      return false;
+   }
+   surface->hash = hash;
+   surface->ivci = ivci;
+   entry = _mesa_hash_table_insert_pre_hashed(&screen->surface_cache, surface->hash, &surface->ivci, surface);
+   assert(entry);
+   surface->simage_view = surface->image_view;
+   surface->image_view = image_view;
+   surface->obj = zink_resource(surface->base.texture)->obj;
+   simple_mtx_unlock(&screen->surface_mtx);
+   return true;
+}
+
 void
 zink_context_surface_init(struct pipe_context *context)
 {
diff --git a/src/gallium/drivers/zink/zink_surface.h b/src/gallium/drivers/zink/zink_surface.h
index 6a88a10350f..c8931ca32fe 100644
--- a/src/gallium/drivers/zink/zink_surface.h
+++ b/src/gallium/drivers/zink/zink_surface.h
@@ -34,6 +34,8 @@ struct zink_surface {
    struct pipe_surface base;
    VkImageViewCreateInfo ivci;
    VkImageView image_view;
+   VkImageView simage_view;//old iview after storage replacement/rebind
+   void *obj; //backing resource object
    uint32_t hash;
    struct zink_batch_usage batch_uses;
    struct util_dynarray framebuffer_refs;
@@ -93,4 +95,7 @@ zink_surface_clamp_viewtype(VkImageViewType viewType, unsigned first_layer, unsi
    }
    return viewType;
 }
+
+bool
+zink_rebind_surface(struct zink_context *ctx, struct pipe_surface **psurface);
 #endif



More information about the mesa-commit mailing list