tu: Faster descriptor set allocator

memmove was slow on large number of descriptor when destroying some of
them.

util_vma_heap is perfect for the task, ANV and RADV already use it.
It also simplifies the code.

Signed-off-by: Danylo Piliaiev <dpiliaiev@igalia.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/38053>
This commit is contained in:
Danylo Piliaiev
2025-10-24 15:16:10 +02:00
committed by Marge Bot
parent 562bb8b62b
commit 62f0ef3445
2 changed files with 64 additions and 97 deletions
+54 -89
View File
@@ -617,58 +617,27 @@ tu_descriptor_set_create(struct tu_device *device,
return vk_error(device, VK_ERROR_OUT_OF_POOL_MEMORY);
}
/* try to allocate linearly first, so that we don't spend
* time looking for gaps if the app only allocates &
* resets via the pool. */
if (pool->current_offset + layout_size <= pool->size) {
set->mapped_ptr = (uint32_t*)(pool_base(pool) + pool->current_offset);
set->va = pool->host_bo ? 0 : pool->bo->iova + pool->current_offset;
uint64_t current_offset = pool->current_offset;
if (!pool->host_memory_base) {
pool->entries[pool->entry_count].offset = pool->current_offset;
pool->entries[pool->entry_count].size = layout_size;
pool->entries[pool->entry_count].set = set;
pool->entry_count++;
}
pool->current_offset += layout_size;
} else if (!pool->host_memory_base) {
uint64_t offset = 0;
int index;
if (!pool->host_memory_base) {
uint64_t pool_vma_offset =
util_vma_heap_alloc(&pool->bo_heap, set->size, 1);
if (!pool_vma_offset)
return vk_error(device, VK_ERROR_FRAGMENTED_POOL);
for (index = 0; index < pool->entry_count; ++index) {
if (pool->entries[index].size == 0)
continue;
if (pool->entries[index].offset - offset >= layout_size)
break;
offset = pool->entries[index].offset + pool->entries[index].size;
}
if (pool->size - offset < layout_size) {
vk_object_free(&device->vk, NULL, set);
assert(pool_vma_offset >= TU_POOL_HEAP_OFFSET &&
pool_vma_offset <= pool->size + TU_POOL_HEAP_OFFSET);
set->offset = pool_vma_offset - TU_POOL_HEAP_OFFSET;
current_offset = set->offset;
} else {
if (current_offset + set->size > pool->size)
return vk_error(device, VK_ERROR_OUT_OF_POOL_MEMORY);
}
set->mapped_ptr = (uint32_t*)(pool_base(pool) + offset);
set->va = pool->host_bo ? 0 : pool->bo->iova + offset;
pool->current_offset += set->size;
}
memmove(&pool->entries[index + 1], &pool->entries[index],
sizeof(pool->entries[0]) * (pool->entry_count - index));
pool->entries[index].offset = offset;
pool->entries[index].size = layout_size;
pool->entries[index].set = set;
pool->entry_count++;
} else
return vk_error(device, VK_ERROR_OUT_OF_POOL_MEMORY);
} else if (!pool->host_memory_base) {
/* Also keep track of zero sized descriptor sets, such as descriptor
* sets with just dynamic descriptors, so that we can free the sets on
* vkDestroyDescriptorPool().
*/
pool->entries[pool->entry_count].offset = ~0;
pool->entries[pool->entry_count].size = 0;
pool->entries[pool->entry_count].set = set;
pool->entry_count++;
set->mapped_ptr = (uint32_t*)(pool_base(pool) + current_offset);
set->va = pool->host_bo ? 0 : pool->bo->iova + current_offset;
}
if (layout->has_immutable_samplers) {
@@ -693,6 +662,7 @@ tu_descriptor_set_create(struct tu_device *device,
vk_descriptor_set_layout_ref(&layout->vk);
list_addtail(&set->pool_link, &pool->desc_sets);
pool->entry_count++;
*out_set = set;
return VK_SUCCESS;
@@ -701,30 +671,17 @@ tu_descriptor_set_create(struct tu_device *device,
static void
tu_descriptor_set_destroy(struct tu_device *device,
struct tu_descriptor_pool *pool,
struct tu_descriptor_set *set,
bool free_bo)
struct tu_descriptor_set *set)
{
assert(!pool->host_memory_base);
if (free_bo && !pool->host_memory_base) {
for (int i = 0; i < pool->entry_count; ++i) {
if (pool->entries[i].set == set) {
if (set->size) {
ASSERTED uint32_t offset =
(uint8_t *) set->mapped_ptr - pool_base(pool);
assert(pool->entries[i].offset == offset);
} else {
assert(pool->entries[i].size == 0);
}
memmove(&pool->entries[i], &pool->entries[i+1],
sizeof(pool->entries[i]) * (pool->entry_count - i - 1));
--pool->entry_count;
break;
}
}
if (set->size) {
util_vma_heap_free(&pool->bo_heap,
(uint64_t) set->offset + TU_POOL_HEAP_OFFSET,
set->size);
}
pool->entry_count--;
vk_object_free(&device->vk, NULL, set);
}
@@ -793,8 +750,6 @@ tu_CreateDescriptorPool(VkDevice _device,
uint64_t host_size = pCreateInfo->maxSets * sizeof(struct tu_descriptor_set);
host_size += dynamic_size;
size += host_size;
} else {
size += sizeof(struct tu_descriptor_pool_entry) * pCreateInfo->maxSets;
}
pool = (struct tu_descriptor_pool *) vk_object_zalloc(
@@ -806,6 +761,12 @@ tu_CreateDescriptorPool(VkDevice _device,
pool->host_memory_base = (uint8_t*)pool + sizeof(struct tu_descriptor_pool);
pool->host_memory_ptr = pool->host_memory_base;
pool->host_memory_end = (uint8_t*)pool + size;
} else {
if (bo_size) {
util_vma_heap_init(&pool->bo_heap, TU_POOL_HEAP_OFFSET,
bo_size + TU_POOL_HEAP_OFFSET);
pool->bo_heap.alloc_high = false;
}
}
if (bo_size) {
@@ -845,6 +806,20 @@ fail_alloc:
return ret;
}
static void
tu_destroy_descriptor_pool_entries(struct tu_device *device,
struct tu_descriptor_pool *pool)
{
list_for_each_entry_safe (struct tu_descriptor_set, set, &pool->desc_sets,
pool_link) {
vk_descriptor_set_layout_unref(&device->vk, &set->layout->vk);
if (!pool->host_memory_base)
tu_descriptor_set_destroy(device, pool, set);
}
list_inithead(&pool->desc_sets);
}
VKAPI_ATTR void VKAPI_CALL
tu_DestroyDescriptorPool(VkDevice _device,
VkDescriptorPool _pool,
@@ -858,18 +833,12 @@ tu_DestroyDescriptorPool(VkDevice _device,
TU_RMV(resource_destroy, device, pool);
list_for_each_entry_safe(struct tu_descriptor_set, set,
&pool->desc_sets, pool_link) {
vk_descriptor_set_layout_unref(&device->vk, &set->layout->vk);
}
if (!pool->host_memory_base) {
for(int i = 0; i < pool->entry_count; ++i) {
tu_descriptor_set_destroy(device, pool, pool->entries[i].set, false);
}
}
tu_destroy_descriptor_pool_entries(device, pool);
if (pool->size) {
if (!pool->host_memory_base)
util_vma_heap_finish(&pool->bo_heap);
if (pool->host_bo)
vk_free2(&device->vk.alloc, pAllocator, pool->host_bo);
else
@@ -887,19 +856,15 @@ tu_ResetDescriptorPool(VkDevice _device,
VK_FROM_HANDLE(tu_device, device, _device);
VK_FROM_HANDLE(tu_descriptor_pool, pool, descriptorPool);
list_for_each_entry_safe(struct tu_descriptor_set, set,
&pool->desc_sets, pool_link) {
vk_descriptor_set_layout_unref(&device->vk, &set->layout->vk);
}
list_inithead(&pool->desc_sets);
tu_destroy_descriptor_pool_entries(device, pool);
if (!pool->host_memory_base) {
for(int i = 0; i < pool->entry_count; ++i) {
tu_descriptor_set_destroy(device, pool, pool->entries[i].set, false);
}
pool->entry_count = 0;
if (!pool->host_memory_base && pool->size) {
util_vma_heap_finish(&pool->bo_heap);
util_vma_heap_init(&pool->bo_heap, TU_POOL_HEAP_OFFSET,
pool->size + TU_POOL_HEAP_OFFSET);
}
pool->entry_count = 0;
pool->current_offset = 0;
pool->host_memory_ptr = pool->host_memory_base;
@@ -967,7 +932,7 @@ tu_FreeDescriptorSets(VkDevice _device,
}
if (set && !pool->host_memory_base)
tu_descriptor_set_destroy(device, pool, set, true);
tu_descriptor_set_destroy(device, pool, set);
}
return VK_SUCCESS;
}
+10 -8
View File
@@ -11,6 +11,7 @@
#include "vk_descriptor_set_layout.h"
#include "tu_sampler.h"
#include "util/vma.h"
/* The hardware supports up to 8 descriptor sets since A7XX.
* Note: This is the maximum across generations, not the maximum for a
@@ -26,6 +27,12 @@
*/
#define MAX_SET_SIZE ((1 << 28) * 4)
/* The vma heap reserves 0 to mean NULL; we have to offset by some amount to
* ensure we can allocate the entire BO without hitting zero. The actual
* amount doesn't matter.
*/
#define TU_POOL_HEAP_OFFSET 32
struct tu_descriptor_set_binding_layout
{
VkDescriptorType type;
@@ -116,6 +123,7 @@ struct tu_descriptor_set
struct tu_descriptor_set_layout *layout;
struct tu_descriptor_pool *pool;
uint32_t offset;
uint32_t size;
uint64_t va;
@@ -132,13 +140,6 @@ struct tu_descriptor_set
VK_DEFINE_NONDISP_HANDLE_CASTS(tu_descriptor_set, base, VkDescriptorSet,
VK_OBJECT_TYPE_DESCRIPTOR_SET)
struct tu_descriptor_pool_entry
{
uint32_t offset;
uint32_t size;
struct tu_descriptor_set *set;
};
struct tu_descriptor_pool
{
struct vk_object_base base;
@@ -154,9 +155,10 @@ struct tu_descriptor_pool
struct list_head desc_sets;
struct util_vma_heap bo_heap;
uint32_t entry_count;
uint32_t max_entry_count;
struct tu_descriptor_pool_entry entries[0];
};
VK_DEFINE_NONDISP_HANDLE_CASTS(tu_descriptor_pool, base, VkDescriptorPool,
VK_OBJECT_TYPE_DESCRIPTOR_POOL)