From 49a0ca6d5d9929391e0633163ebbfec564d27cc1 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Aug 2026 16:58:43 +0200 Subject: [vulkan] Initial implementation on Bindless Buffer/Descriptors (#4251) Adds a VK_EXT_descriptor_buffer, VK_KHR_device_buffer_address path alongside push descriptors and descriptor sets. The ring is chunked and sized from the device limits, so drivers with a small sampler range (QCOM reports 128 KiB) get more, chunks per frame instead of losing the feature. A pipeline uses one path or the other, never both, since some drivers do not support bufferless push descriptors. The chunk binding is only re-emitted when it changes, and a draw that repeats the previous payload reuses its allocation instead of rewriting it. This whole change will help to reduce the constant use of descriptors on the slower path, improves the "slow implementation" on QCOM drivers (A7xx and older) for push descriptors, improves latency when compiling pipelines. The implementation on indexing descriptors was also refined. _Special Thanks_ 1.- Meowly The Bindless Smol (@Gidoly) Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4251 --- src/video_core/vulkan_common/vulkan_device.cpp | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'src/video_core/vulkan_common/vulkan_device.cpp') diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 7e9df7fed0..8c41a7eb67 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -506,6 +506,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR if (is_qualcomm) { must_emulate_scaled_formats = true; LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation."); + has_broken_descriptor_aliasing = true; + LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken descriptor aliasing."); LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken custom border color."); RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); @@ -713,6 +715,37 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); } + // Descriptors feature list + { + auto& descriptor_indexing = features.descriptor_indexing; + descriptor_indexing.shaderInputAttachmentArrayDynamicIndexing = false; + descriptor_indexing.shaderUniformTexelBufferArrayDynamicIndexing = false; + descriptor_indexing.shaderStorageTexelBufferArrayDynamicIndexing = false; + descriptor_indexing.shaderUniformBufferArrayNonUniformIndexing = false; + descriptor_indexing.shaderStorageBufferArrayNonUniformIndexing = false; + descriptor_indexing.shaderInputAttachmentArrayNonUniformIndexing = false; + descriptor_indexing.descriptorBindingUniformBufferUpdateAfterBind = false; + descriptor_indexing.descriptorBindingSampledImageUpdateAfterBind = false; + descriptor_indexing.descriptorBindingStorageImageUpdateAfterBind = false; + descriptor_indexing.descriptorBindingStorageBufferUpdateAfterBind = false; + descriptor_indexing.descriptorBindingUniformTexelBufferUpdateAfterBind = false; + descriptor_indexing.descriptorBindingStorageTexelBufferUpdateAfterBind = false; + descriptor_indexing.descriptorBindingUpdateUnusedWhilePending = false; + descriptor_indexing.descriptorBindingVariableDescriptorCount = false; + descriptor_indexing.runtimeDescriptorArray = false; + } + + // VK_EXT_descriptor_buffer requires VK_KHR_buffer_device_address + if (extensions.descriptor_buffer && !features.buffer_device_address.bufferDeviceAddress) { + LOG_WARNING(Render_Vulkan, "Descriptor buffer needs buffer device address, disabling."); + RemoveExtensionFeature(extensions.descriptor_buffer, features.descriptor_buffer, + VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); + } + if (!extensions.descriptor_buffer) { + RemoveExtensionFeature(extensions.buffer_device_address, features.buffer_device_address, + VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME); + } + logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld); graphics_queue = logical.GetQueue(graphics_family); @@ -726,6 +759,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR if (extensions.memory_budget) { flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; } + if (extensions.buffer_device_address) { + flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + } const VmaAllocatorCreateInfo allocator_info{ .flags = flags, .physicalDevice = physical, @@ -972,6 +1008,10 @@ bool Device::GetSuitability(bool requires_swapchain) { CHECK_EXTENSION(VK_KHR_SWAPCHAIN_EXTENSION_NAME); } + if (instance_version < VK_API_VERSION_1_2) { + CHECK_EXTENSION(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME); + } + #undef LOG_EXTENSION #undef CHECK_EXTENSION @@ -1082,6 +1122,11 @@ bool Device::GetSuitability(bool requires_swapchain) { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR; SetNext(next, properties.push_descriptor); } + if (extensions.descriptor_buffer) { + properties.descriptor_buffer.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_BUFFER_PROPERTIES_EXT; + SetNext(next, properties.descriptor_buffer); + } if (extensions.subgroup_size_control || features.subgroup_size_control.subgroupSizeControl) { properties.subgroup_size_control.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES; @@ -1213,6 +1258,11 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); + // VK_EXT_descriptor_buffer + extensions.descriptor_buffer = features.descriptor_buffer.descriptorBuffer; + RemoveExtensionFeatureIfUnsuitable(extensions.descriptor_buffer, features.descriptor_buffer, + VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME); + // VK_EXT_extended_dynamic_state extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state, -- cgit v1.2.3