/** * @file descriptor_builder.c * @author Piotr Krygier (everyonecancode@gmail.com) * @brief * @version 0.1 * @date 2025-03-14 * * @copyright Copyright (c) 2025 * */ #include "descriptor_builder.h" #include #include #include "src/graphics_context.h" #include "src/vulkan_commons.h" #include "utilities/commons.h" #include "utilities/errors_common.h" #include "utilities/linked_list.h" #include "utilities/localization.h" #include "utilities/logger.h" #include "vulkan/vulkan_core.h" #include "vulkan_errors.h" RSE_LINKED_LIST_DEFINE(descriptor_set_layout_list_t, VkDescriptorSetLayoutBinding); struct descriptor_t { uint32_t binding; VkDescriptorType type; uint32_t descriptor_count; VkShaderStageFlags shader_stages; VkSampler* immutable_sampler; }; RSE_LINKED_LIST_DEFINE(descriptor_set_list_t, struct descriptor_t); struct descriptor_set_handle_t { VkDescriptorSet descriptor_sets; VkDescriptorPool descriptor_pool; VkDescriptorSetLayout descriptor_sets_layout; struct descriptor_set_list_t* descriptor_set_list; }; /* FIXME: When new descriptors are introduced to Vulkan, this enum has to be * updated */ enum descriptor_type_id_t { DESCRIPTOR_TYPE_SAMPLER_ID, DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID, DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID, DESCRIPTOR_TYPE_STORAGE_IMAGE_ID, DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID, DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID, DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID, DESCRIPTOR_TYPE_STORAGE_BUFFER_ID, DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID, DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID, DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID, DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID, DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID, DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID, DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID, DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID, DESCRIPTOR_TYPE_MUTABLE_EXT_ID, DESCRIPTOR_TYPE_ID_MAX_ENUM, }; rse_err_t descriptor_initialize_handle(struct descriptor_set_handle_t **descriptor_set_handle) { if (NULL != *descriptor_set_handle) { LOGE(_("Desciptor already initialized\n")); return RSE_ERROR_INVALID_PARAM; } rse_malloc(*descriptor_set_handle, sizeof(struct descriptor_set_handle_t)); (*descriptor_set_handle)->descriptor_set_list = NULL; return RSE_ERROR_NO_ERROR; } rse_err_t descriptor_free_handle(struct descriptor_set_handle_t **descriptor_set_handle) { if (NULL == *descriptor_set_handle) { LOGE(_("Provided NULL handle\n")); return RSE_ERROR_INVALID_PARAM; } RSE_LINKED_LIST_CLEAR((*descriptor_set_handle)->descriptor_set_list); rse_free(*descriptor_set_handle); *descriptor_set_handle = NULL; return RSE_ERROR_NO_ERROR; } rse_err_t descriptor_add_set(struct descriptor_set_handle_t* descriptor_set_handle, uint32_t binding, VkDescriptorType type, uint32_t descriptor_count, VkShaderStageFlags shader_stages, VkSampler* sampler) { struct descriptor_t descriptor_sets = {.binding = binding, .type = type, .descriptor_count = descriptor_count, .shader_stages = shader_stages, .immutable_sampler = sampler}; RSE_LINKED_LIST_PUSH_BACK(descriptor_set_handle->descriptor_set_list, descriptor_sets); return RSE_ERROR_NO_ERROR; } rse_err_t descriptors_build(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle) { size_t iterator = 0U; size_t pool_size_count = 0U; uint32_t pool_max_sets = 0U; uint32_t sets_per_type = 0U; VkDescriptorPool descriptor_pool = VK_NULL_HANDLE; VkDescriptorPoolCreateInfo pool_create_info = {0}; VkDescriptorPoolCreateFlags pool_create_flags = 0U; // TODO: Flags not set anywhere VkDescriptorPoolSize pool_sizes[DESCRIPTOR_TYPE_ID_MAX_ENUM] = {0}; VkDescriptorPoolSize requested_pool_sizes[DESCRIPTOR_TYPE_ID_MAX_ENUM] = {0}; VkDescriptorSetAllocateInfo alloc_info = {0}; VkDescriptorSetLayoutBinding layout_bindings[DESCRIPTOR_TYPE_ID_MAX_ENUM] = {0}; VkDescriptorSetLayoutCreateInfo layout_create_info = {0}; VkDescriptorSetLayout set_layout = VK_NULL_HANDLE; VkDescriptorSet descriptor_sets = VK_NULL_HANDLE; if (NULL == context) { LOGF(_("Provided NULL context\n")); return RSE_ERROR_NULL_POINTER; } if (NULL == descriptor_set_handle) { LOGF(_("Descriptor set handle is null.\n")); return RSE_ERROR_NULL_POINTER; } if (RSE_LINKED_LIST_IS_EMPTY(descriptor_set_handle->descriptor_set_list)) { LOGF(_("Provided empty descriptors list\n")); return RSE_ERROR_NULL_POINTER; } iterator = 0U; RSE_LINKED_LIST_FOREACH(descriptor_set_handle->descriptor_set_list, descriptor) { switch (descriptor->data.type) { case VK_DESCRIPTOR_TYPE_SAMPLER: pool_sizes[DESCRIPTOR_TYPE_SAMPLER_ID].type = VK_DESCRIPTOR_TYPE_SAMPLER; pool_sizes[DESCRIPTOR_TYPE_SAMPLER_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: pool_sizes[DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; pool_sizes[DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: pool_sizes[DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; pool_sizes[DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: pool_sizes[DESCRIPTOR_TYPE_STORAGE_IMAGE_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; pool_sizes[DESCRIPTOR_TYPE_STORAGE_IMAGE_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: pool_sizes[DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER; pool_sizes[DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: pool_sizes[DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER; pool_sizes[DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: pool_sizes[DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; pool_sizes[DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: pool_sizes[DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID].type = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK; pool_sizes[DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID].type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR; pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV: pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID].type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV; pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM: pool_sizes[DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID].type = VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM; pool_sizes[DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM: pool_sizes[DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID].type = VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM; pool_sizes[DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID].descriptorCount++; break; case VK_DESCRIPTOR_TYPE_MUTABLE_EXT: pool_sizes[DESCRIPTOR_TYPE_MUTABLE_EXT_ID].type = VK_DESCRIPTOR_TYPE_MUTABLE_EXT; pool_sizes[DESCRIPTOR_TYPE_MUTABLE_EXT_ID].descriptorCount++; break; default: LOGF(_("Invalid descriptor type provided\n")); return VULKAN_ERROR_UNKNOWN_DESCRIPTOR_TYPE; } layout_bindings[iterator].binding = descriptor->data.binding; layout_bindings[iterator].descriptorCount = descriptor->data.descriptor_count; layout_bindings[iterator].descriptorType = descriptor->data.type; layout_bindings[iterator].pImmutableSamplers = descriptor->data.immutable_sampler; layout_bindings[iterator].stageFlags = descriptor->data.shader_stages; iterator++; } layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_create_info.bindingCount = iterator; layout_create_info.pBindings = layout_bindings; if (VK_SUCCESS != vkCreateDescriptorSetLayout(context->device, &layout_create_info, NULL, &set_layout)) { LOGF(_("Failed to create descriptor sets layout\n")); return VULKAN_ERROR_DESCRIPTOR_SET_LAYOUT_CREATION_FAILED; } /* Another loop. Seems bit too many? TODO: Optimize this later. * What I'm doing here is calculating minimum viable number of pool max sets. This is a bit tricky, as * (unintuitively) we have to select the MINIMUM number of sets, not maximum. This makes sense, but is difficult to * wrap headr around */ RSE_LINKED_LIST_FOREACH(descriptor_set_handle->descriptor_set_list, descriptor) { switch (descriptor->data.type) { case VK_DESCRIPTOR_TYPE_SAMPLER: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_SAMPLER_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_SAMPLED_IMAGE_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_STORAGE_IMAGE_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_INPUT_ATTACHMENT_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM_ID].descriptorCount / descriptor->data.descriptor_count; break; case VK_DESCRIPTOR_TYPE_MUTABLE_EXT: sets_per_type = pool_sizes[DESCRIPTOR_TYPE_MUTABLE_EXT_ID].descriptorCount / descriptor->data.descriptor_count; break; default: LOGF(_("Invalid descriptor type provided\n")); return VULKAN_ERROR_UNKNOWN_DESCRIPTOR_TYPE; } if (pool_max_sets == 0 || pool_max_sets > sets_per_type) { pool_max_sets = sets_per_type; } } /* Allocate pool based on layout */ /* create array of required descriptors */ for (iterator = 0U; iterator < DESCRIPTOR_TYPE_ID_MAX_ENUM; ++iterator) { if (pool_sizes[iterator].descriptorCount > 0) { requested_pool_sizes[pool_size_count].type = pool_sizes[iterator].type; requested_pool_sizes[pool_size_count].descriptorCount = pool_sizes[iterator].descriptorCount; pool_size_count++; } } pool_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_create_info.pNext = NULL; pool_create_info.flags = pool_create_flags; pool_create_info.maxSets = pool_max_sets; pool_create_info.poolSizeCount = pool_size_count; pool_create_info.pPoolSizes = requested_pool_sizes; if (VK_SUCCESS != vkCreateDescriptorPool(context->device, &pool_create_info, NULL, &descriptor_pool)) { LOGF(_("Failed to create descriptor pool\n")); return VULKAN_ERROR_DESCRIPTOR_POOL_CREATION_FAILED; } /* Allocate descriptors from the pool */ alloc_info.descriptorPool = descriptor_pool; alloc_info.descriptorSetCount = pool_max_sets; /* TODO: Always allocating maximum number of sets? This might get weird */ alloc_info.pSetLayouts = &set_layout; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; if (VK_SUCCESS != vkAllocateDescriptorSets(context->device, &alloc_info, &descriptor_sets)) { LOGF(_("Failed to create descriptor sets\n")); return VULKAN_ERROR_DESCRIPTOR_SET_CREATION_FAILED; } descriptor_set_handle->descriptor_pool = descriptor_pool; descriptor_set_handle->descriptor_sets = descriptor_sets; descriptor_set_handle->descriptor_sets_layout = set_layout; context->descriptor_sets = descriptor_set_handle; return RSE_ERROR_NO_ERROR; } rse_err_t descriptor_get_layout(struct descriptor_set_handle_t* descriptor_set_handle, VkDescriptorSetLayout* descriptor_sets_layout) { if (NULL == descriptor_set_handle->descriptor_sets_layout) { LOGF(_("Requested descriptor set layout is NULL\n")); return RSE_ERROR_NULL_POINTER; } *descriptor_sets_layout = descriptor_set_handle->descriptor_sets_layout; return RSE_ERROR_NO_ERROR; } rse_err_t descriptor_get_vkhandle(struct descriptor_set_handle_t* descriptor_set_handle, VkDescriptorSet* descriptor_set) { if (NULL == descriptor_set_handle) { LOGF(_("Provided handle is NULL\n")); return RSE_ERROR_NULL_POINTER; } *descriptor_set = descriptor_set_handle->descriptor_sets; return RSE_ERROR_NO_ERROR; } rse_err_t descriptor_attach_image(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle, uint16_t texture_id, VkSampler sampler, uint32_t binding) { VkDescriptorImageInfo image_info = {0}; VkWriteDescriptorSet write_set = {0}; image_info.sampler = sampler; image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_info.imageView = context->texture_images[texture_id].image_view; write_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_set.pImageInfo = &image_info; write_set.dstSet = descriptor_set_handle->descriptor_sets; write_set.dstBinding = binding; write_set.dstArrayElement = 0U; write_set.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; //TODO: Maybe not only samplers? write_set.descriptorCount = 1; vkUpdateDescriptorSets(context->device, 1U, &write_set, 0, NULL); return RSE_ERROR_NO_ERROR; } rse_err_t descriptor_attach_buffer(struct graphics_context_t* context, struct descriptor_set_handle_t* descriptor_set_handle, struct vulkan_buffer_t* buffer, uint32_t binding, VkDescriptorType type) { VkDescriptorBufferInfo buffer_info = {0}; VkWriteDescriptorSet write_set = {0}; buffer_info.buffer = buffer->buffer; buffer_info.offset = 0U; buffer_info.range = buffer->allocated_size; write_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_set.pBufferInfo = &buffer_info; write_set.dstSet = descriptor_set_handle->descriptor_sets; write_set.dstBinding = binding; write_set.dstArrayElement = 0U; write_set.descriptorType = type; write_set.descriptorCount = 1; vkUpdateDescriptorSets(context->device, 1U, &write_set, 0, NULL); return RSE_ERROR_NO_ERROR; } void destroy_descriptors(struct graphics_context_t* context) { vkDestroyDescriptorSetLayout(context->device, context->descriptor_sets->descriptor_sets_layout, NULL); vkDestroyDescriptorPool(context->device, context->descriptor_sets->descriptor_pool, NULL); }