[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;