v3dv: implement proper caching for partial clear pipelines
authorIago Toral Quiroga <itoral@igalia.com>
Tue, 31 Mar 2020 11:06:55 +0000 (13:06 +0200)
committerMarge Bot <eric+marge@anholt.net>
Tue, 13 Oct 2020 21:21:29 +0000 (21:21 +0000)
So far we have been caching the first pipeline we produced and always
reusing that, which is obviously incorrect.

This change implements a proper cache and also takes care of releasing
the cached resources when the device is destroyed.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/6766>

src/broadcom/vulkan/v3dv_device.c
src/broadcom/vulkan/v3dv_meta_clear.c
src/broadcom/vulkan/v3dv_private.h

index d7814b0..6dbf38a 100644 (file)
@@ -1057,6 +1057,50 @@ init_device_dispatch(struct v3dv_device *device)
    }
 }
 
+static uint32_t
+meta_color_clear_cache_hash(const void *key)
+{
+   return _mesa_hash_data(key, sizeof(uint64_t));
+}
+
+static bool
+meta_color_clear_cache_compare(const void *key1, const void *key2)
+{
+   return memcmp(key1, key2, sizeof(uint64_t)) == 0;
+}
+
+static void
+init_device_meta(struct v3dv_device *device)
+{
+   mtx_init(&device->meta.mtx, mtx_plain);
+
+   device->meta.color_clear.cache =
+      _mesa_hash_table_create(NULL,
+                              meta_color_clear_cache_hash,
+                              meta_color_clear_cache_compare);
+}
+
+static void
+destroy_device_meta(struct v3dv_device *device)
+{
+   VkDevice _device = v3dv_device_to_handle(device);
+
+   mtx_destroy(&device->meta.mtx);
+
+   hash_table_foreach(device->meta.color_clear.cache, entry) {
+      struct v3dv_meta_color_clear_pipeline *item = entry->data;
+      v3dv_DestroyPipeline(_device, item->pipeline, &device->alloc);
+      v3dv_DestroyRenderPass(_device, item->pass, &device->alloc);
+      vk_free(&device->alloc, item);
+   }
+   _mesa_hash_table_destroy(device->meta.color_clear.cache, NULL);
+
+   if (device->meta.color_clear.playout) {
+      v3dv_DestroyPipelineLayout(_device, device->meta.color_clear.playout,
+                                 &device->alloc);
+   }
+}
+
 VkResult
 v3dv_CreateDevice(VkPhysicalDevice physicalDevice,
                   const VkDeviceCreateInfo *pCreateInfo,
@@ -1157,6 +1201,7 @@ v3dv_CreateDevice(VkPhysicalDevice physicalDevice,
    }
 
    init_device_dispatch(device);
+   init_device_meta(device);
 
    *pDevice = v3dv_device_to_handle(device);
 
@@ -1177,6 +1222,7 @@ v3dv_DestroyDevice(VkDevice _device,
    v3dv_DeviceWaitIdle(_device);
    queue_finish(&device->queue);
    drmSyncobjDestroy(device->render_fd, device->last_job_sync);
+   destroy_device_meta(device);
 
    vk_free2(&default_alloc, pAllocator, device);
 }
index b8989d6..db96ae0 100644 (file)
@@ -258,7 +258,7 @@ create_color_clear_pipeline(struct v3dv_device *device,
                             uint32_t rt_idx,
                             uint32_t samples,
                             VkRenderPass _pass,
-                            VkPipelineLayout *pipeline_layout,
+                            VkPipelineLayout pipeline_layout,
                             VkPipeline *pipeline)
 {
    /* For now we only support clearing a framebuffer with a single attachment */
@@ -305,10 +305,6 @@ create_color_clear_pipeline(struct v3dv_device *device,
       .pAttachments = blend_att_state
    };
 
-   VkResult result = create_color_clear_pipeline_layout(device, pipeline_layout);
-   if (result != VK_SUCCESS)
-      return result;
-
    return create_pipeline(device,
                           pass,
                           samples,
@@ -316,7 +312,7 @@ create_color_clear_pipeline(struct v3dv_device *device,
                           &vi_state,
                           &ds_state,
                           &cb_state,
-                          *pipeline_layout,
+                          pipeline_layout,
                           pipeline);
 }
 
@@ -365,36 +361,76 @@ create_color_clear_render_pass(struct v3dv_device *device,
                                 &info, &device->alloc, pass);
 }
 
+static inline uint64_t
+get_color_clear_pipeline_cache_key(VkFormat format, uint32_t samples)
+{
+   return ((uint64_t) samples) << 32 | format;
+}
+
 static VkResult
-ensure_color_clear_pipeline(struct v3dv_device *device,
-                            VkFormat format,
-                            uint32_t samples)
+get_color_clear_pipeline(struct v3dv_device *device,
+                         VkFormat format,
+                         uint32_t samples,
+                         struct v3dv_meta_color_clear_pipeline **pipeline)
 {
-   /* FIXME: we need a pipeline per [format, samples], right now this
-    * only works for the first combination we need.
-    */
    VkResult result = VK_SUCCESS;
-   if (device->meta.color_clear.pipeline)
-      return result;
 
-   if (!device->meta.color_clear.pass) {
+   mtx_lock(&device->meta.mtx);
+   if (!device->meta.color_clear.playout) {
       result =
-         create_color_clear_render_pass(device,
-                                        format,
-                                        samples,
-                                        &device->meta.color_clear.pass);
-      if (result != VK_SUCCESS)
-         return result;
+         create_color_clear_pipeline_layout(device,
+                                            &device->meta.color_clear.playout);
    }
+   mtx_unlock(&device->meta.mtx);
+   if (result != VK_SUCCESS)
+      return result;
 
-   if (!device->meta.color_clear.pipeline) {
-      result =
-         create_color_clear_pipeline(device, 0 /* rt_idx*/, samples,
-                                     device->meta.color_clear.pass,
-                                     &device->meta.color_clear.playout,
-                                     &device->meta.color_clear.pipeline);
-      if (result != VK_SUCCESS)
-         return result;
+   uint64_t key = get_color_clear_pipeline_cache_key(format, samples);
+   mtx_lock(&device->meta.mtx);
+   struct hash_entry *entry =
+      _mesa_hash_table_search(device->meta.color_clear.cache, &key);
+   if (entry) {
+      mtx_unlock(&device->meta.mtx);
+      *pipeline = entry->data;
+      return VK_SUCCESS;
+   }
+
+   *pipeline = vk_zalloc2(&device->alloc, NULL, sizeof(**pipeline), 8,
+                          VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
+
+   if (*pipeline == NULL)
+      goto fail;
+
+   result = create_color_clear_render_pass(device,
+                                           format,
+                                           samples,
+                                           &(*pipeline)->pass);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   result = create_color_clear_pipeline(device, 0 /* rt_idx*/, samples,
+                                        (*pipeline)->pass,
+                                        device->meta.color_clear.playout,
+                                        &(*pipeline)->pipeline);
+   if (result != VK_SUCCESS)
+      goto fail;
+
+   _mesa_hash_table_insert(device->meta.color_clear.cache, &key, *pipeline);
+
+   mtx_unlock(&device->meta.mtx);
+   return VK_SUCCESS;
+
+fail:
+   mtx_unlock(&device->meta.mtx);
+
+   VkDevice _device = v3dv_device_to_handle(device);
+   if (*pipeline) {
+      if ((*pipeline)->pass)
+         v3dv_DestroyRenderPass(_device, (*pipeline)->pass, &device->alloc);
+      if ((*pipeline)->pipeline)
+         v3dv_DestroyPipeline(_device, (*pipeline)->pipeline, &device->alloc);
+      vk_free(&device->alloc, *pipeline);
+      *pipeline = NULL;
    }
 
    return result;
@@ -444,8 +480,12 @@ emit_color_clear_rect(struct v3dv_cmd_buffer *cmd_buffer,
    if (vk_format_is_depth_or_stencil(rt_format))
       rt_format = get_color_format_for_depth_stencil_format(rt_format);
 
-   if (ensure_color_clear_pipeline(device, rt_format, rt_samples) != VK_SUCCESS)
+   struct v3dv_meta_color_clear_pipeline *pipeline = NULL;
+   VkResult result =
+      get_color_clear_pipeline(device, rt_format, rt_samples, &pipeline);
+   if (result != VK_SUCCESS)
       return;
+   assert(pipeline && pipeline->pipeline && pipeline->pass);
 
    /* Store command buffer state for the current subpass before we interrupt
     * it to emit the color clear pass and then finish the job for the
@@ -508,7 +548,7 @@ emit_color_clear_rect(struct v3dv_cmd_buffer *cmd_buffer,
 
       VkRenderPassBeginInfo rp_info = {
          .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
-         .renderPass = device->meta.color_clear.pass,
+         .renderPass = pipeline->pass,
          .framebuffer = fb,
          .renderArea = { .offset = { 0, 0 },
                          .extent = { fb_info.width, fb_info.height } },
@@ -532,7 +572,7 @@ emit_color_clear_rect(struct v3dv_cmd_buffer *cmd_buffer,
 
       v3dv_CmdBindPipeline(cmd_buffer_handle,
                            VK_PIPELINE_BIND_POINT_GRAPHICS,
-                           device->meta.color_clear.pipeline);
+                           pipeline->pipeline);
 
       const VkViewport viewport = {
          .x = rect->rect.offset.x,
index c72bb6f..474de76 100644 (file)
@@ -217,6 +217,12 @@ struct v3dv_queue {
 
 void v3dv_queue_destroy_completed_noop_jobs(struct v3dv_queue *queue);
 
+
+struct v3dv_meta_color_clear_pipeline {
+   VkPipeline pipeline;
+   VkRenderPass pass;
+};
+
 struct v3dv_device {
    VK_LOADER_DATA _loader_data;
 
@@ -239,10 +245,10 @@ struct v3dv_device {
 
    /* Resources used for meta operations */
    struct {
+      mtx_t mtx;
       struct {
          VkPipelineLayout playout;
-         VkPipeline pipeline;
-         VkRenderPass pass;
+         struct hash_table *cache; /* v3dv_meta_color_clear_pipeline */
       } color_clear;
    } meta;
 };