Vulkan: Add Extended Dynamic State Toggle

Adds the necessary plumbing to enable VK_EXT_extended_dynamic_state
and a toggle to control whether or not it's used, currently set to
false by default.

Does not make use of the extended dynamic state functionality. That
will be added in a follow-up CL.

Bug: 463893795
Change-Id: I453d9ac834d794f34746dbb82c2e0c4e025c7b9d
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/299055
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Reviewed-by: Kyle Charbonneau <kylechar@google.com>
diff --git a/src/dawn/native/Toggles.cpp b/src/dawn/native/Toggles.cpp
index a477646..53a26ea 100644
--- a/src/dawn/native/Toggles.cpp
+++ b/src/dawn/native/Toggles.cpp
@@ -762,6 +762,10 @@
       "Treat the stride operand for cooperative matrix load and store instructions as matrix "
       "elements instead of a source/dest pointee elements.",
       "https://crbug.com/460209126", ToggleStage::Device}},
+    {Toggle::VulkanUseExtendedDynamicState,
+     {"vulkan_use_extended_dynamic_state",
+      "Makes use of VK_EXT_extended_dynamic_state to improve pipeline caching.",
+      "https://crbug.com/chromium/463893793", ToggleStage::Device}},
     {Toggle::WaitIsThreadSafe,
      {"wait_is_thread_safe",
       "WaitFor* functions are thread-safe and can be called without the device-lock if implicit "
diff --git a/src/dawn/native/Toggles.h b/src/dawn/native/Toggles.h
index 414699b..7554c73 100644
--- a/src/dawn/native/Toggles.h
+++ b/src/dawn/native/Toggles.h
@@ -182,6 +182,7 @@
     VulkanUseCreateRenderPass2,
     MetalReplaceWorkgroupBoolWithU32,
     VulkanCooperativeMatrixStrideIsMatrixElements,
+    VulkanUseExtendedDynamicState,
 
     // Once all backends have been updated to be thread safe for waiting, we can remove this toggle.
     WaitIsThreadSafe,
diff --git a/src/dawn/native/vulkan/DeviceVk.cpp b/src/dawn/native/vulkan/DeviceVk.cpp
index c4e7aa7..80fbd89 100644
--- a/src/dawn/native/vulkan/DeviceVk.cpp
+++ b/src/dawn/native/vulkan/DeviceVk.cpp
@@ -668,6 +668,11 @@
         featuresChain.Add(&usedKnobs.multisampledRenderToSingleSampledFeatures);
     }
 
+    if (IsToggleEnabled(Toggle::VulkanUseDynamicRendering)) {
+        usedKnobs.extendedDynamicStateFeatures = mDeviceInfo.extendedDynamicStateFeatures;
+        featuresChain.Add(&usedKnobs.extendedDynamicStateFeatures);
+    }
+
     // 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 7f73d4e..27586e9 100644
--- a/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
+++ b/src/dawn/native/vulkan/PhysicalDeviceVk.cpp
@@ -1234,6 +1234,15 @@
     // Enable validation of generated SPIR-V by default.
     // Graphite and other native clients may turn this off.
     deviceToggles->Default(Toggle::EnableSpirvValidation, true);
+
+    // Use ExtendedDynamicState by default if the corresponding extension is available.
+    if (!GetDeviceInfo().HasExt(DeviceExt::ExtendedDynamicState) ||
+        GetDeviceInfo().extendedDynamicStateFeatures.extendedDynamicState == VK_FALSE) {
+        deviceToggles->ForceSet(Toggle::VulkanUseExtendedDynamicState, false);
+    } else {
+        // TODO(463893795): Default to true when support is fully implemented.
+        deviceToggles->Default(Toggle::VulkanUseExtendedDynamicState, false);
+    }
 }
 
 ResultOrError<Ref<DeviceBase>> PhysicalDevice::CreateDeviceImpl(
diff --git a/src/dawn/native/vulkan/VulkanExtensions.cpp b/src/dawn/native/vulkan/VulkanExtensions.cpp
index ac0bfc1..1056273e 100644
--- a/src/dawn/native/vulkan/VulkanExtensions.cpp
+++ b/src/dawn/native/vulkan/VulkanExtensions.cpp
@@ -139,6 +139,7 @@
     {DeviceExt::Maintenance4, "VK_KHR_maintenance4"},
     {DeviceExt::SubgroupSizeControl, "VK_EXT_subgroup_size_control"},
     {DeviceExt::DynamicRendering, "VK_KHR_dynamic_rendering"},
+    {DeviceExt::ExtendedDynamicState, "VK_EXT_extended_dynamic_state"},
 
     // Promoted in 1.4
     {DeviceExt::PipelineRobustness, "VK_EXT_pipeline_robustness"},
@@ -228,6 +229,7 @@
             case DeviceExt::ExternalSemaphoreFD:
             case DeviceExt::ExternalSemaphoreZirconHandle:
             case DeviceExt::QueueFamilyForeign:
+            case DeviceExt::ExtendedDynamicState:
                 hasDependencies = true;
                 break;
 
diff --git a/src/dawn/native/vulkan/VulkanExtensions.h b/src/dawn/native/vulkan/VulkanExtensions.h
index 8a31520..ab80708 100644
--- a/src/dawn/native/vulkan/VulkanExtensions.h
+++ b/src/dawn/native/vulkan/VulkanExtensions.h
@@ -98,6 +98,7 @@
     Maintenance4,
     SubgroupSizeControl,
     DynamicRendering,
+    ExtendedDynamicState,
 
     // Promoted to 1.4
     PipelineRobustness,
diff --git a/src/dawn/native/vulkan/VulkanFunctions.cpp b/src/dawn/native/vulkan/VulkanFunctions.cpp
index 8ef9251..06b43e6 100644
--- a/src/dawn/native/vulkan/VulkanFunctions.cpp
+++ b/src/dawn/native/vulkan/VulkanFunctions.cpp
@@ -396,6 +396,17 @@
         GET_DEVICE_PROC(CmdEndRenderingKHR);
     }
 
+    if (deviceInfo.HasExt(DeviceExt::ExtendedDynamicState)) {
+        GET_DEVICE_PROC(CmdSetCullModeEXT);
+        GET_DEVICE_PROC(CmdSetDepthCompareOpEXT);
+        GET_DEVICE_PROC(CmdSetDepthTestEnableEXT);
+        GET_DEVICE_PROC(CmdSetDepthWriteEnableEXT);
+        GET_DEVICE_PROC(CmdSetFrontFaceEXT);
+        GET_DEVICE_PROC(CmdSetPrimitiveTopologyEXT);
+        GET_DEVICE_PROC(CmdSetStencilOpEXT);
+        GET_DEVICE_PROC(CmdSetStencilTestEnableEXT);
+    }
+
     // Not promoted to core in any version
     if (deviceInfo.HasExt(DeviceExt::ExternalMemoryFD)) {
         GET_DEVICE_PROC(GetMemoryFdKHR);
diff --git a/src/dawn/native/vulkan/VulkanFunctions.h b/src/dawn/native/vulkan/VulkanFunctions.h
index 4e669b8..0da87dd 100644
--- a/src/dawn/native/vulkan/VulkanFunctions.h
+++ b/src/dawn/native/vulkan/VulkanFunctions.h
@@ -364,6 +364,16 @@
     VkFn<PFN_vkCmdBeginRenderingKHR> CmdBeginRenderingKHR = nullptr;
     VkFn<PFN_vkCmdEndRenderingKHR> CmdEndRenderingKHR = nullptr;
 
+    // VK_EXT_extended_dynamic_state
+    VkFn<PFN_vkCmdSetCullModeEXT> CmdSetCullModeEXT = nullptr;
+    VkFn<PFN_vkCmdSetDepthCompareOpEXT> CmdSetDepthCompareOpEXT = nullptr;
+    VkFn<PFN_vkCmdSetDepthTestEnableEXT> CmdSetDepthTestEnableEXT = nullptr;
+    VkFn<PFN_vkCmdSetDepthWriteEnableEXT> CmdSetDepthWriteEnableEXT = nullptr;
+    VkFn<PFN_vkCmdSetFrontFaceEXT> CmdSetFrontFaceEXT = nullptr;
+    VkFn<PFN_vkCmdSetPrimitiveTopologyEXT> CmdSetPrimitiveTopologyEXT = nullptr;
+    VkFn<PFN_vkCmdSetStencilOpEXT> CmdSetStencilOpEXT = nullptr;
+    VkFn<PFN_vkCmdSetStencilTestEnableEXT> CmdSetStencilTestEnableEXT = nullptr;
+
     // Not promoted to core in any version
 
     // VK_KHR_external_memory_fd
diff --git a/src/dawn/native/vulkan/VulkanInfo.cpp b/src/dawn/native/vulkan/VulkanInfo.cpp
index 632e846..3270996 100644
--- a/src/dawn/native/vulkan/VulkanInfo.cpp
+++ b/src/dawn/native/vulkan/VulkanInfo.cpp
@@ -257,6 +257,8 @@
                       VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES);
     featuresChain.Add(&info.samplerYCbCrConversionFeatures,
                       VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES);
+    featuresChain.Add(&info.extendedDynamicStateFeatures,
+                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT);
 
     if (info.extensions[DeviceExt::ShaderFloat16Int8]) {
         featuresChain.Add(&info.shaderFloat16Int8Features,
diff --git a/src/dawn/native/vulkan/VulkanInfo.h b/src/dawn/native/vulkan/VulkanInfo.h
index 423ff14..f3332f6 100644
--- a/src/dawn/native/vulkan/VulkanInfo.h
+++ b/src/dawn/native/vulkan/VulkanInfo.h
@@ -79,6 +79,7 @@
     VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeatures;
     VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT
         multisampledRenderToSingleSampledFeatures;
+    VkPhysicalDeviceExtendedDynamicStateFeaturesEXT extendedDynamicStateFeatures;
 
     bool HasExt(DeviceExt ext) const;
     DeviceExtSet extensions;