vulkan/cmd_queue: Add a common vk_cmd_enqueue_CmdBindDescriptorSets
authorJason Ekstrand <jason.ekstrand@collabora.com>
Wed, 9 Mar 2022 21:27:33 +0000 (15:27 -0600)
committerMarge Bot <emma+marge@anholt.net>
Thu, 10 Mar 2022 21:08:36 +0000 (21:08 +0000)
In order for this to work, the driver must reference-count pipeline
layouts so we can take a reference while the command is in the queue.

Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15329>

src/vulkan/runtime/vk_cmd_enqueue.c
src/vulkan/runtime/vk_device.h

index b54f90e..1cb91e1 100644 (file)
@@ -25,6 +25,7 @@
 #include "vk_alloc.h"
 #include "vk_cmd_enqueue_entrypoints.h"
 #include "vk_command_buffer.h"
+#include "vk_device.h"
 #include "vk_util.h"
 
 VKAPI_ATTR void VKAPI_CALL
@@ -194,3 +195,70 @@ vk_cmd_enqueue_CmdPushDescriptorSetKHR(VkCommandBuffer commandBuffer,
       }
    }
 }
+
+static void
+unref_pipeline_layout(struct vk_cmd_queue *queue,
+                      struct vk_cmd_queue_entry *cmd)
+{
+   struct vk_command_buffer *cmd_buffer =
+      container_of(queue, struct vk_command_buffer, cmd_queue);
+   struct vk_device *device = cmd_buffer->base.device;
+
+   assert(cmd->type == VK_CMD_BIND_DESCRIPTOR_SETS);
+
+   device->unref_pipeline_layout(device, cmd->u.bind_descriptor_sets.layout);
+}
+
+VKAPI_ATTR void VKAPI_CALL
+vk_cmd_enqueue_CmdBindDescriptorSets(VkCommandBuffer commandBuffer,
+                                     VkPipelineBindPoint pipelineBindPoint,
+                                     VkPipelineLayout layout,
+                                     uint32_t firstSet,
+                                     uint32_t descriptorSetCount,
+                                     const VkDescriptorSet* pDescriptorSets,
+                                     uint32_t dynamicOffsetCount,
+                                     const uint32_t *pDynamicOffsets)
+{
+   VK_FROM_HANDLE(vk_command_buffer, cmd_buffer, commandBuffer);
+   struct vk_device *device = cmd_buffer->base.device;
+
+   struct vk_cmd_queue_entry *cmd =
+      vk_zalloc(cmd_buffer->cmd_queue.alloc, sizeof(*cmd), 8,
+                VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+   if (!cmd)
+      return;
+
+   cmd->type = VK_CMD_BIND_DESCRIPTOR_SETS;
+   list_addtail(&cmd->cmd_link, &cmd_buffer->cmd_queue.cmds);
+
+   /* We need to hold a reference to the descriptor set as long as this
+    * command is in the queue.  Otherwise, it may get deleted out from under
+    * us before the command is replayed.
+    */
+   device->ref_pipeline_layout(device, layout);
+   cmd->u.bind_descriptor_sets.layout = layout;
+   cmd->driver_free_cb = unref_pipeline_layout;
+
+   cmd->u.bind_descriptor_sets.pipeline_bind_point = pipelineBindPoint;
+   cmd->u.bind_descriptor_sets.first_set = firstSet;
+   cmd->u.bind_descriptor_sets.descriptor_set_count = descriptorSetCount;
+   if (pDescriptorSets) {
+      cmd->u.bind_descriptor_sets.descriptor_sets =
+         vk_zalloc(cmd_buffer->cmd_queue.alloc,
+                   sizeof(*cmd->u.bind_descriptor_sets.descriptor_sets) * descriptorSetCount, 8,
+                   VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+
+      memcpy(cmd->u.bind_descriptor_sets.descriptor_sets, pDescriptorSets,
+             sizeof(*cmd->u.bind_descriptor_sets.descriptor_sets) * descriptorSetCount);
+   }
+   cmd->u.bind_descriptor_sets.dynamic_offset_count = dynamicOffsetCount;
+   if (pDynamicOffsets) {
+      cmd->u.bind_descriptor_sets.dynamic_offsets =
+         vk_zalloc(cmd_buffer->cmd_queue.alloc,
+                   sizeof(*cmd->u.bind_descriptor_sets.dynamic_offsets) * dynamicOffsetCount, 8,
+                   VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
+
+      memcpy(cmd->u.bind_descriptor_sets.dynamic_offsets, pDynamicOffsets,
+             sizeof(*cmd->u.bind_descriptor_sets.dynamic_offsets) * dynamicOffsetCount);
+   }
+}
index 9dced00..b649e2e 100644 (file)
@@ -96,6 +96,22 @@ struct vk_device {
                                       bool signal_memory,
                                       struct vk_sync **sync_out);
 
+   /** Increments the reference count on a pipeline layout
+    *
+    * This is required for vk_enqueue_CmdBindDescriptorSets() to avoid
+    * use-after-free problems with pipeline layouts.  If you're not using
+    * the command queue, you can ignore this.
+    */
+   void (*ref_pipeline_layout)(struct vk_device *device,
+                               VkPipelineLayout layout);
+
+   /** Decrements the reference count on a pipeline layout
+    *
+    * See ref_pipeline_layout above.
+    */
+   void (*unref_pipeline_layout)(struct vk_device *device,
+                                 VkPipelineLayout layout);
+
    /* Set by vk_device_set_drm_fd() */
    int drm_fd;