From 4bbde3e5ecb03db5d0abfd9157f527d749cefbab Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Nov 2025 13:59:47 -0400 Subject: [PATCH 01/23] Improve float operations for ARM and Qualcomm drivers and other extensions workarounds --- .../renderer_vulkan/maxwell_to_vk.cpp | 7 +++- .../renderer_vulkan/vk_pipeline_cache.cpp | 36 +++++++++++-------- .../vulkan_common/vulkan_device.cpp | 36 ++++++++++++++++--- src/video_core/vulkan_common/vulkan_device.h | 5 +++ 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index a7a878f18c..1d27f454dd 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -77,7 +77,12 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra ASSERT(false); return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: - return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + if (device.IsKhrSamplerMirrorClampToEdgeSupported()) { + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + } + // Fallback when the sampler mirror clamp extension isn't present. + // Use CLAMP_TO_EDGE as the safest approximation. + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::MirrorOnceBorder: UNIMPLEMENTED(); return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 10ee14773f..fbcad44d5f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -327,20 +327,28 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .support_int64 = device.IsShaderInt64Supported(), .support_vertex_instance_id = false, .support_float_controls = device.IsKhrShaderFloatControlsSupported(), - .support_separate_denorm_behavior = - float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, - .support_separate_rounding_mode = - float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, - .support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE, - .support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE, - .support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE, - .support_fp32_denorm_flush = float_control.shaderDenormFlushToZeroFloat32 != VK_FALSE, - .support_fp16_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat16 != VK_FALSE, - .support_fp32_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE, - .support_fp64_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, + // Only enable per-size float control capabilities when the KHR_shader_float_controls + // extension is actually enabled on the device and the driver reports explicit support + // for the individual properties. This avoids enabling functionality when the extension + // was removed due to driver workarounds. + .support_separate_denorm_behavior = device.IsKhrShaderFloatControlsSupported() && + (float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL), + .support_separate_rounding_mode = device.IsKhrShaderFloatControlsSupported() && + (float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL), + .support_fp16_denorm_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormPreserveFloat16 == VK_TRUE), + .support_fp32_denorm_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormPreserveFloat32 == VK_TRUE), + .support_fp16_denorm_flush = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormFlushToZeroFloat16 == VK_TRUE), + .support_fp32_denorm_flush = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormFlushToZeroFloat32 == VK_TRUE), + .support_fp16_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderSignedZeroInfNanPreserveFloat16 == VK_TRUE), + .support_fp32_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderSignedZeroInfNanPreserveFloat32 == VK_TRUE), + .support_fp64_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderSignedZeroInfNanPreserveFloat64 == VK_TRUE), .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT), .support_viewport_index_layer_non_geometry = diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 170627ea1b..8dbdc8f241 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -507,7 +507,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); } - if (is_qualcomm) { + if (is_qualcomm || is_arm) { if (!force_extensions) { LOG_WARNING(Render_Vulkan, "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); @@ -515,7 +515,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } LOG_WARNING(Render_Vulkan, - "Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers"); + "Disabling shader float controls and 64-bit integer features on Qualcomm and ARM Mali proprietary drivers"); RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); @@ -585,13 +585,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); } } - if (extensions.extended_dynamic_state2 && is_qualcomm) { + if (extensions.extended_dynamic_state2 && is_qualcomm || is_arm) { const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && version < VK_MAKE_API_VERSION(0, 0, 680, 0) && !force_extensions) { + // Arm Mali Inmortalis drivers have broken extendedDynamicState2LogicOp. // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. LOG_WARNING(Render_Vulkan, - "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); + "Qualcomm Adreno 7xx and Arm Mali Inmortalis drivers have broken VK_EXT_extended_dynamic_state2"); RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); @@ -1173,9 +1174,36 @@ bool Device::GetSuitability(bool requires_swapchain) { // Store base properties properties.properties = properties2.properties; + // Diagnostic logging for shader float controls on Qualcomm/ARM drivers. + // Print the reported per-float-size properties so we can debug denorm/flush issues. + { + const auto driver_id = properties.driver.driverID; + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) { + const auto& fc = properties.float_controls; + LOG_INFO(Render_Vulkan, + "Driver '{}' id={} reports VK_KHR_shader_float_controls extension present={} -- " + "denormPreserveF16={} denormPreserveF32={} flushToZeroF16={} flushToZeroF32={} " + "denormBehaviorIndependence={} roundingModeIndependence={}", + properties.driver.driverName, driver_id, extensions.shader_float_controls, + (fc.shaderDenormPreserveFloat16 == VK_TRUE), (fc.shaderDenormPreserveFloat32 == VK_TRUE), + (fc.shaderDenormFlushToZeroFloat16 == VK_TRUE), (fc.shaderDenormFlushToZeroFloat32 == VK_TRUE), + fc.denormBehaviorIndependence, fc.roundingModeIndependence); + } + } + // Unload extensions if feature support is insufficient. RemoveUnsuitableExtensions(); + // Log final state of shader float controls extension on Qualcomm/ARM for diagnostics. + { + const auto driver_id = properties.driver.driverID; + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) { + LOG_INFO(Render_Vulkan, + "Final shader float controls extension enabled={} after suitability checks for driver '{}' id={}", + extensions.shader_float_controls, properties.driver.driverName, driver_id); + } + } + // Check limits. struct Limit { u32 minimum; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cb13f28523..98059c5707 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -445,6 +445,11 @@ public: return extensions.shader_float_controls; } + /// Returns true if VK_KHR_sampler_mirror_clamp_to_edge is enabled. + bool IsKhrSamplerMirrorClampToEdgeSupported() const { + return extensions.sampler_mirror_clamp_to_edge; + } + /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { return extensions.workgroup_memory_explicit_layout; From 004264d2a6e90e98ce2d74a829477bcf4c2083e8 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Nov 2025 14:33:15 -0400 Subject: [PATCH 02/23] Fix parenthesis/ build error --- src/video_core/vulkan_common/vulkan_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 8dbdc8f241..470205c0c5 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -585,7 +585,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); } } - if (extensions.extended_dynamic_state2 && is_qualcomm || is_arm) { + if (extensions.extended_dynamic_state2 && (is_qualcomm || is_arm)) { const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && version < VK_MAKE_API_VERSION(0, 0, 680, 0) && !force_extensions) { From 16ae756da3fba04901c96ddd6b7ad6565901dc45 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Nov 2025 13:59:47 -0400 Subject: [PATCH 03/23] Improve float operations for ARM and Qualcomm drivers and other extensions workarounds --- .../renderer_vulkan/maxwell_to_vk.cpp | 7 +++- .../renderer_vulkan/vk_pipeline_cache.cpp | 36 +++++++++++-------- .../vulkan_common/vulkan_device.cpp | 36 ++++++++++++++++--- src/video_core/vulkan_common/vulkan_device.h | 5 +++ 4 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp index a7a878f18c..1d27f454dd 100644 --- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp +++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp @@ -77,7 +77,12 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra ASSERT(false); return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::MirrorOnceClampToEdge: - return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + if (device.IsKhrSamplerMirrorClampToEdgeSupported()) { + return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; + } + // Fallback when the sampler mirror clamp extension isn't present. + // Use CLAMP_TO_EDGE as the safest approximation. + return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; case Tegra::Texture::WrapMode::MirrorOnceBorder: UNIMPLEMENTED(); return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 10ee14773f..fbcad44d5f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -327,20 +327,28 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .support_int64 = device.IsShaderInt64Supported(), .support_vertex_instance_id = false, .support_float_controls = device.IsKhrShaderFloatControlsSupported(), - .support_separate_denorm_behavior = - float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, - .support_separate_rounding_mode = - float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL, - .support_fp16_denorm_preserve = float_control.shaderDenormPreserveFloat16 != VK_FALSE, - .support_fp32_denorm_preserve = float_control.shaderDenormPreserveFloat32 != VK_FALSE, - .support_fp16_denorm_flush = float_control.shaderDenormFlushToZeroFloat16 != VK_FALSE, - .support_fp32_denorm_flush = float_control.shaderDenormFlushToZeroFloat32 != VK_FALSE, - .support_fp16_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat16 != VK_FALSE, - .support_fp32_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat32 != VK_FALSE, - .support_fp64_signed_zero_nan_preserve = - float_control.shaderSignedZeroInfNanPreserveFloat64 != VK_FALSE, + // Only enable per-size float control capabilities when the KHR_shader_float_controls + // extension is actually enabled on the device and the driver reports explicit support + // for the individual properties. This avoids enabling functionality when the extension + // was removed due to driver workarounds. + .support_separate_denorm_behavior = device.IsKhrShaderFloatControlsSupported() && + (float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL), + .support_separate_rounding_mode = device.IsKhrShaderFloatControlsSupported() && + (float_control.roundingModeIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL), + .support_fp16_denorm_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormPreserveFloat16 == VK_TRUE), + .support_fp32_denorm_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormPreserveFloat32 == VK_TRUE), + .support_fp16_denorm_flush = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormFlushToZeroFloat16 == VK_TRUE), + .support_fp32_denorm_flush = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderDenormFlushToZeroFloat32 == VK_TRUE), + .support_fp16_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderSignedZeroInfNanPreserveFloat16 == VK_TRUE), + .support_fp32_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderSignedZeroInfNanPreserveFloat32 == VK_TRUE), + .support_fp64_signed_zero_nan_preserve = device.IsKhrShaderFloatControlsSupported() && + (float_control.shaderSignedZeroInfNanPreserveFloat64 == VK_TRUE), .support_explicit_workgroup_layout = device.IsKhrWorkgroupMemoryExplicitLayoutSupported(), .support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT), .support_viewport_index_layer_non_geometry = diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 39ecc56bfb..c08e5b432d 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -507,7 +507,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); } - if (is_qualcomm) { + if (is_qualcomm || is_arm) { if (!force_extensions) { LOG_WARNING(Render_Vulkan, "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); @@ -515,7 +515,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } LOG_WARNING(Render_Vulkan, - "Disabling shader float controls and 64-bit integer features on Qualcomm proprietary drivers"); + "Disabling shader float controls and 64-bit integer features on Qualcomm and ARM Mali proprietary drivers"); RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); @@ -585,13 +585,14 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); } } - if (extensions.extended_dynamic_state2 && is_qualcomm) { + if (extensions.extended_dynamic_state2 && is_qualcomm || is_arm) { const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && version < VK_MAKE_API_VERSION(0, 0, 680, 0) && !force_extensions) { + // Arm Mali Inmortalis drivers have broken extendedDynamicState2LogicOp. // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. LOG_WARNING(Render_Vulkan, - "Qualcomm Adreno 7xx drivers have broken VK_EXT_extended_dynamic_state2"); + "Qualcomm Adreno 7xx and Arm Mali Inmortalis drivers have broken VK_EXT_extended_dynamic_state2"); RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); @@ -1178,9 +1179,36 @@ bool Device::GetSuitability(bool requires_swapchain) { // Store base properties properties.properties = properties2.properties; + // Diagnostic logging for shader float controls on Qualcomm/ARM drivers. + // Print the reported per-float-size properties so we can debug denorm/flush issues. + { + const auto driver_id = properties.driver.driverID; + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) { + const auto& fc = properties.float_controls; + LOG_INFO(Render_Vulkan, + "Driver '{}' id={} reports VK_KHR_shader_float_controls extension present={} -- " + "denormPreserveF16={} denormPreserveF32={} flushToZeroF16={} flushToZeroF32={} " + "denormBehaviorIndependence={} roundingModeIndependence={}", + properties.driver.driverName, driver_id, extensions.shader_float_controls, + (fc.shaderDenormPreserveFloat16 == VK_TRUE), (fc.shaderDenormPreserveFloat32 == VK_TRUE), + (fc.shaderDenormFlushToZeroFloat16 == VK_TRUE), (fc.shaderDenormFlushToZeroFloat32 == VK_TRUE), + fc.denormBehaviorIndependence, fc.roundingModeIndependence); + } + } + // Unload extensions if feature support is insufficient. RemoveUnsuitableExtensions(); + // Log final state of shader float controls extension on Qualcomm/ARM for diagnostics. + { + const auto driver_id = properties.driver.driverID; + if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) { + LOG_INFO(Render_Vulkan, + "Final shader float controls extension enabled={} after suitability checks for driver '{}' id={}", + extensions.shader_float_controls, properties.driver.driverName, driver_id); + } + } + // Check limits. struct Limit { u32 minimum; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cb13f28523..98059c5707 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -445,6 +445,11 @@ public: return extensions.shader_float_controls; } + /// Returns true if VK_KHR_sampler_mirror_clamp_to_edge is enabled. + bool IsKhrSamplerMirrorClampToEdgeSupported() const { + return extensions.sampler_mirror_clamp_to_edge; + } + /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { return extensions.workgroup_memory_explicit_layout; From 3a8677597f999128a76299287d0c7bf0f5babc73 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Nov 2025 14:33:15 -0400 Subject: [PATCH 04/23] Fix parenthesis/ build error --- src/video_core/vulkan_common/vulkan_device.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index c08e5b432d..f72c451789 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -585,7 +585,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); } } - if (extensions.extended_dynamic_state2 && is_qualcomm || is_arm) { + if (extensions.extended_dynamic_state2 && (is_qualcomm || is_arm)) { const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && version < VK_MAKE_API_VERSION(0, 0, 680, 0) && !force_extensions) { From 3af7aafc25b3a69bcd84f815d1555e7490aac423 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Nov 2025 16:50:18 -0400 Subject: [PATCH 05/23] Add suitability for Robustness2 --- src/video_core/vulkan_common/vulkan_device.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 470205c0c5..5cfdda687c 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1249,6 +1249,14 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); + // VK_EXT_robustness2 + // Enable the extension only if at least one of the useful robustness2 features is present. + extensions.robustness2 = features.robustness2.nullDescriptor || + features.robustness2.robustBufferAccess2 || + features.robustness2.robustImageAccess2; + RemoveExtensionFeatureIfUnsuitable(extensions.robustness2, features.robustness2, + VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + // VK_EXT_extended_dynamic_state extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state, From 9ffead7e7ad2d6772e063bdcf167e5ce4cdaa1eb Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Nov 2025 19:23:37 -0400 Subject: [PATCH 06/23] Re-enabling VK_KHR_shader_float_controls for further testing --- src/video_core/vulkan_common/vulkan_device.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 962838dd8a..97a3287d17 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -514,11 +514,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); } - LOG_WARNING(Render_Vulkan, - "Disabling shader float controls and 64-bit integer features on Qualcomm and ARM Mali proprietary drivers"); - RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME); - RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64, - VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); + LOG_WARNING(Render_Vulkan, + "Disabling 64-bit integer features on Qualcomm and ARM Mali proprietary drivers"); + RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64, + VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME); features.shader_atomic_int64.shaderBufferInt64Atomics = false; features.shader_atomic_int64.shaderSharedInt64Atomics = false; features.features.shaderInt64 = false; From 1fc9c3f6ff0362dfaf5b7caecd5a58257e2535f4 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 5 Nov 2025 22:10:02 -0400 Subject: [PATCH 10/23] Added FTZ optimizations and adjust pipeline float control handling for Qualcomm drivers --- src/common/settings.h | 4 +++ .../spirv/emit_spirv_floating_point.cpp | 24 +++++++++++++-- .../renderer_vulkan/vk_graphics_pipeline.h | 6 +++- .../renderer_vulkan/vk_pipeline_cache.cpp | 30 ++++++++++++++++++- 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index 0b61da88f9..c52c4ca345 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -547,6 +547,10 @@ struct Values { Specialization::Scalar}; SwitchableSetting force_unsupported_extensions{linkage, false, "force_unsupported_extensions", Category::RendererExtensions}; + // Developer toggle to enable FTZ (flush-to-zero) optimizations for testing on + // Qualcomm devices. Default false for safety; when true, pipeline code may + // emit FTZ-friendly SPIR-V and allow fast-math transforms for Adreno GPUs. + SwitchableSetting enable_ftz{linkage, false, "enable_ftz", Category::RendererExtensions}; SwitchableSetting provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions}; SwitchableSetting descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions}; SwitchableSetting sample_shading{linkage, false, "sample_shading", Category::RendererExtensions, Specialization::Paired}; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index d921913b4a..9f3ceb09ed 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp @@ -73,15 +73,33 @@ Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { } Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { - return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); + // Use OpFma only when fast-math is allowed. Some drivers/targets require + // conservative separate mul+add semantics; when need_fastmath_off is true + // we emit separate FMul + FAdd to avoid relying on fused behavior. + if (!ctx.profile.need_fastmath_off) { + return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); + } else { + const Id mul = ctx.OpFMul(ctx.F16[1], a, b); + return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], mul, c)); + } } Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { - return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); + if (!ctx.profile.need_fastmath_off) { + return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); + } else { + const Id mul = ctx.OpFMul(ctx.F32[1], a, b); + return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], mul, c)); + } } Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { - return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); + if (!ctx.profile.need_fastmath_off) { + return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); + } else { + const Id mul = ctx.OpFMul(ctx.F64[1], a, b); + return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], mul, c)); + } } Id EmitFPMax32(EmitContext& ctx, Id a, Id b) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 650c8e07ed..6703c8d8c5 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -31,6 +31,10 @@ namespace Vulkan { struct GraphicsPipelineCacheKey { std::array unique_hashes; FixedPipelineState state; + // Per-pipeline float control choices (selected at pipeline key time). + // 0 = disabled, 1 = enabled + uint8_t use_ftz_f32{}; + uint8_t use_ftz_f16{}; size_t Hash() const noexcept; @@ -41,7 +45,7 @@ struct GraphicsPipelineCacheKey { } size_t Size() const noexcept { - return sizeof(unique_hashes) + state.Size(); + return sizeof(unique_hashes) + state.Size() + sizeof(use_ftz_f32) + sizeof(use_ftz_f16); } }; static_assert(std::has_unique_object_representations_v); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index fbcad44d5f..22245f91f6 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -446,6 +446,26 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { } graphics_key.state.Refresh(*maxwell3d, dynamic_features); + // Decide per-pipeline FTZ (flush-to-zero) usage based on device float-controls + // properties and a dedicated enable_ftz developer toggle. FTZ is gated to + // Qualcomm drivers for initial testing to avoid widespread regressions. + const bool enable_ftz_setting = Settings::values.enable_ftz.GetValue(); + const auto& float_control = device.FloatControlProperties(); + const bool has_khr_float_controls = device.IsKhrShaderFloatControlsSupported(); + const bool denorm_indep_all = float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL; + const bool denorm_indep_32 = denorm_indep_all || float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY; + + const bool is_qualcomm = device.GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; + const bool allow_ftz = enable_ftz_setting && is_qualcomm; + + graphics_key.use_ftz_f32 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat32 == VK_TRUE) && denorm_indep_32 && allow_ftz) ? 1 : 0; + graphics_key.use_ftz_f16 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat16 == VK_TRUE) && denorm_indep_all && allow_ftz) ? 1 : 0; + + if (allow_ftz && (graphics_key.use_ftz_f32 || graphics_key.use_ftz_f16)) { + LOG_INFO(Render_Vulkan, "Enabling per-pipeline FTZ (fast-math) for Qualcomm device: f32={} f16={}", + graphics_key.use_ftz_f32, graphics_key.use_ftz_f16); + } + if (current_pipeline) { GraphicsPipeline* const next{current_pipeline->Next(graphics_key)}; if (next) { @@ -690,7 +710,15 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( const auto runtime_info{MakeRuntimeInfo(programs, key, program, previous_stage)}; ConvertLegacyToGeneric(program, runtime_info); - const std::vector code{EmitSPIRV(profile, runtime_info, program, binding, this->optimize_spirv_output)}; + // Forward a pipeline-specific profile to the SPIR-V emitter so it can + // enable/disable fast-math (FTZ) optimizations per-pipeline. We use the + // GraphicsPipelineCacheKey's FTZ choice to decide whether to allow + // fast-math. When fast-math is enabled we keep need_fastmath_off=false + // (allow optimizations); otherwise we set it to true to prevent unsafe + // transformations. + Shader::Profile emit_profile = profile; + emit_profile.need_fastmath_off = (key.use_ftz_f32 == 0); + const std::vector code{EmitSPIRV(emit_profile, runtime_info, program, binding, this->optimize_spirv_output)}; device.SaveShader(code); modules[stage_index] = BuildShader(device, code); if (device.HasDebuggingToolAttached()) { From 2685f832e5b5a563e184d94cad06b0e1530f737a Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 09:22:57 -0400 Subject: [PATCH 15/23] Profilling FP16 to FTZ usage in QCOM drivers --- src/common/settings.h | 4 --- .../spirv/emit_spirv_floating_point.cpp | 24 ++------------- .../renderer_vulkan/vk_graphics_pipeline.h | 4 +-- .../renderer_vulkan/vk_pipeline_cache.cpp | 30 +++++++++++-------- 4 files changed, 22 insertions(+), 40 deletions(-) diff --git a/src/common/settings.h b/src/common/settings.h index c52c4ca345..0b61da88f9 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -547,10 +547,6 @@ struct Values { Specialization::Scalar}; SwitchableSetting force_unsupported_extensions{linkage, false, "force_unsupported_extensions", Category::RendererExtensions}; - // Developer toggle to enable FTZ (flush-to-zero) optimizations for testing on - // Qualcomm devices. Default false for safety; when true, pipeline code may - // emit FTZ-friendly SPIR-V and allow fast-math transforms for Adreno GPUs. - SwitchableSetting enable_ftz{linkage, false, "enable_ftz", Category::RendererExtensions}; SwitchableSetting provoking_vertex{linkage, false, "provoking_vertex", Category::RendererExtensions}; SwitchableSetting descriptor_indexing{linkage, false, "descriptor_indexing", Category::RendererExtensions}; SwitchableSetting sample_shading{linkage, false, "sample_shading", Category::RendererExtensions, Specialization::Paired}; diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp index 9f3ceb09ed..d921913b4a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_floating_point.cpp @@ -73,33 +73,15 @@ Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { } Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { - // Use OpFma only when fast-math is allowed. Some drivers/targets require - // conservative separate mul+add semantics; when need_fastmath_off is true - // we emit separate FMul + FAdd to avoid relying on fused behavior. - if (!ctx.profile.need_fastmath_off) { - return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); - } else { - const Id mul = ctx.OpFMul(ctx.F16[1], a, b); - return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], mul, c)); - } + return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); } Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { - if (!ctx.profile.need_fastmath_off) { - return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); - } else { - const Id mul = ctx.OpFMul(ctx.F32[1], a, b); - return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], mul, c)); - } + return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); } Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { - if (!ctx.profile.need_fastmath_off) { - return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); - } else { - const Id mul = ctx.OpFMul(ctx.F64[1], a, b); - return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], mul, c)); - } + return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); } Id EmitFPMax32(EmitContext& ctx, Id a, Id b) { diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 6703c8d8c5..f3571014b0 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -48,9 +48,9 @@ struct GraphicsPipelineCacheKey { return sizeof(unique_hashes) + state.Size() + sizeof(use_ftz_f32) + sizeof(use_ftz_f16); } }; -static_assert(std::has_unique_object_representations_v); +// The key is compared/hashed using a custom Size() and memcmp over the +// meaningful bytes. Ensure it's trivially copyable so memcmp/hash are safe. static_assert(std::is_trivially_copyable_v); -static_assert(std::is_trivially_constructible_v); } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 22245f91f6..b35a33c326 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -447,24 +447,19 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { graphics_key.state.Refresh(*maxwell3d, dynamic_features); // Decide per-pipeline FTZ (flush-to-zero) usage based on device float-controls - // properties and a dedicated enable_ftz developer toggle. FTZ is gated to - // Qualcomm drivers for initial testing to avoid widespread regressions. - const bool enable_ftz_setting = Settings::values.enable_ftz.GetValue(); + // properties and vendor-specific workarounds, going initially for Qualcomm drivers + const bool force_extensions = Settings::values.force_unsupported_extensions.GetValue(); + const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; const auto& float_control = device.FloatControlProperties(); const bool has_khr_float_controls = device.IsKhrShaderFloatControlsSupported(); const bool denorm_indep_all = float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL; const bool denorm_indep_32 = denorm_indep_all || float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY; - const bool is_qualcomm = device.GetDriverID() == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; - const bool allow_ftz = enable_ftz_setting && is_qualcomm; - - graphics_key.use_ftz_f32 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat32 == VK_TRUE) && denorm_indep_32 && allow_ftz) ? 1 : 0; - graphics_key.use_ftz_f16 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat16 == VK_TRUE) && denorm_indep_all && allow_ftz) ? 1 : 0; - - if (allow_ftz && (graphics_key.use_ftz_f32 || graphics_key.use_ftz_f16)) { - LOG_INFO(Render_Vulkan, "Enabling per-pipeline FTZ (fast-math) for Qualcomm device: f32={} f16={}", - graphics_key.use_ftz_f32, graphics_key.use_ftz_f16); - } + // Allow FTZ when device reports support the device is a Qualcomm driver + // (we allow Qualcomm to opt-in automatically to work around driver reporting differences). + const bool allow_ftz_override = force_extensions || is_qualcomm; + graphics_key.use_ftz_f32 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat32 == VK_TRUE) && denorm_indep_32 && allow_ftz_override) ? 1 : 0; + graphics_key.use_ftz_f16 = (has_khr_float_controls && (float_control.shaderDenormFlushToZeroFloat16 == VK_TRUE) && denorm_indep_all && allow_ftz_override) ? 1 : 0; if (current_pipeline) { GraphicsPipeline* const next{current_pipeline->Next(graphics_key)}; @@ -717,7 +712,16 @@ std::unique_ptr PipelineCache::CreateGraphicsPipeline( // (allow optimizations); otherwise we set it to true to prevent unsafe // transformations. Shader::Profile emit_profile = profile; + // Per-pipeline override: control fast-math (FTZ) for FP32 via the pipeline key. emit_profile.need_fastmath_off = (key.use_ftz_f32 == 0); + // Per-pipeline FP16 FTZ selection is stored in the pipeline key but was not + // previously propagated to the SPIR-V emitter. Some drivers (notably certain + // Qualcomm builds) report FP16 float-control support incorrectly. Respect the + // pipeline's FP16 FTZ choice here and disable emitting FP16 DenormFlush when + // the pipeline explicitly disables it. + if (key.use_ftz_f16 == 0) { + emit_profile.support_fp16_denorm_flush = false; + } const std::vector code{EmitSPIRV(emit_profile, runtime_info, program, binding, this->optimize_spirv_output)}; device.SaveShader(code); modules[stage_index] = BuildShader(device, code); From 380112bcb4937a083ea7b4a4ae53da546da670ff Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 09:59:01 -0400 Subject: [PATCH 16/23] Added QCOM helper for driver detections --- src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 6 +++--- src/video_core/shader_environment.h | 8 +++++++- src/video_core/vulkan_common/vulkan_device.h | 4 ++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index b35a33c326..a53cb13c99 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -376,13 +376,13 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA, .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS, - .has_broken_spirv_position_input = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, + .has_broken_spirv_position_input = device.IsQualcomm(), .has_broken_unsigned_image_offsets = false, .has_broken_signed_operations = false, .has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY, .ignore_nan_fp_comparisons = false, .has_broken_spirv_subgroup_mask_vector_extract_dynamic = - driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY, + device.IsQualcomm(), .has_broken_robust = device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, .min_ssbo_alignment = device.GetStorageBufferAlignment(), @@ -449,7 +449,7 @@ GraphicsPipeline* PipelineCache::CurrentGraphicsPipeline() { // Decide per-pipeline FTZ (flush-to-zero) usage based on device float-controls // properties and vendor-specific workarounds, going initially for Qualcomm drivers const bool force_extensions = Settings::values.force_unsupported_extensions.GetValue(); - const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; + const bool is_qualcomm = device.IsQualcomm(); const auto& float_control = device.FloatControlProperties(); const bool has_khr_float_controls = device.IsKhrShaderFloatControlsSupported(); const bool denorm_indep_all = float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL; diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 95c2d79277..27e9c078f2 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -222,7 +222,13 @@ template void SerializePipeline(const Key& key, const Envs& envs, const std::filesystem::path& filename, u32 cache_version) { static_assert(std::is_trivially_copyable_v); - static_assert(std::has_unique_object_representations_v); + // Note: we relax the unique object representation requirement because some + // pipeline/key types (e.g. GraphicsPipelineCacheKey) contain unions or + // bitfield-backed types that do not guarantee "unique object + // representations" across compilers/platforms. We still require + // trivially-copyable so the raw byte serialization is well-defined for a + // given build. Be aware that serialized blobs may not be portable across + // builds with different compilers or packing rules. SerializePipeline(std::span(reinterpret_cast(&key), sizeof(key)), std::span(envs.data(), envs.size()), filename, cache_version); } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 98059c5707..9d5c4c4eae 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -732,6 +732,10 @@ public: return properties.driver.driverID == VK_DRIVER_ID_MOLTENVK; } + bool IsQualcomm() const noexcept { + return properties.driver.driverID == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; + } + NvidiaArchitecture GetNvidiaArch() const noexcept { return nvidia_arch; } From 7bed3bb947b72e2dc6d42534c3684d2f6a6cca96 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 12:10:33 -0400 Subject: [PATCH 17/23] Disabling VK_KHR_shader_float_controls on QCOM and ARM --- .../vulkan_common/vulkan_device.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 97a3287d17..1a94c50e38 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1195,6 +1195,25 @@ bool Device::GetSuitability(bool requires_swapchain) { } } + // Some drivers (notably Qualcomm and certain ARM drivers) misreport the + // VK_KHR_shader_float_controls capabilities. Disable the extension and + // clear the reported properties unless the user explicitly forces + // unsupported extensions via settings. + { + const auto driver_id = properties.driver.driverID; + const bool force_extensions = Settings::values.force_unsupported_extensions.GetValue(); + if ((driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || + driver_id == VK_DRIVER_ID_ARM_PROPRIETARY) && + !force_extensions) { + LOG_WARNING(Render_Vulkan, + "Disabling VK_KHR_shader_float_controls for driver '{}' id={} due to unreliable float-control reporting", + properties.driver.driverName, driver_id); + extensions.shader_float_controls = false; + // Zero-out the structure to avoid accidental use of reported values. + properties.float_controls = {}; + } + } + // Unload extensions if feature support is insufficient. RemoveUnsuitableExtensions(); From 9600f99b1a22c19dc6fd8b6ca4cbee68653678ab Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 14:39:39 -0400 Subject: [PATCH 18/23] Maintance for older driver statements and new handling on QCOM drivers --- .../vulkan_common/vulkan_device.cpp | 67 +++++++++++++++++-- src/video_core/vulkan_common/vulkan_device.h | 36 ++++++++-- 2 files changed, 92 insertions(+), 11 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 1a94c50e38..1108578b6b 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -501,10 +501,25 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } if ((is_qualcomm || is_turnip) && !force_extensions) { - LOG_WARNING(Render_Vulkan, - "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); - RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, - VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + // Some Qualcomm/Turnip drivers have a broken implementation of + // VK_EXT_custom_border_color, but on certain devices the driver may + // still implement a subset of the extension's functions. If the + // driver reports any of the useful custom border color features, allow + // them; otherwise remove the extension to avoid crashes. + const bool has_custom_border_colors = + features.custom_border_color.customBorderColors || + features.custom_border_color.customBorderColorWithoutFormat; + if (!has_custom_border_colors) { + LOG_WARNING(Render_Vulkan, + "Qualcomm and Turnip drivers have broken VK_EXT_custom_border_color"); + RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, + VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + } else { + LOG_INFO(Render_Vulkan, + "Partial VK_EXT_custom_border_color support detected on driver '{}'; enabling available features", + properties.driver.driverName); + // Keep extensions.custom_border_color set based on RemoveUnsuitableExtensions later. + } } if (is_qualcomm || is_arm) { @@ -1034,6 +1049,25 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); + // Maintenance extensions may not have corresponding macros in older Vulkan + // headers. Detect them by name and enable them if present. + if (supported_extensions.contains("VK_KHR_maintenance1")) { + loaded_extensions.insert("VK_KHR_maintenance1"); + extensions.maintenance1 = true; + } + if (supported_extensions.contains("VK_KHR_maintenance2")) { + loaded_extensions.insert("VK_KHR_maintenance2"); + extensions.maintenance2 = true; + } + if (supported_extensions.contains("VK_KHR_maintenance3")) { + loaded_extensions.insert("VK_KHR_maintenance3"); + extensions.maintenance3 = true; + } + if (supported_extensions.contains("VK_KHR_maintenance4")) { + loaded_extensions.insert("VK_KHR_maintenance4"); + extensions.maintenance4 = true; + } + #undef FEATURE_EXTENSION #undef EXTENSION @@ -1254,9 +1288,30 @@ bool Device::GetSuitability(bool requires_swapchain) { } void Device::RemoveUnsuitableExtensions() { + // Restrict NV-specific extensions to NVIDIA drivers only. + if (!IsNvidia()) { + RemoveExtension(extensions.device_diagnostics_config, + VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME); + RemoveExtension(extensions.geometry_shader_passthrough, + VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME); + RemoveExtension(extensions.viewport_array2, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME); + RemoveExtension(extensions.viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME); + } + // VK_EXT_custom_border_color - extensions.custom_border_color = features.custom_border_color.customBorderColors && - features.custom_border_color.customBorderColorWithoutFormat; + // On most drivers we require both customBorderColors and + // customBorderColorWithoutFormat. However, some Qualcomm drivers expose a + // subset of the functionality; when running on Qualcomm, enable the + // extension if the driver reports any useful feature. + if (IsQualcomm()) { + extensions.custom_border_color = + features.custom_border_color.customBorderColors || + features.custom_border_color.customBorderColorWithoutFormat; + } else { + extensions.custom_border_color = + features.custom_border_color.customBorderColors && + features.custom_border_color.customBorderColorWithoutFormat; + } RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 9d5c4c4eae..cfc684ae07 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -445,6 +445,26 @@ public: return extensions.shader_float_controls; } + /// Returns true if VK_KHR_maintenance1 is enabled. + bool IsKhrMaintenance1Supported() const { + return extensions.maintenance1; + } + + /// Returns true if VK_KHR_maintenance2 is enabled. + bool IsKhrMaintenance2Supported() const { + return extensions.maintenance2; + } + + /// Returns true if VK_KHR_maintenance3 is enabled. + bool IsKhrMaintenance3Supported() const { + return extensions.maintenance3; + } + + /// Returns true if VK_KHR_maintenance4 is enabled. + bool IsKhrMaintenance4Supported() const { + return extensions.maintenance4; + } + /// Returns true if VK_KHR_sampler_mirror_clamp_to_edge is enabled. bool IsKhrSamplerMirrorClampToEdgeSupported() const { return extensions.sampler_mirror_clamp_to_edge; @@ -792,11 +812,17 @@ private: #define EXTENSION(prefix, macro_name, var_name) bool var_name{}; #define FEATURE(prefix, struct_name, macro_name, var_name) bool var_name{}; - FOR_EACH_VK_FEATURE_1_1(FEATURE); - FOR_EACH_VK_FEATURE_1_2(FEATURE); - FOR_EACH_VK_FEATURE_1_3(FEATURE); - FOR_EACH_VK_FEATURE_EXT(FEATURE); - FOR_EACH_VK_EXTENSION(EXTENSION); + FOR_EACH_VK_FEATURE_1_1(FEATURE); + FOR_EACH_VK_FEATURE_1_2(FEATURE); + FOR_EACH_VK_FEATURE_1_3(FEATURE); + FOR_EACH_VK_FEATURE_EXT(FEATURE); + FOR_EACH_VK_EXTENSION(EXTENSION); + + // Maintenance extensions (may not be present in older Vulkan headers). + bool maintenance1{}; + bool maintenance2{}; + bool maintenance3{}; + bool maintenance4{}; #undef EXTENSION #undef FEATURE From 88555a17befa446f9f5d68208b938820d93f76b6 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 15:55:10 -0400 Subject: [PATCH 19/23] Forcing hardware handling for decode formats on Android --- src/common/android/id_cache.cpp | 9 +++++++++ src/common/settings.h | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/common/android/id_cache.cpp b/src/common/android/id_cache.cpp index 1198833996..96e458a9f1 100644 --- a/src/common/android/id_cache.cpp +++ b/src/common/android/id_cache.cpp @@ -10,6 +10,8 @@ #include "video_core/rasterizer_interface.h" #include "common/android/multiplayer/multiplayer.h" #include +#include "common/settings.h" +#include "common/logging/log.h" static JavaVM *s_java_vm; @@ -524,6 +526,13 @@ namespace Common::Android { s_patch_title_id_field = env->GetFieldID(patch_class, "titleId", "Ljava/lang/String;"); env->DeleteLocalRef(patch_class); + // Prefer hardware decoding on Android by default, forcing this setting will + // make the native side attempt GPU decoding first. If the platform lacks a usable + // FFmpeg HW device, FFmpeg will fall back to CPU automatically. + Settings::values.nvdec_emulation.SetValue(Settings::NvdecEmulation::Gpu); + LOG_INFO(HW_GPU, "Android JNI_OnLoad: forced nvdec_emulation = GPU"); + + const jclass double_class = env->FindClass("java/lang/Double"); s_double_class = reinterpret_cast(env->NewGlobalRef(double_class)); s_double_constructor = env->GetMethodID(double_class, "", "(D)V"); diff --git a/src/common/settings.h b/src/common/settings.h index 0b61da88f9..718c7d4e42 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -349,7 +349,9 @@ struct Values { linkage, true, "use_asynchronous_gpu_emulation", Category::Renderer}; SwitchableSetting accelerate_astc{linkage, #ifdef ANDROID - AstcDecodeMode::Cpu, + // Most modern Android devices have native ASTC support + // and benefit from GPU decoding. Default to GPU there. + AstcDecodeMode::Gpu, #else AstcDecodeMode::Gpu, #endif From 5a03ef7fba5b12b49bf2e0227b99b1022e685810 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 17:37:16 -0400 Subject: [PATCH 20/23] Disabling BGR5 emulation format on 8 Elite stock drivers --- .../vulkan_common/vulkan_device.cpp | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 1108578b6b..984f2e28e2 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -712,13 +712,29 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); cant_blit_msaa = true; } - has_broken_compute = - CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && - !Settings::values.enable_compute_pipelines.GetValue(); - if (is_intel_anv || (is_qualcomm && !is_s8gen2)) { - LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format"); +has_broken_compute = + CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && + !Settings::values.enable_compute_pipelines.GetValue(); + +// Qualcomm driver version where VK_KHR_maintenance5 and A1B5G5R5 become reliable +// Check if VK_KHR_maintenance5 is supported +constexpr uint32_t QUALCOMM_FIXED_DRIVER_VERSION = VK_MAKE_VERSION(512, 800, 1); +bool has_maintenance5 = HasExtension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); +must_emulate_bgr565 = false; // Default: assume emulation required + +if (is_intel_anv) { + LOG_WARNING(Render_Vulkan, "Intel ANV driver does not support native BGR format"); + must_emulate_bgr565 = true; +} else if (is_qualcomm) { + if (has_maintenance5 && properties.properties.driverVersion >= QUALCOMM_FIXED_DRIVER_VERSION) { + LOG_INFO(Render_Vulkan, "Qualcomm driver supports VK_KHR_maintenance5, disabling BGR emulation"); + must_emulate_bgr565 = false; + } else { + LOG_WARNING(Render_Vulkan, "Qualcomm driver doesn't support native BGR, emulating formats"); must_emulate_bgr565 = true; } +} + } if (extensions.push_descriptor && is_intel_anv) { const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version >= VK_MAKE_API_VERSION(0, 22, 3, 0) && @@ -1067,6 +1083,26 @@ bool Device::GetSuitability(bool requires_swapchain) { loaded_extensions.insert("VK_KHR_maintenance4"); extensions.maintenance4 = true; } + if (supported_extensions.contains("VK_KHR_maintenance5")) { + loaded_extensions.insert("VK_KHR_maintenance5"); + extensions.maintenance4 = true; + } + if (supported_extensions.contains("VK_KHR_maintenance6")) { + loaded_extensions.insert("VK_KHR_maintenance6"); + extensions.maintenance4 = true; + } + if (supported_extensions.contains("VK_KHR_maintenance7")) { + loaded_extensions.insert("VK_KHR_maintenance7"); + extensions.maintenance4 = true; + } + if (supported_extensions.contains("VK_KHR_maintenance8")) { + loaded_extensions.insert("VK_KHR_maintenance8"); + extensions.maintenance4 = true; + } + if (supported_extensions.contains("VK_KHR_maintenance9")) { + loaded_extensions.insert("VK_KHR_maintenance9"); + extensions.maintenance4 = true; + } #undef FEATURE_EXTENSION #undef EXTENSION From bb1d1e484d857520851441f10831b8121598e357 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 17:47:05 -0400 Subject: [PATCH 21/23] Added Maintenance EXT's for better driver compatibility --- .../vulkan_common/vulkan_device.cpp | 10 ++++---- src/video_core/vulkan_common/vulkan_device.h | 25 +++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 984f2e28e2..2233bfce35 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1085,23 +1085,23 @@ bool Device::GetSuitability(bool requires_swapchain) { } if (supported_extensions.contains("VK_KHR_maintenance5")) { loaded_extensions.insert("VK_KHR_maintenance5"); - extensions.maintenance4 = true; + extensions.maintenance5 = true; } if (supported_extensions.contains("VK_KHR_maintenance6")) { loaded_extensions.insert("VK_KHR_maintenance6"); - extensions.maintenance4 = true; + extensions.maintenance6 = true; } if (supported_extensions.contains("VK_KHR_maintenance7")) { loaded_extensions.insert("VK_KHR_maintenance7"); - extensions.maintenance4 = true; + extensions.maintenance7 = true; } if (supported_extensions.contains("VK_KHR_maintenance8")) { loaded_extensions.insert("VK_KHR_maintenance8"); - extensions.maintenance4 = true; + extensions.maintenance8 = true; } if (supported_extensions.contains("VK_KHR_maintenance9")) { loaded_extensions.insert("VK_KHR_maintenance9"); - extensions.maintenance4 = true; + extensions.maintenance9 = true; } #undef FEATURE_EXTENSION diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cfc684ae07..567663c629 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -465,6 +465,31 @@ public: return extensions.maintenance4; } + /// Returns true if VK_KHR_maintenance5 is enabled. + bool IsKhrMaintenance5Supported() const { + return extensions.maintenance5; + } + + /// Returns true if VK_KHR_maintenance6 is enabled. + bool IsKhrMaintenance6Supported() const { + return extensions.maintenance6; + } + + /// Returns true if VK_KHR_maintenance7 is enabled. + bool IsKhrMaintenance7Supported() const { + return extensions.maintenance7; + } + + /// Returns true if VK_KHR_maintenance8 is enabled. + bool IsKhrMaintenance8Supported() const { + return extensions.maintenance8; + } + + /// Returns true if VK_KHR_maintenance9 is enabled. + bool IsKhrMaintenance9Supported() const { + return extensions.maintenance9; + } + /// Returns true if VK_KHR_sampler_mirror_clamp_to_edge is enabled. bool IsKhrSamplerMirrorClampToEdgeSupported() const { return extensions.sampler_mirror_clamp_to_edge; From 672011f3028d7d4aea5e126ed7bdf7a056fde497 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 6 Nov 2025 18:03:30 -0400 Subject: [PATCH 22/23] Added Maintenance5 to 9 headers --- src/video_core/vulkan_common/vulkan_device.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 567663c629..1f10d1799a 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -848,6 +848,11 @@ private: bool maintenance2{}; bool maintenance3{}; bool maintenance4{}; + bool maintenance5{}; + bool maintenance6{}; + bool maintenance7{}; + bool maintenance8{}; + bool maintenance9{}; #undef EXTENSION #undef FEATURE From 8e80396ee0c01056ca02404958e74b58568c94d0 Mon Sep 17 00:00:00 2001 From: lizzie Date: Thu, 6 Nov 2025 22:49:49 +0000 Subject: [PATCH 23/23] fix --- src/video_core/vulkan_common/vulkan.h | 28 ++++++ .../vulkan_common/vulkan_device.cpp | 85 +++++-------------- src/video_core/vulkan_common/vulkan_device.h | 22 +++-- 3 files changed, 60 insertions(+), 75 deletions(-) diff --git a/src/video_core/vulkan_common/vulkan.h b/src/video_core/vulkan_common/vulkan.h index 13f679ff54..7092243f7f 100644 --- a/src/video_core/vulkan_common/vulkan.h +++ b/src/video_core/vulkan_common/vulkan.h @@ -22,6 +22,34 @@ #include +#ifndef VK_KHR_MAINTENANCE_1_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_1_EXTENSION_NAME "VK_KHR_maintenance1" +#endif +#ifndef VK_KHR_MAINTENANCE_2_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_2_EXTENSION_NAME "VK_KHR_maintenance2" +#endif +#ifndef VK_KHR_MAINTENANCE_3_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_3_EXTENSION_NAME "VK_KHR_maintenance3" +#endif +#ifndef VK_KHR_MAINTENANCE_4_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_4_EXTENSION_NAME "VK_KHR_maintenance4" +#endif +#ifndef VK_KHR_MAINTENANCE_5_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_5_EXTENSION_NAME "VK_KHR_maintenance5" +#endif +#ifndef VK_KHR_MAINTENANCE_6_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_6_EXTENSION_NAME "VK_KHR_maintenance6" +#endif +#ifndef VK_KHR_MAINTENANCE_7_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_7_EXTENSION_NAME "VK_KHR_maintenance7" +#endif +#ifndef VK_KHR_MAINTENANCE_8_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_8_EXTENSION_NAME "VK_KHR_maintenance8" +#endif +#ifndef VK_KHR_MAINTENANCE_9_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_9_EXTENSION_NAME "VK_KHR_maintenance9" +#endif + // Sanitize macros #undef CreateEvent #undef CreateSemaphore diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 2233bfce35..9b4d3ccbc8 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -603,7 +603,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version >= VK_MAKE_API_VERSION(0, 0, 676, 0) && version < VK_MAKE_API_VERSION(0, 0, 680, 0) && !force_extensions) { - // Arm Mali Inmortalis drivers have broken extendedDynamicState2LogicOp. + // Arm Mali Inmortalis drivers have broken extendedDynamicState2LogicOp. // Qualcomm Adreno 7xx drivers do not properly support extended_dynamic_state2. LOG_WARNING(Render_Vulkan, "Qualcomm Adreno 7xx and Arm Mali Inmortalis drivers have broken VK_EXT_extended_dynamic_state2"); @@ -712,29 +712,27 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR LOG_WARNING(Render_Vulkan, "Intel proprietary drivers do not support MSAA image blits"); cant_blit_msaa = true; } -has_broken_compute = - CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && - !Settings::values.enable_compute_pipelines.GetValue(); + has_broken_compute = + CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && + !Settings::values.enable_compute_pipelines.GetValue(); -// Qualcomm driver version where VK_KHR_maintenance5 and A1B5G5R5 become reliable -// Check if VK_KHR_maintenance5 is supported -constexpr uint32_t QUALCOMM_FIXED_DRIVER_VERSION = VK_MAKE_VERSION(512, 800, 1); -bool has_maintenance5 = HasExtension(VK_KHR_MAINTENANCE_5_EXTENSION_NAME); -must_emulate_bgr565 = false; // Default: assume emulation required - -if (is_intel_anv) { - LOG_WARNING(Render_Vulkan, "Intel ANV driver does not support native BGR format"); - must_emulate_bgr565 = true; -} else if (is_qualcomm) { - if (has_maintenance5 && properties.properties.driverVersion >= QUALCOMM_FIXED_DRIVER_VERSION) { - LOG_INFO(Render_Vulkan, "Qualcomm driver supports VK_KHR_maintenance5, disabling BGR emulation"); - must_emulate_bgr565 = false; - } else { - LOG_WARNING(Render_Vulkan, "Qualcomm driver doesn't support native BGR, emulating formats"); + must_emulate_bgr565 = false; // Default: assume emulation required + if (is_intel_anv) { + LOG_WARNING(Render_Vulkan, "Intel ANV driver does not support native BGR format"); must_emulate_bgr565 = true; + } else if (is_qualcomm) { + // Qualcomm driver version where VK_KHR_maintenance5 and A1B5G5R5 become reliable + constexpr uint32_t QUALCOMM_FIXED_DRIVER_VERSION = VK_MAKE_VERSION(512, 800, 1); + // Check if VK_KHR_maintenance5 is supported + if (extensions.maintenance5 && properties.properties.driverVersion >= QUALCOMM_FIXED_DRIVER_VERSION) { + LOG_INFO(Render_Vulkan, "Qualcomm driver supports VK_KHR_maintenance5, disabling BGR emulation"); + must_emulate_bgr565 = false; + } else { + LOG_WARNING(Render_Vulkan, "Qualcomm driver doesn't support native BGR, emulating formats"); + must_emulate_bgr565 = true; + } } -} - } + if (extensions.push_descriptor && is_intel_anv) { const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version >= VK_MAKE_API_VERSION(0, 22, 3, 0) && @@ -1065,58 +1063,19 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); - // Maintenance extensions may not have corresponding macros in older Vulkan - // headers. Detect them by name and enable them if present. - if (supported_extensions.contains("VK_KHR_maintenance1")) { - loaded_extensions.insert("VK_KHR_maintenance1"); - extensions.maintenance1 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance2")) { - loaded_extensions.insert("VK_KHR_maintenance2"); - extensions.maintenance2 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance3")) { - loaded_extensions.insert("VK_KHR_maintenance3"); - extensions.maintenance3 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance4")) { - loaded_extensions.insert("VK_KHR_maintenance4"); - extensions.maintenance4 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance5")) { - loaded_extensions.insert("VK_KHR_maintenance5"); - extensions.maintenance5 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance6")) { - loaded_extensions.insert("VK_KHR_maintenance6"); - extensions.maintenance6 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance7")) { - loaded_extensions.insert("VK_KHR_maintenance7"); - extensions.maintenance7 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance8")) { - loaded_extensions.insert("VK_KHR_maintenance8"); - extensions.maintenance8 = true; - } - if (supported_extensions.contains("VK_KHR_maintenance9")) { - loaded_extensions.insert("VK_KHR_maintenance9"); - extensions.maintenance9 = true; - } - #undef FEATURE_EXTENSION #undef EXTENSION // Some extensions are mandatory. Check those. #define CHECK_EXTENSION(extension_name) \ if (!loaded_extensions.contains(extension_name)) { \ - LOG_ERROR(Render_Vulkan, "Missing required extension {}", extension_name); \ - suitable = false; \ + LOG_ERROR(Render_Vulkan, "Missing required extension " extension_name); \ + suitable = false; \ } #define LOG_EXTENSION(extension_name) \ if (!loaded_extensions.contains(extension_name)) { \ - LOG_INFO(Render_Vulkan, "Device doesn't support extension {}", extension_name); \ + LOG_INFO(Render_Vulkan, "Device doesn't support extension " extension_name); \ } FOR_EACH_VK_RECOMMENDED_EXTENSION(LOG_EXTENSION); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 1f10d1799a..a751bc685a 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -90,7 +90,16 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(NV, VIEWPORT_SWIZZLE, viewport_swizzle) \ EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \ EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \ - EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) + EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) \ + EXTENSION(KHR, MAINTENANCE_1, maintenance1) \ + EXTENSION(KHR, MAINTENANCE_2, maintenance2) \ + EXTENSION(KHR, MAINTENANCE_3, maintenance3) \ + EXTENSION(KHR, MAINTENANCE_4, maintenance4) \ + EXTENSION(KHR, MAINTENANCE_5, maintenance5) \ + EXTENSION(KHR, MAINTENANCE_6, maintenance6) \ + EXTENSION(KHR, MAINTENANCE_7, maintenance7) \ + EXTENSION(KHR, MAINTENANCE_8, maintenance8) \ + EXTENSION(KHR, MAINTENANCE_9, maintenance9) \ // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ @@ -843,17 +852,6 @@ private: FOR_EACH_VK_FEATURE_EXT(FEATURE); FOR_EACH_VK_EXTENSION(EXTENSION); - // Maintenance extensions (may not be present in older Vulkan headers). - bool maintenance1{}; - bool maintenance2{}; - bool maintenance3{}; - bool maintenance4{}; - bool maintenance5{}; - bool maintenance6{}; - bool maintenance7{}; - bool maintenance8{}; - bool maintenance9{}; - #undef EXTENSION #undef FEATURE };