venus: extend async descriptor set alloc coverage

Previously asynchronous descriptor set allocation is only enabled when
the VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT bit is not set.
However, some engine would use that bit but alloc/free with identical
descriptor set layout. So this change extends the async set alloc to
cover that since the spec has guaranteed no fragmentation there.
Besides, a pool before any descriptor set free is also considered w/o
fragmentation. so this change extends to cover here as well. Both
would also help with dEQP run time since all descriptor pools involved
are with that bit set.

Signed-off-by: Yiwei Zhang <zzyiwei@chromium.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/33966>
This commit is contained in:
Yiwei Zhang 2025-03-10 01:10:48 -07:00 committed by Marge Bot
parent 14101ff948
commit 0543c3a886
2 changed files with 103 additions and 19 deletions

View file

@ -316,15 +316,25 @@ vn_CreateDescriptorPool(VkDevice device,
vk_find_struct_const(pCreateInfo->pNext,
MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT);
/* Without VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, the set
* allocation must not fail due to a fragmented pool per spec. In this
* case, set allocation can be asynchronous with pool resource tracking.
/* Per spec:
*
* If a descriptor pool has not had any descriptor sets freed since it was
* created or most recently reset then fragmentation must not cause an
* allocation failure (note that this is always the case for a pool created
* without the VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT bit set).
*
* Additionally, if all sets allocated from the pool since it was created
* or most recently reset use the same number of descriptors (of each type)
* and the requested allocation also uses that same number of descriptors
* (of each type), then fragmentation must not cause an allocation failure.
*/
pool->async_set_allocation =
!VN_PERF(NO_ASYNC_SET_ALLOC) &&
!(pCreateInfo->flags &
VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT);
pool->initial_state = pool->current_state =
VN_PERF(NO_ASYNC_SET_ALLOC) ? VN_ASYNC_SET_ALLOC_NONE
: pCreateInfo->flags & VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT
? VN_ASYNC_SET_ALLOC_SAME_ALLOC
: VN_ASYNC_SET_ALLOC_ALWAYS;
pool->last_layout = VK_NULL_HANDLE;
pool->max.set_count = pCreateInfo->maxSets;
if (iub_info)
@ -411,6 +421,11 @@ vn_DestroyDescriptorPool(VkDevice device,
&pool->descriptor_sets, head)
vn_descriptor_set_destroy(dev, set, alloc);
if (pool->last_layout != VK_NULL_HANDLE) {
assert(pool->current_state == VN_ASYNC_SET_ALLOC_SAME_ALLOC);
vn_descriptor_set_layout_unref(dev, pool->last_layout);
}
vn_object_base_fini(&pool->base);
vk_free(alloc, pool);
}
@ -456,7 +471,7 @@ vn_descriptor_pool_alloc_descriptors(
const struct vn_descriptor_set_layout *layout,
uint32_t last_binding_descriptor_count)
{
assert(pool->async_set_allocation);
assert(pool->current_state != VN_ASYNC_SET_ALLOC_NONE);
if (pool->used.set_count == pool->max.set_count)
return false;
@ -525,7 +540,7 @@ vn_descriptor_pool_free_descriptors(
const struct vn_descriptor_set_layout *layout,
uint32_t last_binding_descriptor_count)
{
assert(pool->async_set_allocation);
assert(pool->current_state != VN_ASYNC_SET_ALLOC_NONE);
for (uint32_t i = 0; i <= layout->last_binding; i++) {
const uint32_t count = i == layout->last_binding
@ -551,7 +566,7 @@ vn_descriptor_pool_free_descriptors(
static inline void
vn_descriptor_pool_reset_descriptors(struct vn_descriptor_pool *pool)
{
assert(pool->async_set_allocation);
assert(pool->current_state != VN_ASYNC_SET_ALLOC_NONE);
memset(&pool->used, 0, sizeof(pool->used));
@ -577,7 +592,15 @@ vn_ResetDescriptorPool(VkDevice device,
&pool->descriptor_sets, head)
vn_descriptor_set_destroy(dev, set, alloc);
if (pool->async_set_allocation)
if (pool->last_layout != VK_NULL_HANDLE) {
assert(pool->current_state == VN_ASYNC_SET_ALLOC_SAME_ALLOC);
vn_descriptor_set_layout_unref(dev, pool->last_layout);
pool->last_layout = VK_NULL_HANDLE;
}
pool->current_state = pool->initial_state;
if (pool->current_state != VN_ASYNC_SET_ALLOC_NONE)
vn_descriptor_pool_reset_descriptors(pool);
return VK_SUCCESS;
@ -612,6 +635,19 @@ vn_AllocateDescriptorSets(VkDevice device,
for (; i < pAllocateInfo->descriptorSetCount; i++) {
struct vn_descriptor_set_layout *layout =
vn_descriptor_set_layout_from_handle(pAllocateInfo->pSetLayouts[i]);
bool same_alloc = true;
if (pool->current_state == VN_ASYNC_SET_ALLOC_SAME_ALLOC) {
if (pool->last_layout == VK_NULL_HANDLE)
pool->last_layout = vn_descriptor_set_layout_ref(dev, layout);
/* If a different set layout is used, set allocations are not
* considered to be the same alloc for simplicity, though we could
* also compare the exact descriptor types and counts.
*/
if (pool->last_layout != layout)
same_alloc = false;
}
/* 14.2.3. Allocation of Descriptor Sets
*
@ -625,9 +661,25 @@ vn_AllocateDescriptorSets(VkDevice device,
layout->bindings[layout->last_binding].count;
} else if (variable_info) {
last_binding_descriptor_count = variable_info->pDescriptorCounts[i];
/* If variable descriptor count is used, set allocations are not
* considered to be the same alloc for simplicity, though we could
* also track the last_binding_descriptor_count used.
*/
if (pool->current_state == VN_ASYNC_SET_ALLOC_SAME_ALLOC)
same_alloc = false;
}
if (pool->async_set_allocation &&
if (!same_alloc) {
assert(pool->current_state == VN_ASYNC_SET_ALLOC_SAME_ALLOC &&
pool->last_layout != VK_NULL_HANDLE);
pool->current_state = VN_ASYNC_SET_ALLOC_BEFORE_FREE;
vn_descriptor_set_layout_unref(dev, pool->last_layout);
pool->last_layout = VK_NULL_HANDLE;
}
if (pool->current_state != VN_ASYNC_SET_ALLOC_NONE &&
!vn_descriptor_pool_alloc_descriptors(
pool, layout, last_binding_descriptor_count)) {
result = VK_ERROR_OUT_OF_POOL_MEMORY;
@ -638,7 +690,7 @@ vn_AllocateDescriptorSets(VkDevice device,
vk_zalloc(alloc, sizeof(*set), VN_DEFAULT_ALIGN,
VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
if (!set) {
if (pool->async_set_allocation) {
if (pool->current_state != VN_ASYNC_SET_ALLOC_NONE) {
vn_descriptor_pool_free_descriptors(
pool, layout, last_binding_descriptor_count);
}
@ -670,7 +722,7 @@ vn_AllocateDescriptorSets(VkDevice device,
pDescriptorSets[i] = vn_descriptor_set_to_handle(set);
}
if (pool->async_set_allocation) {
if (pool->current_state != VN_ASYNC_SET_ALLOC_NONE) {
vn_async_vkAllocateDescriptorSets(dev->primary_ring, device,
pAllocateInfo, pDescriptorSets);
} else {
@ -687,7 +739,7 @@ fail:
struct vn_descriptor_set *set =
vn_descriptor_set_from_handle(pDescriptorSets[j]);
if (pool->async_set_allocation) {
if (pool->current_state != VN_ASYNC_SET_ALLOC_NONE) {
vn_descriptor_pool_free_descriptors(
pool, set->layout, set->last_binding_descriptor_count);
}
@ -712,7 +764,7 @@ vn_FreeDescriptorSets(VkDevice device,
vn_descriptor_pool_from_handle(descriptorPool);
const VkAllocationCallbacks *alloc = &pool->allocator;
assert(!pool->async_set_allocation);
assert(pool->current_state != VN_ASYNC_SET_ALLOC_ALWAYS);
vn_async_vkFreeDescriptorSets(dev->primary_ring, device, descriptorPool,
descriptorSetCount, pDescriptorSets);
@ -724,6 +776,14 @@ vn_FreeDescriptorSets(VkDevice device,
if (!set)
continue;
if (pool->current_state == VN_ASYNC_SET_ALLOC_SAME_ALLOC) {
vn_descriptor_pool_free_descriptors(
pool, set->layout, set->last_binding_descriptor_count);
} else if (pool->current_state == VN_ASYNC_SET_ALLOC_BEFORE_FREE) {
assert(pool->last_layout == VK_NULL_HANDLE);
pool->current_state = VN_ASYNC_SET_ALLOC_NONE;
}
vn_descriptor_set_destroy(dev, set, alloc);
}

View file

@ -69,16 +69,40 @@ struct vn_descriptor_pool_state_mutable {
BITSET_DECLARE(types, VN_NUM_DESCRIPTOR_TYPES);
};
enum vn_async_set_alloc_state {
/* Must do synchronous set alloc. It comes from either no_async_set_alloc
* perf option or transition from below conditional states.
*/
VN_ASYNC_SET_ALLOC_NONE,
/* Can still do async set alloc, but the pool might become fragmented after
* freeing up any descriptor set and that will change the state to NONE.
*/
VN_ASYNC_SET_ALLOC_BEFORE_FREE,
/* Can still do async set alloc, and the sets allocated are with the same
* number of descriptors and types. Allocating a set with different number
* of descriptors or types will change the state to BEFORE_FREE.
*/
VN_ASYNC_SET_ALLOC_SAME_ALLOC,
/* Will always do async set alloc, since the pool is not created with
* VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT.
*/
VN_ASYNC_SET_ALLOC_ALWAYS,
};
struct vn_descriptor_pool {
struct vn_object_base base;
VkAllocationCallbacks allocator;
bool async_set_allocation;
struct list_head descriptor_sets;
enum vn_async_set_alloc_state initial_state;
enum vn_async_set_alloc_state current_state;
struct vn_descriptor_set_layout *last_layout;
struct vn_descriptor_pool_state max;
struct vn_descriptor_pool_state used;
struct list_head descriptor_sets;
uint32_t mutable_states_count;
struct vn_descriptor_pool_state_mutable *mutable_states;
};