[dawn][vk] Enable bindless using "descriptor indexing"

Vulkan's equivalent of bindless is called "descriptor indexing" and a
soup of features and limits. Add support for this Vulkan feature and
gate WebGPU's bindless on a bunch of features and limits.

Note that "robustBufferAccessUpdateAfterBind" is a device property that
has implication for robustness when bindless is available. It will be
handled in a future CL and is not handled here.

Bug: 435251399
Change-Id: I9e77026c53b83dc72b30532c12541bd6193871d1
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/256936
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn/common/Constants.h b/src/dawn/common/Constants.h
index 86ec2bd..fc9122e 100644
--- a/src/dawn/common/Constants.h
+++ b/src/dawn/common/Constants.h
@@ -115,6 +115,9 @@
 // Default limit for dynamic binding arrays.
 // TODO(https://issues.chromium.org/issues/435317394): Update once the spec decides on a value.
 static constexpr uint32_t kMaxDynamicBindingArraySize = 50'000;
+// TODO(https://issues.chromium.org/issues/435317394): Find if this is a reasonable amount to
+// reserve for placeholders.
+static constexpr uint32_t kReservedDynamicBindingArrayEntries = 1000;
 
 }  // namespace dawn
 
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index 7845a42..053855c 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -603,6 +603,11 @@
         }
     }
 
+    if (HasFeature(Feature::ChromiumExperimentalBindless)) {
+        usedKnobs.descriptorIndexingFeatures = mDeviceInfo.descriptorIndexingFeatures;
+        featuresChain.Add(&usedKnobs.descriptorIndexingFeatures);
+    }
+
     // Find a universal queue family
     {
         // Note that GRAPHICS and COMPUTE imply TRANSFER so we don't need to check for it.
diff --git a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
index 0425772..e32e5cc 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -611,6 +611,54 @@
     }
 
     EnableFeature(Feature::TextureComponentSwizzle);
+
+    // Enable support for bindless if WebGPU semantics are supported:
+    //  - We can update descriptor sets while pending.
+    //  - Descriptor set may have variable size and be partially bound.
+    //  - We can declare runtime-sized arrays of resources in SPIRV.
+    //  - Limits are large enough for WebGPU.
+    // Bindless support for WebGPU requires checking a LOT of things, use runtimeDescriptorArray as
+    // good proxy to whether bindless will be available, and do the proper check using separate
+    // boolean variables instead of a giant if-statement. runtimeDescriptorArray checks if we can
+    // declare runtime-sized arrays of resources in SPIRV.
+    if (mDeviceInfo.HasExt(DeviceExt::DescriptorIndexing) &&
+        mDeviceInfo.descriptorIndexingFeatures.runtimeDescriptorArray) {
+        const auto& vkFeatures = mDeviceInfo.descriptorIndexingFeatures;
+        const auto& vkProperties = mDeviceInfo.descriptorIndexingProperties;
+
+        // All shader resource types support update-after-bind, except uniform buffers and input
+        // attachments that are not supported in WebGPU"s bindless.
+        bool hasUpdateAfterBind = vkFeatures.descriptorBindingSampledImageUpdateAfterBind &&
+                                  vkFeatures.descriptorBindingStorageImageUpdateAfterBind &&
+                                  vkFeatures.descriptorBindingStorageBufferUpdateAfterBind &&
+                                  vkFeatures.descriptorBindingStorageTexelBufferUpdateAfterBind;
+
+        // We can modify descriptor sets after binding them, they can have variable size and be
+        // sparse.
+        bool hasOtherFeatures = vkFeatures.descriptorBindingUpdateUnusedWhilePending &&
+                                vkFeatures.descriptorBindingPartiallyBound &&
+                                vkFeatures.descriptorBindingVariableDescriptorCount;
+
+        // We need to check a dozen Vulkan limits against the WebGPU limit for dynamic binding
+        // arrays.
+        constexpr uint32_t kRequiredLimit =
+            kMaxDynamicBindingArraySize + kReservedDynamicBindingArrayEntries;
+        bool hasLimit =
+            vkProperties.maxPerStageDescriptorUpdateAfterBindSamplers >= kRequiredLimit &&
+            vkProperties.maxPerStageDescriptorUpdateAfterBindStorageBuffers >= kRequiredLimit &&
+            vkProperties.maxPerStageDescriptorUpdateAfterBindSampledImages >= kRequiredLimit &&
+            vkProperties.maxPerStageDescriptorUpdateAfterBindStorageImages >= kRequiredLimit &&
+            vkProperties.maxPerStageUpdateAfterBindResources >= kRequiredLimit &&
+            vkProperties.maxDescriptorSetUpdateAfterBindSamplers >= kRequiredLimit &&
+            vkProperties.maxDescriptorSetUpdateAfterBindStorageBuffers >= kRequiredLimit &&
+            vkProperties.maxDescriptorSetUpdateAfterBindSampledImages >= kRequiredLimit &&
+            vkProperties.maxDescriptorSetUpdateAfterBindStorageImages >= kRequiredLimit &&
+            vkProperties.maxUpdateAfterBindDescriptorsInAllPools >= kRequiredLimit;
+
+        if (hasUpdateAfterBind && hasOtherFeatures && hasLimit) {
+            EnableFeature(Feature::ChromiumExperimentalBindless);
+        }
+    }
 }
 
 MaybeError PhysicalDevice::InitializeSupportedLimitsImpl(CombinedLimits* limits) {
@@ -818,6 +866,25 @@
             kMinimumHostMappedPointerAlignment;
     }
 
+    // Compute the limits for bindless, it requires checking a dozen vulkan limits.
+    if (mDeviceInfo.HasExt(DeviceExt::DescriptorIndexing)) {
+        const auto& vkProperties = mDeviceInfo.descriptorIndexingProperties;
+        uint32_t vkMax = 0;
+        vkMax = std::max(vkMax, vkProperties.maxPerStageDescriptorUpdateAfterBindSamplers);
+        vkMax = std::max(vkMax, vkProperties.maxPerStageDescriptorUpdateAfterBindStorageBuffers);
+        vkMax = std::max(vkMax, vkProperties.maxPerStageDescriptorUpdateAfterBindSampledImages);
+        vkMax = std::max(vkMax, vkProperties.maxPerStageDescriptorUpdateAfterBindStorageImages);
+        vkMax = std::max(vkMax, vkProperties.maxPerStageUpdateAfterBindResources);
+        vkMax = std::max(vkMax, vkProperties.maxDescriptorSetUpdateAfterBindSamplers);
+        vkMax = std::max(vkMax, vkProperties.maxDescriptorSetUpdateAfterBindStorageBuffers);
+        vkMax = std::max(vkMax, vkProperties.maxDescriptorSetUpdateAfterBindSampledImages);
+        vkMax = std::max(vkMax, vkProperties.maxDescriptorSetUpdateAfterBindStorageImages);
+        vkMax = std::max(vkMax, vkProperties.maxUpdateAfterBindDescriptorsInAllPools);
+
+        limits->dynamicBindingArrayLimits.maxDynamicBindingArraySize =
+            vkMax - kReservedDynamicBindingArrayEntries;
+    }
+
     return {};
 }
 
diff --git a/src/dawn/native/vulkan/VulkanExtensions.cpp b/src/dawn/native/vulkan/VulkanExtensions.cpp
index efad9bf..669fba6 100644
--- a/src/dawn/native/vulkan/VulkanExtensions.cpp
+++ b/src/dawn/native/vulkan/VulkanExtensions.cpp
@@ -173,6 +173,7 @@
     {DeviceExt::VulkanMemoryModel, "VK_KHR_vulkan_memory_model", VulkanVersion_1_2},
     {DeviceExt::ShaderFloatControls, "VK_KHR_shader_float_controls", VulkanVersion_1_2},
     {DeviceExt::Spirv14, "VK_KHR_spirv_1_4", VulkanVersion_1_2},
+    {DeviceExt::DescriptorIndexing, "VK_EXT_descriptor_indexing", VulkanVersion_1_2},
 
     {DeviceExt::ShaderIntegerDotProduct, "VK_KHR_shader_integer_dot_product", VulkanVersion_1_3},
     {DeviceExt::ZeroInitializeWorkgroupMemory, "VK_KHR_zero_initialize_workgroup_memory",
@@ -336,6 +337,11 @@
                                   HasDep(DeviceExt::StorageBufferStorageClass);
                 break;
 
+            case DeviceExt::DescriptorIndexing:
+                hasDependencies = HasDep(DeviceExt::GetPhysicalDeviceProperties2) &&
+                                  HasDep(DeviceExt::Maintenance3);
+                break;
+
             case DeviceExt::DisplayTiming:
                 hasDependencies = HasDep(DeviceExt::Swapchain);
                 break;
diff --git a/src/dawn/native/vulkan/VulkanExtensions.h b/src/dawn/native/vulkan/VulkanExtensions.h
index 4edffd2..775bfc2 100644
--- a/src/dawn/native/vulkan/VulkanExtensions.h
+++ b/src/dawn/native/vulkan/VulkanExtensions.h
@@ -111,6 +111,7 @@
     VulkanMemoryModel,
     ShaderFloatControls,
     Spirv14,
+    DescriptorIndexing,
 
     // Promoted to 1.3
     ShaderIntegerDotProduct,
diff --git a/src/dawn/native/vulkan/VulkanInfo.cpp b/src/dawn/native/vulkan/VulkanInfo.cpp
index e97931a..58888d5 100644
--- a/src/dawn/native/vulkan/VulkanInfo.cpp
+++ b/src/dawn/native/vulkan/VulkanInfo.cpp
@@ -349,6 +349,13 @@
                 VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_PROPERTIES_KHR);
         }
 
+        if (info.extensions[DeviceExt::DescriptorIndexing]) {
+            featuresChain.Add(&info.descriptorIndexingFeatures,
+                              VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES);
+            propertiesChain.Add(&info.descriptorIndexingProperties,
+                                VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES);
+        }
+
         // Use vkGetPhysicalDevice{Features,Properties}2 if required to gather information about
         // the extensions. DeviceExt::GetPhysicalDeviceProperties2 is guaranteed to be available
         // because these extensions (transitively) depend on it in `EnsureDependencies`
diff --git a/src/dawn/native/vulkan/VulkanInfo.h b/src/dawn/native/vulkan/VulkanInfo.h
index 8ce4fe6..60c1787 100644
--- a/src/dawn/native/vulkan/VulkanInfo.h
+++ b/src/dawn/native/vulkan/VulkanInfo.h
@@ -73,6 +73,7 @@
     VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR shaderSubgroupExtendedTypes;
     VkPhysicalDeviceVulkanMemoryModelFeatures vulkanMemoryModelFeatures;
     VkPhysicalDeviceCooperativeMatrixFeaturesKHR cooperativeMatrixFeatures;
+    VkPhysicalDeviceDescriptorIndexingFeatures descriptorIndexingFeatures;
 
     bool HasExt(DeviceExt ext) const;
     DeviceExtSet extensions;
@@ -88,6 +89,7 @@
     VkPhysicalDeviceSubgroupProperties subgroupProperties;
     VkPhysicalDeviceExternalMemoryHostPropertiesEXT externalMemoryHostProperties;
     VkPhysicalDeviceCooperativeMatrixPropertiesKHR cooperativeMatrixProperties;
+    VkPhysicalDeviceDescriptorIndexingProperties descriptorIndexingProperties;
 
     std::vector<VkQueueFamilyProperties> queueFamilies;
     std::vector<VkCooperativeMatrixPropertiesKHR> cooperativeMatrixConfigs;