From 2570a58bcdf30d699b89323fef60692093dee7ea Mon Sep 17 00:00:00 2001 From: =?utf8?q?Kristian=20H=C3=B8gsberg=20Kristensen?= Date: Thu, 11 Feb 2016 22:46:28 -0800 Subject: [PATCH] anv: Implement descriptor pools 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 | 189 +++++++++++++++++++++----- src/intel/vulkan/anv_meta.c | 25 ++++ src/intel/vulkan/anv_meta_blit.c | 6 +- src/intel/vulkan/anv_meta_resolve.c | 8 +- src/intel/vulkan/anv_private.h | 17 +++ 5 files changed, 200 insertions(+), 45 deletions(-) diff --git a/src/intel/vulkan/anv_descriptor_set.c b/src/intel/vulkan/anv_descriptor_set.c index 7a77336602a..718bc216f73 100644 --- a/src/intel/vulkan/anv_descriptor_set.c +++ b/src/intel/vulkan/anv_descriptor_set.c @@ -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; diff --git a/src/intel/vulkan/anv_meta.c b/src/intel/vulkan/anv_meta.c index 82944ea1a92..683a1623cc3 100644 --- a/src/intel/vulkan/anv_meta.c +++ b/src/intel/vulkan/anv_meta.c @@ -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; } diff --git a/src/intel/vulkan/anv_meta_blit.c b/src/intel/vulkan/anv_meta_blit.c index 06f13ecc8db..9c6cd8c510e 100644 --- a/src/intel/vulkan/anv_meta_blit.c +++ b/src/intel/vulkan/anv_meta_blit.c @@ -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, diff --git a/src/intel/vulkan/anv_meta_resolve.c b/src/intel/vulkan/anv_meta_resolve.c index ea5020c5f24..9a77d21452f 100644 --- a/src/intel/vulkan/anv_meta_resolve.c +++ b/src/intel/vulkan/anv_meta_resolve.c @@ -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); } diff --git a/src/intel/vulkan/anv_private.h b/src/intel/vulkan/anv_private.h index 479f3826135..6ce3f02d1f7 100644 --- a/src/intel/vulkan/anv_private.h +++ b/src/intel/vulkan/anv_private.h @@ -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) -- 2.30.2