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 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_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 650c8e07ed..f3571014b0 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,12 +45,12 @@ 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); +// 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 10ee14773f..a53cb13c99 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 = @@ -368,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(), @@ -438,6 +446,21 @@ 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 vendor-specific workarounds, going initially for Qualcomm drivers + const bool force_extensions = Settings::values.force_unsupported_extensions.GetValue(); + 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; + const bool denorm_indep_32 = denorm_indep_all || float_control.denormBehaviorIndependence == VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY; + + // 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)}; if (next) { @@ -682,7 +705,24 @@ 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; + // 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); if (device.HasDebuggingToolAttached()) { 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.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 39ecc56bfb..9b4d3ccbc8 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -501,24 +501,38 @@ 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) { + if (is_qualcomm || is_arm) { if (!force_extensions) { LOG_WARNING(Render_Vulkan, "Qualcomm drivers have a slow VK_KHR_push_descriptor implementation"); 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 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; @@ -585,13 +599,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); @@ -700,10 +715,24 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR 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"); + + 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) && @@ -1040,13 +1069,13 @@ bool Device::GetSuitability(bool requires_swapchain) { // 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); @@ -1178,9 +1207,55 @@ 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); + } + } + + // 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(); + // 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; @@ -1208,9 +1283,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); @@ -1226,6 +1322,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, diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cb13f28523..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) \ @@ -445,6 +454,56 @@ 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_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; + } + /// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout. bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const { return extensions.workgroup_memory_explicit_layout; @@ -727,6 +786,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; } @@ -783,11 +846,11 @@ 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); #undef EXTENSION #undef FEATURE