anv: Implement descriptor pools
authorKristian Høgsberg Kristensen <kristian.h.kristensen@intel.com>
Fri, 12 Feb 2016 06:46:28 +0000 (22:46 -0800)
committerKristian Høgsberg Kristensen <kristian.h.kristensen@intel.com>
Tue, 23 Feb 2016 01:13:51 +0000 (17:13 -0800)
Descriptor pools are an optimization that lets applications allocate
descriptor sets through an externally synchronized object (that is,
unlocked).  In our case it's also plugging a memory leak, since we
didn't track all allocated sets and failed to free them in
vkResetDescriptorPool() and vkDestroyDescriptorPool().

src/intel/vulkan/anv_descriptor_set.c
src/intel/vulkan/anv_meta.c
src/intel/vulkan/anv_meta_blit.c
src/intel/vulkan/anv_meta_resolve.c
src/intel/vulkan/anv_private.h

index 7a77336602a42be997743bc23b96738372bde09b..718bc216f73730285c177cb9e0ee23222aee7248 100644 (file)
@@ -244,17 +244,67 @@ void anv_DestroyPipelineLayout(
 }
 
 /*
- * Descriptor pools.  These are a no-op for now.
+ * Descriptor pools.
+ *
+ * These are implemented using a big pool of memory and a free-list for the
+ * host memory allocations and a state_stream and a free list for the buffer
+ * view surface state. The spec allows us to fail to allocate due to
+ * fragmentation in all cases but two: 1) after pool reset, allocating up
+ * until the pool size with no freeing must succeed and 2) allocating and
+ * freeing only descriptor sets with the same layout. Case 1) is easy enogh,
+ * and the free lists lets us recycle blocks for case 2).
  */
 
+#define EMPTY 1
+
 VkResult anv_CreateDescriptorPool(
-    VkDevice                                    device,
+    VkDevice                                    _device,
     const VkDescriptorPoolCreateInfo*           pCreateInfo,
     const VkAllocationCallbacks*                pAllocator,
     VkDescriptorPool*                           pDescriptorPool)
 {
-   anv_finishme("VkDescriptorPool is a stub");
-   *pDescriptorPool = (VkDescriptorPool)1;
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   struct anv_descriptor_pool *pool;
+
+   uint32_t descriptor_count = 0;
+   uint32_t buffer_count = 0;
+   for (uint32_t i = 0; i < pCreateInfo->poolSizeCount; i++) {
+      switch (pCreateInfo->pPoolSizes[i].type) {
+      case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+      case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+      case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+      case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+         buffer_count += pCreateInfo->pPoolSizes[i].descriptorCount;
+      default:
+         descriptor_count += pCreateInfo->pPoolSizes[i].descriptorCount;
+         break;
+      }
+   }
+
+   const size_t set_size =
+      sizeof(struct anv_descriptor_set) +
+      descriptor_count * sizeof(struct anv_descriptor) +
+      buffer_count * sizeof(struct anv_buffer_view);
+
+   const size_t size =
+      sizeof(*pool) +
+      pCreateInfo->maxSets * set_size;
+
+   pool = anv_alloc2(&device->alloc, pAllocator, size, 8,
+                     VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
+   if (!pool)
+      return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
+
+   pool->size = size;
+   pool->next = 0;
+   pool->free_list = EMPTY;
+
+   anv_state_stream_init(&pool->surface_state_stream,
+                         &device->surface_state_block_pool);
+   pool->surface_state_free_list = NULL;
+
+   *pDescriptorPool = anv_descriptor_pool_to_handle(pool);
+
    return VK_SUCCESS;
 }
 
@@ -263,37 +313,85 @@ void anv_DestroyDescriptorPool(
     VkDescriptorPool                            _pool,
     const VkAllocationCallbacks*                pAllocator)
 {
-   anv_finishme("VkDescriptorPool is a stub: free the pool's descriptor sets");
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   ANV_FROM_HANDLE(anv_descriptor_pool, pool, _pool);
+
+   anv_state_stream_finish(&pool->surface_state_stream);
+   anv_free2(&device->alloc, pAllocator, pool);
 }
 
 VkResult anv_ResetDescriptorPool(
-    VkDevice                                    device,
+    VkDevice                                    _device,
     VkDescriptorPool                            descriptorPool,
     VkDescriptorPoolResetFlags                  flags)
 {
-   anv_finishme("VkDescriptorPool is a stub: free the pool's descriptor sets");
+   ANV_FROM_HANDLE(anv_device, device, _device);
+   ANV_FROM_HANDLE(anv_descriptor_pool, pool, descriptorPool);
+
+   pool->next = 0;
+   pool->free_list = EMPTY;
+   anv_state_stream_finish(&pool->surface_state_stream);
+   anv_state_stream_init(&pool->surface_state_stream,
+                         &device->surface_state_block_pool);
+   pool->surface_state_free_list = NULL;
+
    return VK_SUCCESS;
 }
 
+struct pool_free_list_entry {
+   uint32_t next;
+   uint32_t size;
+};
+
+static size_t
+layout_size(const struct anv_descriptor_set_layout *layout)
+{
+   return
+      sizeof(struct anv_descriptor_set) +
+      layout->size * sizeof(struct anv_descriptor) +
+      layout->buffer_count * sizeof(struct anv_buffer_view);
+}
+
+struct surface_state_free_list_entry {
+   void *next;
+   uint32_t offset;
+};
+
 VkResult
 anv_descriptor_set_create(struct anv_device *device,
+                          struct anv_descriptor_pool *pool,
                           const struct anv_descriptor_set_layout *layout,
                           struct anv_descriptor_set **out_set)
 {
    struct anv_descriptor_set *set;
-   size_t size = sizeof(*set) + layout->size * sizeof(set->descriptors[0]);
+   const size_t size = layout_size(layout);
+
+   set = NULL;
+   if (size <= pool->size - pool->next) {
+      set = (struct anv_descriptor_set *) (pool->data + pool->next);
+      pool->next += size;
+   } else {
+      struct pool_free_list_entry *entry;
+      uint32_t *link = &pool->free_list;
+      for (uint32_t f = pool->free_list; f != EMPTY; f = entry->next) {
+         entry = (struct pool_free_list_entry *) (pool->data + f);
+         if (size <= entry->size) {
+            *link = entry->next;
+            set = (struct anv_descriptor_set *) entry;
+            break;
+         }
+         link = &entry->next;
+      }
+   }
 
-   set = anv_alloc(&device->alloc /* XXX: Use the pool */, size, 8,
-                   VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
-   if (!set)
+   if (set == NULL)
       return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
 
-   /* A descriptor set may not be 100% filled. Clear the set so we can can
-    * later detect holes in it.
-    */
-   memset(set, 0, size);
-
+   set->size = size;
    set->layout = layout;
+   set->buffer_views =
+      (struct anv_buffer_view *) &set->descriptors[layout->size];
+   set->buffer_count = layout->buffer_count;
 
    /* Go through and fill out immutable samplers if we have any */
    struct anv_descriptor *desc = set->descriptors;
@@ -305,21 +403,24 @@ anv_descriptor_set_create(struct anv_device *device,
       desc += layout->binding[b].array_size;
    }
 
-   /* XXX: Use the pool */
-   set->buffer_views =
-      anv_alloc(&device->alloc,
-                sizeof(set->buffer_views[0]) * layout->buffer_count, 8,
-                VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
-   if (!set->buffer_views) {
-      anv_free(&device->alloc, set);
-      return vk_error(VK_ERROR_OUT_OF_HOST_MEMORY);
-   }
-
+   /* Allocate surface state for the buffer views. */
    for (uint32_t b = 0; b < layout->buffer_count; b++) {
-      set->buffer_views[b].surface_state =
-         anv_state_pool_alloc(&device->surface_state_pool, 64, 64);
+      struct surface_state_free_list_entry *entry =
+         pool->surface_state_free_list;
+      struct anv_state state;
+
+      if (entry) {
+         state.map = entry;
+         state.offset = entry->offset;
+         state.alloc_size = 64;
+         pool->surface_state_free_list = entry->next;
+      } else {
+         state = anv_state_stream_alloc(&pool->surface_state_stream, 64, 64);
+      }
+
+      set->buffer_views[b].surface_state = state;
    }
-   set->buffer_count = layout->buffer_count;
+
    *out_set = set;
 
    return VK_SUCCESS;
@@ -327,15 +428,27 @@ anv_descriptor_set_create(struct anv_device *device,
 
 void
 anv_descriptor_set_destroy(struct anv_device *device,
+                           struct anv_descriptor_pool *pool,
                            struct anv_descriptor_set *set)
 {
-   /* XXX: Use the pool */
-   for (uint32_t b = 0; b < set->buffer_count; b++)
-      anv_state_pool_free(&device->surface_state_pool,
-                          set->buffer_views[b].surface_state);
+   /* Put the buffer view surface state back on the free list. */
+   for (uint32_t b = 0; b < set->buffer_count; b++) {
+      struct surface_state_free_list_entry *entry =
+         set->buffer_views[b].surface_state.map;
+      entry->next = pool->surface_state_free_list;
+      pool->surface_state_free_list = entry;
+   }
 
-   anv_free(&device->alloc, set->buffer_views);
-   anv_free(&device->alloc, set);
+   /* Put the descriptor set allocation back on the free list. */
+   const uint32_t index = (char *) set - pool->data;
+   if (index + set->size == pool->next) {
+      pool->next = index;
+   } else {
+      struct pool_free_list_entry *entry = (struct pool_free_list_entry *) set;
+      entry->next = pool->free_list;
+      entry->size = set->size;
+      pool->free_list = (char *) entry - pool->data;
+   }
 }
 
 VkResult anv_AllocateDescriptorSets(
@@ -344,6 +457,7 @@ VkResult anv_AllocateDescriptorSets(
     VkDescriptorSet*                            pDescriptorSets)
 {
    ANV_FROM_HANDLE(anv_device, device, _device);
+   ANV_FROM_HANDLE(anv_descriptor_pool, pool, pAllocateInfo->descriptorPool);
 
    VkResult result = VK_SUCCESS;
    struct anv_descriptor_set *set;
@@ -353,7 +467,7 @@ VkResult anv_AllocateDescriptorSets(
       ANV_FROM_HANDLE(anv_descriptor_set_layout, layout,
                       pAllocateInfo->pSetLayouts[i]);
 
-      result = anv_descriptor_set_create(device, layout, &set);
+      result = anv_descriptor_set_create(device, pool, layout, &set);
       if (result != VK_SUCCESS)
          break;
 
@@ -374,11 +488,12 @@ VkResult anv_FreeDescriptorSets(
     const VkDescriptorSet*                      pDescriptorSets)
 {
    ANV_FROM_HANDLE(anv_device, device, _device);
+   ANV_FROM_HANDLE(anv_descriptor_pool, pool, descriptorPool);
 
    for (uint32_t i = 0; i < count; i++) {
       ANV_FROM_HANDLE(anv_descriptor_set, set, pDescriptorSets[i]);
 
-      anv_descriptor_set_destroy(device, set);
+      anv_descriptor_set_destroy(device, pool, set);
    }
 
    return VK_SUCCESS;
index 82944ea1a92012360d488d6476289c916663a80c..683a1623cc3d3d5ebe2f3c4f70fd9a6884b18bca 100644 (file)
@@ -138,6 +138,27 @@ anv_device_init_meta(struct anv_device *device)
       .pfnFree = meta_free,
    };
 
+   const VkDescriptorPoolCreateInfo create_info = {
+      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
+      .pNext = NULL,
+      .flags = 0,
+      .maxSets = 1,
+      .poolSizeCount = 1,
+      .pPoolSizes = (VkDescriptorPoolSize[]) {
+         {
+            .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
+            .descriptorCount = 1
+         },
+      }
+   };
+
+   result = anv_CreateDescriptorPool(anv_device_to_handle(device),
+                                     &create_info,
+                                     &device->meta_state.alloc,
+                                     &device->meta_state.desc_pool);
+   if (result != VK_SUCCESS)
+      goto fail_desc_pool;
+
    result = anv_device_init_meta_clear_state(device);
    if (result != VK_SUCCESS)
       goto fail_clear;
@@ -157,6 +178,10 @@ fail_blit:
 fail_resolve:
    anv_device_finish_meta_clear_state(device);
 fail_clear:
+   anv_DestroyDescriptorPool(anv_device_to_handle(device),
+                             device->meta_state.desc_pool,
+                             &device->meta_state.alloc);
+fail_desc_pool:
    return result;
 }
 
index 06f13ecc8db55d908ce78f195d359099ab288060..9c6cd8c510e8b9a443fa55704b91b0cd5284c3c4 100644 (file)
@@ -165,7 +165,6 @@ meta_emit_blit(struct anv_cmd_buffer *cmd_buffer,
                VkFilter blit_filter)
 {
    struct anv_device *device = cmd_buffer->device;
-   VkDescriptorPool dummy_desc_pool = (VkDescriptorPool)1;
 
    struct blit_vb_data {
       float pos[2];
@@ -248,7 +247,7 @@ meta_emit_blit(struct anv_cmd_buffer *cmd_buffer,
    anv_AllocateDescriptorSets(anv_device_to_handle(device),
       &(VkDescriptorSetAllocateInfo) {
          .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
-         .descriptorPool = dummy_desc_pool,
+         .descriptorPool = device->meta_state.desc_pool,
          .descriptorSetCount = 1,
          .pSetLayouts = &device->meta_state.blit.ds_layout
       }, &set);
@@ -341,7 +340,8 @@ meta_emit_blit(struct anv_cmd_buffer *cmd_buffer,
    /* At the point where we emit the draw call, all data from the
     * descriptor sets, etc. has been used.  We are free to delete it.
     */
-   anv_descriptor_set_destroy(device, anv_descriptor_set_from_handle(set));
+   anv_ResetDescriptorPool(anv_device_to_handle(device),
+                           device->meta_state.desc_pool, 0);
    anv_DestroySampler(anv_device_to_handle(device), sampler,
                       &cmd_buffer->pool->alloc);
    anv_DestroyFramebuffer(anv_device_to_handle(device), fb,
index ea5020c5f241dfe9262829e914747bfc27d9b6cf..9a77d21452f4f73e528b734b15638bf74f8d2210 100644 (file)
@@ -483,7 +483,6 @@ emit_resolve(struct anv_cmd_buffer *cmd_buffer,
    VkCommandBuffer cmd_buffer_h = anv_cmd_buffer_to_handle(cmd_buffer);
    const struct anv_framebuffer *fb = cmd_buffer->state.framebuffer;
    const struct anv_image *src_image = src_iview->image;
-   VkDescriptorPool dummy_desc_pool_h = (VkDescriptorPool) 1;
 
    const struct vertex_attrs vertex_data[3] = {
       {
@@ -564,7 +563,7 @@ emit_resolve(struct anv_cmd_buffer *cmd_buffer,
    anv_AllocateDescriptorSets(device_h,
       &(VkDescriptorSetAllocateInfo) {
          .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
-         .descriptorPool = dummy_desc_pool_h,
+         .descriptorPool = device->meta_state.desc_pool,
          .descriptorSetCount = 1,
          .pSetLayouts = (VkDescriptorSetLayout[]) {
             device->meta_state.resolve.ds_layout,
@@ -572,8 +571,6 @@ emit_resolve(struct anv_cmd_buffer *cmd_buffer,
       },
       &desc_set_h);
 
-   ANV_FROM_HANDLE(anv_descriptor_set, desc_set, desc_set_h);
-
    anv_UpdateDescriptorSets(device_h,
       /*writeCount*/ 1,
       (VkWriteDescriptorSet[]) {
@@ -644,7 +641,8 @@ emit_resolve(struct anv_cmd_buffer *cmd_buffer,
    /* All objects below are consumed by the draw call. We may safely destroy
     * them.
     */
-   anv_descriptor_set_destroy(device, desc_set);
+   anv_ResetDescriptorPool(anv_device_to_handle(device),
+                           device->meta_state.desc_pool, 0);
    anv_DestroySampler(device_h, sampler_h,
                       &cmd_buffer->pool->alloc);
 }
index 479f38261355496028abbc4c2f28315977d7ca03..6ce3f02d1f7ed6c51a6121e1dc934cf5def1ee8c 100644 (file)
@@ -571,6 +571,8 @@ void anv_finish_wsi(struct anv_instance *instance);
 struct anv_meta_state {
    VkAllocationCallbacks alloc;
 
+   VkDescriptorPool desc_pool;
+
    /**
     * Use array element `i` for images with `2^i` samples.
     */
@@ -959,18 +961,32 @@ struct anv_descriptor {
 
 struct anv_descriptor_set {
    const struct anv_descriptor_set_layout *layout;
+   uint32_t size;
    uint32_t buffer_count;
    struct anv_buffer_view *buffer_views;
    struct anv_descriptor descriptors[0];
 };
 
+struct anv_descriptor_pool {
+   uint32_t size;
+   uint32_t next;
+   uint32_t free_list;
+
+   struct anv_state_stream surface_state_stream;
+   void *surface_state_free_list;
+
+   char data[0];
+};
+
 VkResult
 anv_descriptor_set_create(struct anv_device *device,
+                          struct anv_descriptor_pool *pool,
                           const struct anv_descriptor_set_layout *layout,
                           struct anv_descriptor_set **out_set);
 
 void
 anv_descriptor_set_destroy(struct anv_device *device,
+                           struct anv_descriptor_pool *pool,
                            struct anv_descriptor_set *set);
 
 struct anv_pipeline_binding {
@@ -1839,6 +1855,7 @@ ANV_DEFINE_HANDLE_CASTS(anv_queue, VkQueue)
 ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_cmd_pool, VkCommandPool)
 ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_buffer, VkBuffer)
 ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_buffer_view, VkBufferView)
+ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_descriptor_pool, VkDescriptorPool)
 ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_descriptor_set, VkDescriptorSet)
 ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_descriptor_set_layout, VkDescriptorSetLayout)
 ANV_DEFINE_NONDISP_HANDLE_CASTS(anv_device_memory, VkDeviceMemory)