diff --git a/src/broadcom/vulkan/v3dv_descriptor_set.c b/src/broadcom/vulkan/v3dv_descriptor_set.c index f2cce59cac0..e6187870aba 100644 --- a/src/broadcom/vulkan/v3dv_descriptor_set.c +++ b/src/broadcom/vulkan/v3dv_descriptor_set.c @@ -25,6 +25,35 @@ #include "v3dv_private.h" +/* + * Returns how much space a given descriptor type needs on a bo (GPU + * memory). + */ +static uint32_t +descriptor_bo_size(VkDescriptorType type) +{ + switch(type) { + default: + return 0; + } +} + +/* + * For a given descriptor defined by the descriptor_set it belongs, its + * binding layout, and array_index, it returns the map region assigned to it + * from the descriptor pool bo. + */ +static void* +descriptor_bo_map(struct v3dv_descriptor_set *set, + const struct v3dv_descriptor_set_binding_layout *binding_layout, + uint32_t array_index) +{ + assert(descriptor_bo_size(binding_layout->type) > 0); + return set->pool->bo->map + + set->base_offset + binding_layout->descriptor_offset + + array_index * descriptor_bo_size(binding_layout->type); +} + static bool descriptor_type_is_dynamic(VkDescriptorType type) { @@ -230,13 +259,18 @@ v3dv_CreateDescriptorPool(VkDevice _device, { V3DV_FROM_HANDLE(v3dv_device, device, _device); struct v3dv_descriptor_pool *pool; + /* size is for the vulkan object descriptor pool. The final size would + * depend on some of FREE_DESCRIPTOR flags used + */ uint64_t size = sizeof(struct v3dv_descriptor_pool); + /* bo_size is for the descriptor related info that we need to have on a GPU + * address (so on v3dv_bo_alloc allocated memory), like for example the + * texture sampler state. Note that not all the descriptors use it + */ + uint32_t bo_size = 0; uint32_t descriptor_count = 0; for (unsigned i = 0; i < pCreateInfo->poolSizeCount; ++i) { - if (pCreateInfo->pPoolSizes[i].type != VK_DESCRIPTOR_TYPE_SAMPLER) - descriptor_count += pCreateInfo->pPoolSizes[i].descriptorCount; - /* Verify supported descriptor type */ switch(pCreateInfo->pPoolSizes[i].type) { case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: @@ -251,6 +285,10 @@ v3dv_CreateDescriptorPool(VkDevice _device, unreachable("Unimplemented descriptor type"); break; } + + descriptor_count += pCreateInfo->pPoolSizes[i].descriptorCount; + bo_size += descriptor_bo_size(pCreateInfo->pPoolSizes[i].type) * + pCreateInfo->pPoolSizes[i].descriptorCount; } if (!(pCreateInfo->flags & VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT)) { @@ -278,24 +316,45 @@ v3dv_CreateDescriptorPool(VkDevice _device, pool->max_entry_count = pCreateInfo->maxSets; + if (bo_size > 0) { + pool->bo = v3dv_bo_alloc(device, bo_size, "descriptor pool bo"); + if (!pool->bo) + goto out_of_device_memory; + + bool ok = v3dv_bo_map(device, pool->bo, pool->bo->size); + if (!ok) + goto out_of_device_memory; + + pool->current_offset = 0; + } else { + pool->bo = NULL; + } + *pDescriptorPool = v3dv_descriptor_pool_to_handle(pool); return VK_SUCCESS; + + out_of_device_memory: + vk_free2(&device->alloc, pAllocator, pool); + return vk_error(device->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY); } static void descriptor_set_destroy(struct v3dv_device *device, struct v3dv_descriptor_pool *pool, - struct v3dv_descriptor_set *set) + struct v3dv_descriptor_set *set, + bool free_bo) { assert(!pool->host_memory_base); - for (uint32_t i = 0; i < pool->entry_count; i++) { - if (pool->entries[i].set == set) { - memmove(&pool->entries[i], &pool->entries[i+1], - sizeof(pool->entries[i]) * (pool->entry_count - i - 1)); - --pool->entry_count; - break; + if (free_bo && !pool->host_memory_base) { + for (uint32_t i = 0; i < pool->entry_count; i++) { + if (pool->entries[i].set == set) { + memmove(&pool->entries[i], &pool->entries[i+1], + sizeof(pool->entries[i]) * (pool->entry_count - i - 1)); + --pool->entry_count; + break; + } } } vk_free2(&device->alloc, NULL, set); @@ -314,10 +373,15 @@ v3dv_DestroyDescriptorPool(VkDevice _device, if (!pool->host_memory_base) { for(int i = 0; i < pool->entry_count; ++i) { - descriptor_set_destroy(device, pool, pool->entries[i].set); + descriptor_set_destroy(device, pool, pool->entries[i].set, false); } } + if (pool->bo) { + v3dv_bo_free(device, pool->bo); + pool->bo = NULL; + } + vk_free2(&device->alloc, pAllocator, pool); } @@ -331,12 +395,13 @@ v3dv_ResetDescriptorPool(VkDevice _device, if (!pool->host_memory_base) { for(int i = 0; i < pool->entry_count; ++i) { - descriptor_set_destroy(device, pool, pool->entries[i].set); + descriptor_set_destroy(device, pool, pool->entries[i].set, false); } } pool->entry_count = 0; pool->host_memory_ptr = pool->host_memory_base; + pool->current_offset = 0; return VK_SUCCESS; } @@ -439,6 +504,7 @@ v3dv_CreateDescriptorSetLayout(VkDevice _device, set_layout->binding_count = max_binding + 1; set_layout->flags = pCreateInfo->flags; set_layout->shader_stages = 0; + set_layout->bo_size = 0; uint32_t descriptor_count = 0; uint32_t dynamic_offset_count = 0; @@ -492,6 +558,11 @@ v3dv_CreateDescriptorSetLayout(VkDevice _device, * descriptor data. */ set_layout->shader_stages |= binding->stageFlags; + + set_layout->binding[binding_number].descriptor_offset = set_layout->bo_size; + set_layout->bo_size += + descriptor_bo_size(set_layout->binding[binding_number].type) * + binding->descriptorCount; } vk_free2(&device->alloc, pAllocator, bindings); @@ -526,9 +597,8 @@ descriptor_set_create(struct v3dv_device *device, { struct v3dv_descriptor_set *set; uint32_t descriptor_count = layout->descriptor_count; - unsigned range_offset = sizeof(struct v3dv_descriptor_set) + + unsigned mem_size = sizeof(struct v3dv_descriptor_set) + sizeof(struct v3dv_descriptor) * descriptor_count; - unsigned mem_size = range_offset; if (pool->host_memory_base) { if (pool->host_memory_end - pool->host_memory_ptr < mem_size) @@ -549,13 +619,58 @@ descriptor_set_create(struct v3dv_device *device, set->layout = layout; - if (!pool->host_memory_base && pool->entry_count == pool->max_entry_count) { - vk_free2(&device->alloc, NULL, set); - return vk_error(device->instance, VK_ERROR_OUT_OF_POOL_MEMORY); + /* FIXME: VK_EXT_descriptor_indexing introduces + * VARIABLE_DESCRIPTOR_LAYOUT_COUNT. That would affect the layout_size used + * below for bo allocation + */ + + uint32_t offset = 0; + uint32_t index = pool->entry_count; + + if (layout->bo_size) { + if (!pool->host_memory_base && pool->entry_count == pool->max_entry_count) { + vk_free2(&device->alloc, NULL, set); + return vk_error(device->instance, VK_ERROR_OUT_OF_POOL_MEMORY); + } + + /* We first try to allocate linearly fist, so that we don't spend time + * looking for gaps if the app only allocates & resets via the pool. + * + * If that fails, we try to find a gap from previously freed subregions + * iterating through the descriptor pool entries. Note that we are not + * doing that if we have a pool->host_memory_base. We only have that if + * VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT is not set, so in + * that case the user can't free subregions, so it doesn't make sense to + * even try (or track those subregions). + */ + if (pool->current_offset + layout->bo_size <= pool->bo->size) { + offset = pool->current_offset; + pool->current_offset += layout->bo_size; + } else if (!pool->host_memory_base) { + for (index = 0; index < pool->entry_count; index++) { + if (pool->entries[index].offset - offset >= layout->bo_size) + break; + offset = pool->entries[index].offset + pool->entries[index].size; + } + if (pool->bo->size - offset < layout->bo_size) { + vk_free2(&device->alloc, NULL, set); + return vk_error(device->instance, VK_ERROR_OUT_OF_POOL_MEMORY); + } + memmove(&pool->entries[index + 1], &pool->entries[index], + sizeof(pool->entries[0]) * (pool->entry_count - index)); + } else { + assert(pool->host_memory_base); + vk_free2(&device->alloc, NULL, set); + return vk_error(device->instance, VK_ERROR_OUT_OF_POOL_MEMORY); + } + + set->base_offset = offset; } if (!pool->host_memory_base) { - pool->entries[pool->entry_count].set = set; + pool->entries[index].set = set; + pool->entries[index].offset = offset; + pool->entries[index].size = layout->bo_size; pool->entry_count++; } @@ -609,14 +724,29 @@ v3dv_FreeDescriptorSets(VkDevice _device, for (uint32_t i = 0; i < count; i++) { V3DV_FROM_HANDLE(v3dv_descriptor_set, set, pDescriptorSets[i]); - if (set && !pool->host_memory_base) - descriptor_set_destroy(device, pool, set); + descriptor_set_destroy(device, pool, set, true); } return VK_SUCCESS; } +static void +descriptor_bo_copy(struct v3dv_descriptor_set *dst_set, + const struct v3dv_descriptor_set_binding_layout *dst_binding_layout, + uint32_t dst_array_index, + struct v3dv_descriptor_set *src_set, + const struct v3dv_descriptor_set_binding_layout *src_binding_layout, + uint32_t src_array_index) +{ + assert(dst_binding_layout->type == src_binding_layout->type); + + void *dst_map = descriptor_bo_map(dst_set, dst_binding_layout, dst_array_index); + void *src_map = descriptor_bo_map(src_set, src_binding_layout, src_array_index); + + memcpy(dst_map, src_map, descriptor_bo_size(src_binding_layout->type)); +} + void v3dv_UpdateDescriptorSets(VkDevice _device, uint32_t descriptorWriteCount, @@ -711,6 +841,14 @@ v3dv_UpdateDescriptorSets(VkDevice _device, *dst_descriptor = *src_descriptor; dst_descriptor++; src_descriptor++; + + if (descriptor_bo_size(src_binding_layout->type) > 0) { + descriptor_bo_copy(dst_set, dst_binding_layout, + j + copyset->dstArrayElement, + src_set, src_binding_layout, + j + copyset->srcArrayElement); + } + } } } diff --git a/src/broadcom/vulkan/v3dv_private.h b/src/broadcom/vulkan/v3dv_private.h index b59a62cc6b7..783ba8bebbf 100644 --- a/src/broadcom/vulkan/v3dv_private.h +++ b/src/broadcom/vulkan/v3dv_private.h @@ -885,6 +885,14 @@ struct v3dv_cmd_buffer_state { } query; }; +/* The following struct represents the info from a descriptor that we store on + * the host memory. They are mostly links to other existing vulkan objects, + * like the image_view in order to access to swizzle info, or the buffer used + * for a UBO/SSBO, for example. + * + * FIXME: revisit if makes sense to just move everything that would be needed + * from a descriptor to the bo. + */ struct v3dv_descriptor { VkDescriptorType type; @@ -1158,12 +1166,37 @@ struct vpm_config { uint32_t gs_width; }; +/* We are using the descriptor pool entry for two things: + * * Track the allocated sets, so we can properly free it if needed + * * Track the suballocated pool bo regions, so if some descriptor set is + * freed, the gap could be reallocated later. + * + * Those only make sense if the pool was not created with the flag + * VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT + */ struct v3dv_descriptor_pool_entry { struct v3dv_descriptor_set *set; + /* Offset and size of the subregion allocated for this entry from the + * pool->bo + */ + uint32_t offset; + uint32_t size; }; struct v3dv_descriptor_pool { + struct v3dv_bo *bo; + /* Current offset at the descriptor bo. 0 means that we didn't use it for + * any descriptor. If the descriptor bo is NULL, current offset is + * meaningless + */ + uint32_t current_offset; + + /* If VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT is not set the + * descriptor sets are handled as a whole as pool memory and handled by the + * following pointers. If set, they are not used, and individually + * descriptor sets are allocated/freed. + */ uint8_t *host_memory_base; uint8_t *host_memory_ptr; uint8_t *host_memory_end; @@ -1178,6 +1211,9 @@ struct v3dv_descriptor_set { const struct v3dv_descriptor_set_layout *layout; + /* Offset relative to the descriptor pool bo for this set */ + uint32_t base_offset; + /* The descriptors below can be indexed (set/binding) using the set_layout */ struct v3dv_descriptor descriptors[0]; @@ -1189,11 +1225,17 @@ struct v3dv_descriptor_set_binding_layout { /* Number of array elements in this binding */ uint32_t array_size; + /* Index into the flattend descriptor set */ uint32_t descriptor_index; uint32_t dynamic_offset_count; uint32_t dynamic_offset_index; + /* Offset into the descriptor set where this descriptor lives (final offset + * on the descriptor bo need to take into account set->base_offset) + */ + uint32_t descriptor_offset; + /* Offset in the v3dv_descriptor_set_layout of the immutable samplers, or 0 * if there are no immutable samplers. */ @@ -1206,8 +1248,9 @@ struct v3dv_descriptor_set_layout { /* Number of bindings in this descriptor set */ uint32_t binding_count; - /* Total size of the descriptor set with room for all array entries */ - uint32_t size; + /* Total bo size needed for this descriptor set + */ + uint32_t bo_size; /* Shader stages affected by this descriptor set */ uint16_t shader_stages;