Add VulkanRobustness feature

Enables all supported VK_EXT_robustness2 features to avoid
emulator crashes due to invalid application behavior on the
GPU.

Bug: 389637419
Test: -feature VulkanRobustness
Change-Id: I234180352356a14210f5e1b22eebad574ea58d6a
GitOrigin-RevId: a4e88ac1f7b71a893b6d44ca519d963d980a4865
diff --git a/host/features/include/gfxstream/host/Features.h b/host/features/include/gfxstream/host/Features.h
index 6c03a70..2eaa0d2 100644
--- a/host/features/include/gfxstream/host/Features.h
+++ b/host/features/include/gfxstream/host/Features.h
@@ -221,8 +221,10 @@
     };
     FeatureInfo BypassVulkanDeviceFeatureOverrides = {
         "BypassVulkanDeviceFeatureOverrides",
-        "We are force disabling (overriding) some vulkan features (private data, uniform inline block etc) which the device may naturally support."
-        "If toggled ON, this flag will cause the host side to not force disable anything and let the device fully advertise supported features.",
+        "We are force disabling (overriding) some vulkan features (private data, uniform inline "
+        "block etc) which the device may naturally support."
+        "If toggled ON, this flag will cause the host side to not force disable anything and let "
+        "the device fully advertise supported features.",
         &map,
     };
     FeatureInfo VulkanAllocateDeviceMemoryOnly = {
@@ -321,7 +323,14 @@
         "device properties for the guest queries.",
         &map,
     };
+    FeatureInfo VulkanRobustness = {
+        "VulkanRobustness",
+        "If enabled, robustness extensions with all supported features will be enabled on "
+        "all created devices. (e.g. VK_EXT_robustness2)",
+        &map,
+    };
 };
+
 struct FeatureDependencyHandler {
     FeatureDependencyHandler(const FeatureSet& set) : featureSetView(set){}
     const FeatureSet& featureSetView;
diff --git a/host/vulkan/VkCommonOperations.cpp b/host/vulkan/VkCommonOperations.cpp
index 3fc4f0b..42ecdd8 100644
--- a/host/vulkan/VkCommonOperations.cpp
+++ b/host/vulkan/VkCommonOperations.cpp
@@ -1220,6 +1220,15 @@
                 vk_append_struct(&features2Chain, &privateDataFeatures);
             }
 
+            VkPhysicalDeviceRobustness2FeaturesEXT robustness2Features = {
+                .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT};
+            const bool robustnessRequested = emulation->mFeatures.VulkanRobustness.enabled;
+            const bool robustnessSupported =
+                extensionsSupported(deviceExts, {VK_EXT_ROBUSTNESS_2_EXTENSION_NAME});
+            if (robustnessRequested && robustnessSupported) {
+                vk_append_struct(&features2Chain, &robustness2Features);
+            }
+
             emulation->mGetPhysicalDeviceFeatures2Func(physicalDevices[i], &features2);
 
             deviceInfos[i].supportsSamplerYcbcrConversion =
@@ -1230,6 +1239,15 @@
 
             deviceInfos[i].supportsPrivateData = (privateDataFeatures.privateData == VK_TRUE);
 
+            // Enable robustness only when requested
+            if (robustnessRequested && robustnessSupported) {
+                deviceInfos[i].robustness2Features = vk_make_orphan_copy(robustness2Features);
+            } else if (robustnessRequested) {
+                WARN(
+                    "VulkanRobustness was requested but the "
+                    "VK_EXT_robustness2 extension is not supported.");
+            }
+
 #if defined(__QNX__)
             deviceInfos[i].supportsExternalMemoryImport =
                 extMemScreenBufferFeatures.screenBufferImport == VK_TRUE;
@@ -1358,6 +1376,10 @@
     }
 #endif
 
+    if (emulation->mDeviceInfo.robustness2Features) {
+        selectedDeviceExtensionNames_.emplace(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
+    }
+
     std::vector<const char*> selectedDeviceExtensionNames(selectedDeviceExtensionNames_.begin(),
                                                           selectedDeviceExtensionNames_.end());
 
@@ -1422,6 +1444,14 @@
             "VK_NV_device_diagnostic_checkpoints extension is not supported.");
     }
 
+    VkPhysicalDeviceRobustness2FeaturesEXT r2features = {};
+    if (emulation->mDeviceInfo.robustness2Features) {
+        r2features = *emulation->mDeviceInfo.robustness2Features;
+        INFO("Enabling VK_EXT_robustness2 (%d %d %d).", r2features.robustBufferAccess2,
+             r2features.robustImageAccess2, r2features.nullDescriptor);
+        vk_append_struct(&deviceCiChain, &r2features);
+    }
+
     ivk->vkCreateDevice(emulation->mPhysicalDevice, &dCi, nullptr, &emulation->mDevice);
 
     if (res != VK_SUCCESS) {
@@ -1757,6 +1787,10 @@
     return mDeviceInfo.supportsExternalMemoryHostProps;
 }
 
+std::optional<VkPhysicalDeviceRobustness2FeaturesEXT> VkEmulation::getRobustness2Features() const {
+    return mDeviceInfo.robustness2Features;
+}
+
 VkPhysicalDeviceExternalMemoryHostPropertiesEXT VkEmulation::externalMemoryHostProperties() const {
     return mDeviceInfo.externalMemoryHostProps;
 }
diff --git a/host/vulkan/VkCommonOperations.h b/host/vulkan/VkCommonOperations.h
index bb50ca3..9e14b4d 100644
--- a/host/vulkan/VkCommonOperations.h
+++ b/host/vulkan/VkCommonOperations.h
@@ -126,6 +126,8 @@
 
     bool supportsExternalMemoryHostProperties() const;
 
+    std::optional<VkPhysicalDeviceRobustness2FeaturesEXT> getRobustness2Features() const;
+
     VkPhysicalDeviceExternalMemoryHostPropertiesEXT externalMemoryHostProperties() const;
 
     bool isGuestVulkanOnly() const;
@@ -472,6 +474,9 @@
 #else
         PFN_vkGetMemoryFdKHR getMemoryHandleFunc = nullptr;
 #endif
+
+        // Set only if requested and supported
+        std::optional<VkPhysicalDeviceRobustness2FeaturesEXT> robustness2Features;
     };
 
     uint32_t getValidMemoryTypeIndex(uint32_t requiredMemoryTypeBits,
diff --git a/host/vulkan/VkDecoderGlobalState.cpp b/host/vulkan/VkDecoderGlobalState.cpp
index 6bca012..2e4aca7 100644
--- a/host/vulkan/VkDecoderGlobalState.cpp
+++ b/host/vulkan/VkDecoderGlobalState.cpp
@@ -1822,6 +1822,17 @@
             }
         }
 
+        VkPhysicalDeviceRobustness2FeaturesEXT modifiedRobustness2features;
+        const auto r2features = m_vkEmulation->getRobustness2Features();
+        if (r2features && vk_find_struct<VkPhysicalDeviceRobustness2FeaturesEXT>(
+                                       &createInfoFiltered) == nullptr) {
+            VERBOSE("Force-enabling VK_EXT_robustness2 on device creation.");
+            updatedDeviceExtensions.push_back(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME);
+            modifiedRobustness2features = *r2features;
+            modifiedRobustness2features.pNext = const_cast<void*>(createInfoFiltered.pNext);
+            createInfoFiltered.pNext = &modifiedRobustness2features;
+        }
+
         if (VkPhysicalDeviceFeatures2* features2 =
                 vk_find_struct<VkPhysicalDeviceFeatures2>(&createInfoFiltered)) {
             featuresToFilter.emplace_back(&features2->features);
diff --git a/host/vulkan/cereal/common/vk_struct_id.h b/host/vulkan/cereal/common/vk_struct_id.h
index 0a9fb23..65f428b 100644
--- a/host/vulkan/cereal/common/vk_struct_id.h
+++ b/host/vulkan/cereal/common/vk_struct_id.h
@@ -105,6 +105,8 @@
                       VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES);
 REGISTER_VK_STRUCT_ID(VkPhysicalDeviceInlineUniformBlockFeatures,
                       VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES);
+REGISTER_VK_STRUCT_ID(VkPhysicalDeviceRobustness2FeaturesEXT,
+                      VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT);
 
 #if defined(VK_USE_PLATFORM_SCREEN_QNX)
 REGISTER_VK_STRUCT_ID(VkPhysicalDeviceExternalMemoryScreenBufferFeaturesQNX,