diff --git a/src/renderer/vk/bindless_registry.c b/src/renderer/vk/bindless_registry.c new file mode 100644 index 0000000..fa8df69 --- /dev/null +++ b/src/renderer/vk/bindless_registry.c @@ -0,0 +1,481 @@ +#include "bindless_registry.h" +#include "device.h" + +#include +#include + +RT_CVAR_UI(r_VkBindlessUniformBufferDescriptors, + "Number of uniform buffer descriptors to create. (Default: 1024)", + 1024); +RT_CVAR_UI(r_VkBindlessStorageBufferDescriptors, + "Number of storage buffer descriptors to create. (Default: 1024)", + 1024); +RT_CVAR_UI(r_VkBindlessSampledImageDescriptors, + "Number of sampled image descriptors to create. (Default: 1024)", + 1024); +RT_CVAR_UI(r_VkBindlessStorageImageDescriptors, + "Number of storage image descriptors to create. (Default: 1024)", + 1024); +RT_CVAR_UI(r_VkBindlessSamplerDescriptors, + "Number of sampler descriptors to create. (Default: 128)", + 128); + +static size_t ReleaseListCapacity(void) { + return (size_t)r_VkBindlessUniformBufferDescriptors.ui + + (size_t)r_VkBindlessStorageBufferDescriptors.ui + + (size_t)r_VkBindlessSampledImageDescriptors.ui + + (size_t)r_VkBindlessStorageBufferDescriptors.ui + + (size_t)r_VkBindlessSamplerDescriptors.ui; +} + +static rt_vk_bindless_reuse_stack AllocReuseStack(uint32_t max_descriptors) { + rt_vk_bindless_reuse_stack stack; + stack.indices = calloc(max_descriptors, sizeof(uint32_t)); + stack.size = 0; + return stack; +} + +rt_create_vk_bindless_registry_result rtCreateVkBindlessRegistry(rt_vk_device *dev) { + /* Check that required features are supported */ + VkPhysicalDeviceDescriptorIndexingFeatures indexing_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES}; + VkPhysicalDeviceFeatures2 features = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .pNext = &indexing_features}; + vkGetPhysicalDeviceFeatures2(dev->phys_device, &features); + + if (!indexing_features.shaderSampledImageArrayNonUniformIndexing || + !indexing_features.descriptorBindingSampledImageUpdateAfterBind || + !indexing_features.shaderUniformBufferArrayNonUniformIndexing || + !indexing_features.descriptorBindingUniformBufferUpdateAfterBind || + !indexing_features.shaderStorageBufferArrayNonUniformIndexing || + !indexing_features.descriptorBindingStorageBufferUpdateAfterBind || + !indexing_features.shaderStorageImageArrayNonUniformIndexing || + !indexing_features.descriptorBindingStorageImageUpdateAfterBind) { + +#define TO_STRING(_f) ((_f) ? "SUPPORTED" : "NOT SUPPORTED") + rtReportError("VK", + "Required bindless features are not supported:\n" + " shaderSampledImageArrayNonUniformIndexing: %s\n" + " descriptorBindingSampledImageUpdateAfterBind: %s\n" + " shaderUniformBufferArrayNonUniformIndexing: %s\n" + " descriptorBindingUniformBufferUpdateAfterBind: %s\n" + " shaderStorageBufferArrayNonUniformIndexing: %s\n" + " descriptorBindingStorageBufferUpdateAfterBind: %s\n" + " shaderStorageImageArrayNonUniformIndexing: %s\n" + " descriptorBindingStorageImageUpdateAfterBind: %s", + TO_STRING(indexing_features.shaderSampledImageArrayNonUniformIndexing), + TO_STRING(indexing_features.descriptorBindingSampledImageUpdateAfterBind), + TO_STRING(indexing_features.shaderUniformBufferArrayNonUniformIndexing), + TO_STRING(indexing_features.descriptorBindingUniformBufferUpdateAfterBind), + TO_STRING(indexing_features.shaderStorageBufferArrayNonUniformIndexing), + TO_STRING(indexing_features.descriptorBindingStorageBufferUpdateAfterBind), + TO_STRING(indexing_features.shaderStorageImageArrayNonUniformIndexing), + TO_STRING(indexing_features.descriptorBindingStorageImageUpdateAfterBind)); + + /* NOTE(kevin): In the future we may fall back on a non-bindless renderer. But + * for now, we just error out */ + return (rt_create_vk_bindless_registry_result){.result = RT_NOT_SUPPORTED}; + } + + rt_vk_bindless_registry bindless_registry = {.dev = dev, + .uniform_buffer_binding = 0, + .storage_buffer_binding = 1, + .sampled_image_binding = 2, + .storage_image_binding = 3, + .sampler_binding = 4}; + + /* Create the descriptor set layout */ + { + VkDescriptorSetLayoutBinding bindings[5]; + VkDescriptorBindingFlags flags[5]; + VkDescriptorType types[5] = { + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + VK_DESCRIPTOR_TYPE_SAMPLER, + }; + uint32_t counts[5] = { + r_VkBindlessUniformBufferDescriptors.ui, + r_VkBindlessStorageBufferDescriptors.ui, + r_VkBindlessSampledImageDescriptors.ui, + r_VkBindlessStorageImageDescriptors.ui, + r_VkBindlessSamplerDescriptors.ui, + }; + + for (int i = 0; i < 5; ++i) { + bindings[i].binding = i; + bindings[i].descriptorType = types[i]; + bindings[i].descriptorCount = counts[i]; + bindings[i].stageFlags = VK_SHADER_STAGE_ALL, bindings[i].pImmutableSamplers = NULL; + flags[i] = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT; + } + + VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO, + .pBindingFlags = &flags[0], + .bindingCount = 5}; + VkDescriptorSetLayoutCreateInfo layout_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = &binding_flags, + .flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT, + .pBindings = &bindings[0], + .bindingCount = 5, + }; + if (vkCreateDescriptorSetLayout(dev->device, + &layout_info, + dev->alloc_cb, + &bindless_registry.bindless_set_layout) != VK_SUCCESS) { + rtReportError("VK", "Failed to create the bindless descriptor set layout."); + return (rt_create_vk_bindless_registry_result){.result = RT_UNKNOWN_ERROR}; + } + } + + /* Create the descriptor pool */ + { + VkDescriptorPoolSize pool_sizes[5] = { + {.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = r_VkBindlessUniformBufferDescriptors.ui}, + {.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = r_VkBindlessStorageBufferDescriptors.ui}, + { .type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .descriptorCount = r_VkBindlessSampledImageDescriptors.ui }, + { .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .descriptorCount = r_VkBindlessStorageImageDescriptors.ui }, + { .type = VK_DESCRIPTOR_TYPE_SAMPLER, + .descriptorCount = r_VkBindlessSamplerDescriptors.ui }, + }; + + VkDescriptorPoolCreateInfo pool_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT, + .maxSets = 1, + .pPoolSizes = &pool_sizes[0], + .poolSizeCount = 5}; + if (vkCreateDescriptorPool(dev->device, + &pool_info, + dev->alloc_cb, + &bindless_registry.bindless_set_pool) != VK_SUCCESS) { + rtReportError("VK", "Failed to create the bindless descriptor pool."); + vkDestroyDescriptorSetLayout(dev->device, + bindless_registry.bindless_set_layout, + dev->alloc_cb); + return (rt_create_vk_bindless_registry_result){.result = RT_UNKNOWN_ERROR}; + } + } + + /* Allocate the global descriptor set */ + { + VkDescriptorSetAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = bindless_registry.bindless_set_pool, + .descriptorSetCount = 1, + .pSetLayouts = &bindless_registry.bindless_set_layout}; + if (vkAllocateDescriptorSets(dev->device, &alloc_info, &bindless_registry.bindless_set) != + VK_SUCCESS) { + rtReportError("VK", "Failed to allocate the bindless descriptor set."); + vkDestroyDescriptorPool(dev->device, + bindless_registry.bindless_set_pool, + dev->alloc_cb); + vkDestroyDescriptorSetLayout(dev->device, + bindless_registry.bindless_set_layout, + dev->alloc_cb); + return (rt_create_vk_bindless_registry_result){.result = RT_UNKNOWN_ERROR}; + } + } + + /* Prepare the management data */ + bindless_registry.uniform_buffer_reuse_stack = + AllocReuseStack(r_VkBindlessUniformBufferDescriptors.ui); + bindless_registry.storage_buffer_reuse_stack = + AllocReuseStack(r_VkBindlessStorageBufferDescriptors.ui); + bindless_registry.sampled_image_reuse_stack = + AllocReuseStack(r_VkBindlessSampledImageDescriptors.ui); + bindless_registry.storage_image_reuse_stack = + AllocReuseStack(r_VkBindlessStorageImageDescriptors.ui); + bindless_registry.sampler_reuse_stack = AllocReuseStack(r_VkBindlessSamplerDescriptors.ui); + + bindless_registry.release_list = + calloc(ReleaseListCapacity(), sizeof(rt_vk_bindless_release_list_entry)); + bindless_registry.release_list_length = 0; + + bindless_registry.mutex = rtCreateMutex(); + + return (rt_create_vk_bindless_registry_result){.result = RT_SUCCESS, + .bindless_registry = bindless_registry}; +} + +void rtDestroyVkBindlessRegistry(rt_vk_bindless_registry *registry) { + rtDestroyMutex(registry->mutex); + free(registry->release_list); + free(registry->uniform_buffer_reuse_stack.indices); + free(registry->storage_buffer_reuse_stack.indices); + free(registry->sampled_image_reuse_stack.indices); + free(registry->storage_image_reuse_stack.indices); + free(registry->sampler_reuse_stack.indices); + vkDestroyDescriptorPool(registry->dev->device, + registry->bindless_set_pool, + registry->dev->alloc_cb); + vkDestroyDescriptorSetLayout(registry->dev->device, + registry->bindless_set_layout, + registry->dev->alloc_cb); +} + +RT_INLINE static rt_vk_bindless_handle MakeBindlessHandle(rt_vk_bindless_handle_type type, + uint32_t index) { + RT_ASSERT(index < (1u << 29), ""); + rt_vk_bindless_handle handle = {.value = index | (type << 29)}; + return handle; +} + +static uint32_t +AcquireSlot(rt_vk_bindless_reuse_stack *reuse_stack, uint32_t *next_of_type, rt_cvar *max_cvar) { + uint32_t index = UINT32_MAX; + if (*next_of_type < max_cvar->ui) { + index = *next_of_type; + *next_of_type += 1; + } else if (reuse_stack->size > 0) { + index = reuse_stack->indices[--reuse_stack->size]; + } else { + rtLog("VK", "No available descriptor set slots for requested resource."); + RT_DEBUGBREAK; + } + return index; +} + +rt_vk_bindless_handle rtStoreUniformBuffer(rt_vk_bindless_registry *registry, VkBuffer buffer) { + rtLockMutex(registry->mutex); + uint32_t index = AcquireSlot(®istry->uniform_buffer_reuse_stack, + ®istry->next_uniform_buffer, + &r_VkBindlessUniformBufferDescriptors); + if (index == UINT32_MAX) { + rtUnlockMutex(registry->mutex); + return (rt_vk_bindless_handle){RT_VK_INVALID_BINDLESS_HANDLE_VALUE}; + } + + VkDescriptorBufferInfo buffer_info = { + .buffer = buffer, + .range = VK_WHOLE_SIZE, + .offset = 0, + }; + + VkWriteDescriptorSet write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .dstBinding = registry->uniform_buffer_binding, + .dstSet = registry->bindless_set, + .dstArrayElement = index, + .pBufferInfo = &buffer_info, + }; + + vkUpdateDescriptorSets(registry->dev->device, 1, &write, 0, NULL); + rtUnlockMutex(registry->mutex); + + return MakeBindlessHandle(RT_VK_BINDLESS_HANDLE_TYPE_UNIFORM_BUFFER, index); +} + +rt_vk_bindless_handle rtStoreStorageBuffer(rt_vk_bindless_registry *registry, VkBuffer buffer) { + rtLockMutex(registry->mutex); + uint32_t index = AcquireSlot(®istry->storage_buffer_reuse_stack, + ®istry->next_storage_buffer, + &r_VkBindlessStorageBufferDescriptors); + if (index == UINT32_MAX) { + rtUnlockMutex(registry->mutex); + return (rt_vk_bindless_handle){RT_VK_INVALID_BINDLESS_HANDLE_VALUE}; + } + + VkDescriptorBufferInfo buffer_info = { + .buffer = buffer, + .range = VK_WHOLE_SIZE, + .offset = 0, + }; + + VkWriteDescriptorSet write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .dstBinding = registry->storage_buffer_binding, + .dstSet = registry->bindless_set, + .dstArrayElement = index, + .pBufferInfo = &buffer_info, + }; + + vkUpdateDescriptorSets(registry->dev->device, 1, &write, 0, NULL); + rtUnlockMutex(registry->mutex); + + return MakeBindlessHandle(RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_BUFFER, index); +} + +rt_vk_bindless_handle rtStoreSampledImage(rt_vk_bindless_registry *registry, VkImageView image) { + rtLockMutex(registry->mutex); + uint32_t index = AcquireSlot(®istry->sampled_image_reuse_stack, + ®istry->next_sampled_image, + &r_VkBindlessSampledImageDescriptors); + if (index == UINT32_MAX) { + rtUnlockMutex(registry->mutex); + return (rt_vk_bindless_handle){RT_VK_INVALID_BINDLESS_HANDLE_VALUE}; + } + + VkDescriptorImageInfo image_info = { + .sampler = VK_NULL_HANDLE, + .imageView = image, + .imageLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL, + }; + + VkWriteDescriptorSet write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, + .dstBinding = registry->sampled_image_binding, + .dstSet = registry->bindless_set, + .dstArrayElement = index, + .pImageInfo = &image_info, + }; + + vkUpdateDescriptorSets(registry->dev->device, 1, &write, 0, NULL); + rtUnlockMutex(registry->mutex); + + return MakeBindlessHandle(RT_VK_BINDLESS_HANDLE_TYPE_SAMPLED_IMAGE, index); +} + +rt_vk_bindless_handle rtStoreStorageImage(rt_vk_bindless_registry *registry, VkImageView image) { + rtLockMutex(registry->mutex); + uint32_t index = AcquireSlot(®istry->storage_image_reuse_stack, + ®istry->next_storage_image, + &r_VkBindlessStorageImageDescriptors); + if (index == UINT32_MAX) { + rtUnlockMutex(registry->mutex); + return (rt_vk_bindless_handle){RT_VK_INVALID_BINDLESS_HANDLE_VALUE}; + } + + VkDescriptorImageInfo image_info = { + .sampler = VK_NULL_HANDLE, + .imageView = image, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + VkWriteDescriptorSet write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .dstBinding = registry->storage_image_binding, + .dstSet = registry->bindless_set, + .dstArrayElement = index, + .pImageInfo = &image_info, + }; + + vkUpdateDescriptorSets(registry->dev->device, 1, &write, 0, NULL); + rtUnlockMutex(registry->mutex); + + return MakeBindlessHandle(RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_IMAGE, index); +} + +rt_vk_bindless_handle rtStoreSampler(rt_vk_bindless_registry *registry, VkSampler sampler) { + rtLockMutex(registry->mutex); + uint32_t index = AcquireSlot(®istry->sampler_reuse_stack, + ®istry->next_sampler, + &r_VkBindlessSamplerDescriptors); + if (index == UINT32_MAX) { + rtUnlockMutex(registry->mutex); + return (rt_vk_bindless_handle){RT_VK_INVALID_BINDLESS_HANDLE_VALUE}; + } + + VkDescriptorImageInfo image_info = { + .sampler = sampler, + .imageView = VK_NULL_HANDLE, + .imageLayout = VK_IMAGE_LAYOUT_UNDEFINED, + }; + + VkWriteDescriptorSet write = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER, + .dstBinding = registry->sampler_binding, + .dstSet = registry->bindless_set, + .dstArrayElement = index, + .pImageInfo = &image_info, + }; + + vkUpdateDescriptorSets(registry->dev->device, 1, &write, 0, NULL); + rtUnlockMutex(registry->mutex); + + return MakeBindlessHandle(RT_VK_BINDLESS_HANDLE_TYPE_SAMPLER, index); +} + +void rtFreeBindlessResource(rt_vk_bindless_registry *registry, rt_vk_bindless_handle handle) { + if (!rtIsVkBindlessHandleValid(handle)) + return; + rt_vk_bindless_handle_type type = rtGetVkBindlessHandleType(handle); + RT_VERIFY(type >= RT_VK_BINDLESS_HANDLE_TYPE_UNIFORM_BUFFER && + type <= RT_VK_BINDLESS_HANDLE_TYPE_SAMPLER); + + rtLockMutex(registry->mutex); + RT_ASSERT(registry->release_list_length < ReleaseListCapacity(), + "Ran out of release list space."); + registry->release_list[registry->release_list_length].handle = handle; + registry->release_list[registry->release_list_length].frame = + registry->dev->current_frame_id + registry->dev->max_frames_in_flight; + ++registry->release_list_length; + rtUnlockMutex(registry->mutex); +} + +void rtVkBindlessRegistryOnBeginFrame(rt_vk_bindless_registry *registry) { + /* Free resources that can be freed */ + rtLockMutex(registry->mutex); + for (uint32_t i = 0; i < registry->release_list_length; ++i) { + if (registry->release_list[i].frame == registry->dev->current_frame_id) { + uint32_t index = rtGetVkBindlessHandleIndex(registry->release_list[i].handle); + switch (rtGetVkBindlessHandleType(registry->release_list[i].handle)) { + case RT_VK_BINDLESS_HANDLE_TYPE_UNIFORM_BUFFER: + RT_ASSERT(registry->uniform_buffer_reuse_stack.size < + r_VkBindlessUniformBufferDescriptors.ui, + "Stack overflow"); + registry->uniform_buffer_reuse_stack + .indices[registry->uniform_buffer_reuse_stack.size++] = index; + break; + case RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_BUFFER: + RT_ASSERT(registry->storage_buffer_reuse_stack.size < + r_VkBindlessStorageBufferDescriptors.ui, + "Stack overflow"); + registry->storage_buffer_reuse_stack + .indices[registry->storage_buffer_reuse_stack.size++] = index; + break; + case RT_VK_BINDLESS_HANDLE_TYPE_SAMPLED_IMAGE: + RT_ASSERT(registry->sampled_image_reuse_stack.size < + r_VkBindlessSampledImageDescriptors.ui, + "Stack overflow"); + registry->sampled_image_reuse_stack + .indices[registry->sampled_image_reuse_stack.size++] = index; + break; + case RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_IMAGE: + RT_ASSERT(registry->storage_image_reuse_stack.size < + r_VkBindlessStorageImageDescriptors.ui, + "Stack overflow"); + registry->storage_image_reuse_stack + .indices[registry->storage_image_reuse_stack.size++] = index; + break; + case RT_VK_BINDLESS_HANDLE_TYPE_SAMPLER: + RT_ASSERT(registry->sampler_reuse_stack.size < r_VkBindlessSamplerDescriptors.ui, + "Stack overflow"); + registry->sampler_reuse_stack.indices[registry->sampler_reuse_stack.size++] = index; + break; + default: + rtLog("VK", + "Invalid bindless handle type: %u", + rtGetVkBindlessHandleType(registry->release_list[i].handle)); + break; + } + + /* Pop and swap */ + if (i < registry->release_list_length - 1) { + registry->release_list[i] = + registry->release_list[registry->release_list_length - 1]; + } + --i; + --registry->release_list_length; + } + } + rtUnlockMutex(registry->mutex); +} diff --git a/src/renderer/vk/bindless_registry.h b/src/renderer/vk/bindless_registry.h new file mode 100644 index 0000000..98f8cfd --- /dev/null +++ b/src/renderer/vk/bindless_registry.h @@ -0,0 +1,105 @@ +#ifndef RT_VK_BINDLESS_REGISTRY_H +#define RT_VK_BINDLESS_REGISTRY_H + +#include +#include +#include +#include + +struct rt_vk_device; + +enum { RT_VK_INVALID_BINDLESS_HANDLE_VALUE = UINT32_MAX }; + +typedef enum { + RT_VK_BINDLESS_HANDLE_TYPE_UNIFORM_BUFFER, + RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_BUFFER, + RT_VK_BINDLESS_HANDLE_TYPE_SAMPLED_IMAGE, + RT_VK_BINDLESS_HANDLE_TYPE_STORAGE_IMAGE, + RT_VK_BINDLESS_HANDLE_TYPE_SAMPLER, +} rt_vk_bindless_handle_type; + +/* Handle to a bindless resource. + * The layout is: + * | type : 3 | index : 29 | + * MSB LSB + */ +typedef struct { + uint32_t value; +} rt_vk_bindless_handle; + +/* Utilities for bindless handles */ + +static RT_INLINE bool rtIsVkBindlessHandleValid(rt_vk_bindless_handle handle) { + return handle.value != RT_VK_INVALID_BINDLESS_HANDLE_VALUE; +} + +static RT_INLINE rt_vk_bindless_handle_type rtGetVkBindlessHandleType(rt_vk_bindless_handle handle) { + return (handle.value >> 29) & 0x7; +} + +static RT_INLINE uint32_t rtGetVkBindlessHandleIndex(rt_vk_bindless_handle handle) { + return handle.value & ((1u << 29) - 1); +} + +typedef struct { + uint32_t *indices; + uint32_t size; +} rt_vk_bindless_reuse_stack; + +typedef struct { + rt_vk_bindless_handle handle; + /* Frame id on which the handle should be released */ + uint32_t frame; +} rt_vk_bindless_release_list_entry; + +/* Bindless registry manages the global descriptor set of bindless resources. */ +typedef struct { + struct rt_vk_device *dev; + rt_mutex *mutex; + + VkDescriptorSetLayout bindless_set_layout; + VkDescriptorPool bindless_set_pool; + VkDescriptorSet bindless_set; + + uint32_t uniform_buffer_binding; + uint32_t storage_buffer_binding; + uint32_t sampled_image_binding; + uint32_t storage_image_binding; + uint32_t sampler_binding; + + rt_vk_bindless_reuse_stack uniform_buffer_reuse_stack; + rt_vk_bindless_reuse_stack storage_buffer_reuse_stack; + rt_vk_bindless_reuse_stack sampled_image_reuse_stack; + rt_vk_bindless_reuse_stack storage_image_reuse_stack; + rt_vk_bindless_reuse_stack sampler_reuse_stack; + uint32_t next_uniform_buffer; + uint32_t next_storage_buffer; + uint32_t next_sampled_image; + uint32_t next_storage_image; + uint32_t next_sampler; + + rt_vk_bindless_release_list_entry *release_list; + uint32_t release_list_length; +} rt_vk_bindless_registry; + +typedef struct { + rt_result result; + rt_vk_bindless_registry bindless_registry; +} rt_create_vk_bindless_registry_result; + +rt_create_vk_bindless_registry_result rtCreateVkBindlessRegistry(struct rt_vk_device *dev); + +void rtDestroyVkBindlessRegistry(rt_vk_bindless_registry *registry); + +void rtVkBindlessRegistryOnBeginFrame(rt_vk_bindless_registry *registry); + +rt_vk_bindless_handle rtStoreUniformBuffer(rt_vk_bindless_registry *registry, VkBuffer buffer); +rt_vk_bindless_handle rtStoreStorageBuffer(rt_vk_bindless_registry *registry, VkBuffer buffer); +rt_vk_bindless_handle rtStoreSampledImage(rt_vk_bindless_registry *registry, VkImageView image); +rt_vk_bindless_handle rtStoreStorageImage(rt_vk_bindless_registry *registry, VkImageView image); +rt_vk_bindless_handle rtStoreSampler(rt_vk_bindless_registry *registry, VkSampler sampler); + +void rtFreeBindlessResource(rt_vk_bindless_registry *registry, rt_vk_bindless_handle handle); + + +#endif diff --git a/src/renderer/vk/device.c b/src/renderer/vk/device.c index 33e2dcc..309776d 100644 --- a/src/renderer/vk/device.c +++ b/src/renderer/vk/device.c @@ -622,6 +622,10 @@ rt_create_vk_device_result rtCreateVkDevice(const rt_renderer_window_info *info) if ((res = phys_res_mgr_res.result) != RT_SUCCESS) goto out; dev.phys_res_mgr = phys_res_mgr_res.phys_res_mgr; + rt_create_vk_bindless_registry_result bindless_registry_result = rtCreateVkBindlessRegistry(&dev); + if ((res = bindless_registry_result.result) != RT_SUCCESS) + goto out; + dev.bindless_registry = bindless_registry_result.bindless_registry; rt_time_delta initTime = rtTimeBetween(initBegin, rtTimeNow()); rtLog("VK", "Init complete. Took %lf seconds.", initTime); @@ -633,9 +637,10 @@ out: void rtDestroyVkDevice(rt_vk_device *dev) { rtLog("VK", "Shutdown"); vkDeviceWaitIdle(dev->device); + rtDestroyVkBindlessRegistry(&dev->bindless_registry); + rtDestroyVkPhysicalResourceManager(&dev->phys_res_mgr); DestroySwapchain(dev); DestroyPerFrameObjects(dev); - rtDestroyVkPhysicalResourceManager(&dev->phys_res_mgr); vkDestroyDevice(dev->device, dev->alloc_cb); vkDestroySurfaceKHR(dev->instance, dev->surface, dev->alloc_cb); #ifdef RT_DEBUG diff --git a/src/renderer/vk/device.h b/src/renderer/vk/device.h index 8307f7d..d74dd1f 100644 --- a/src/renderer/vk/device.h +++ b/src/renderer/vk/device.h @@ -7,6 +7,7 @@ #include #include "physical_resource_manager.h" +#include "bindless_registry.h" #ifdef _WIN32 struct HINSTANCE__; @@ -62,6 +63,7 @@ typedef struct rt_vk_device { /* *** Per frame data *** */ uint32_t max_frames_in_flight; rt_vk_frame_data frames[3]; + uint32_t current_frame_id; /* *** Windowing system *** */ rt_vk_native_window native_window; @@ -70,7 +72,9 @@ typedef struct rt_vk_device { VkImageView swapchain_image_views[4]; uint32_t swapchain_image_count; + /* *** Subsystems *** */ rt_vk_physical_resource_manager phys_res_mgr; + rt_vk_bindless_registry bindless_registry; /* *** Debug utils *** */ #ifdef RT_DEBUG diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index c65538d..35c52e4 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -11,6 +11,11 @@ extern rt_cvar r_VkPreferMailboxMode; extern rt_cvar r_VkPreferRelaxedMode; extern rt_cvar r_VkEnableVSync; extern rt_cvar r_VkMaxResources; +extern rt_cvar r_VkBindlessUniformBufferDescriptors; +extern rt_cvar r_VkBindlessStorageBufferDescriptors; +extern rt_cvar r_VkBindlessSampledImageDescriptors; +extern rt_cvar r_VkBindlessStorageImageDescriptors; +extern rt_cvar r_VkBindlessSamplerDescriptors; void VkRegisterCVARs(void) { rtRegisterCVAR(&r_VkEnableAPIAllocTracking); @@ -20,6 +25,11 @@ void VkRegisterCVARs(void) { rtRegisterCVAR(&r_VkPreferRelaxedMode); rtRegisterCVAR(&r_VkEnableVSync); rtRegisterCVAR(&r_VkMaxResources); + rtRegisterCVAR(&r_VkBindlessUniformBufferDescriptors); + rtRegisterCVAR(&r_VkBindlessStorageBufferDescriptors); + rtRegisterCVAR(&r_VkBindlessSampledImageDescriptors); + rtRegisterCVAR(&r_VkBindlessStorageImageDescriptors); + rtRegisterCVAR(&r_VkBindlessSamplerDescriptors); } static rt_vk_device _device; diff --git a/src/renderer/vk/meson.build b/src/renderer/vk/meson.build index 5ffcbb9..558c6ed 100644 --- a/src/renderer/vk/meson.build +++ b/src/renderer/vk/meson.build @@ -13,9 +13,11 @@ if get_option('build_vk') endif vk_renderer_lib = library('rtvk', + 'bindless_registry.h', 'device.h', 'physical_resource_manager.h', + 'bindless_registry.c', 'device.c', 'init.c', 'physical_resource_manager.c',