From 217759925e40019e61464ac9b7c5c0fcec41d99d Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 20 Nov 2025 01:11:17 -0300 Subject: [PATCH] [vk] Refactor DynamicState and ExtendedState to granular per-feature enabling * Also giving maintance to driver features and unused extensions Signed-off-by: Caio Oliveira --- .../settings/model/view/SettingsItem.kt | 12 +- .../app/src/main/res/values/strings.xml | 2 +- src/qt_common/config/shared_translation.cpp | 2 +- .../renderer_vulkan/fixed_pipeline_state.cpp | 59 ++- .../renderer_vulkan/fixed_pipeline_state.h | 100 ++++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 141 +++--- .../renderer_vulkan/vk_pipeline_cache.cpp | 104 ++++- .../renderer_vulkan/vk_pipeline_cache.h | 7 + .../renderer_vulkan/vk_rasterizer.cpp | 91 ++-- src/video_core/vulkan_common/vulkan.h | 28 ++ .../vulkan_common/vulkan_device.cpp | 409 ++++++++++++++---- src/video_core/vulkan_common/vulkan_device.h | 161 ++++++- 12 files changed, 896 insertions(+), 220 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index d8100e07e2..13eb3071c7 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -141,16 +141,16 @@ abstract class SettingsItem( ) put( SwitchSetting( - BooleanSetting.RENDERER_PROVOKING_VERTEX, - titleId = R.string.provoking_vertex, - descriptionId = R.string.provoking_vertex_description + BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE, + titleId = R.string.vertex_input_dynamic_state, + descriptionId = R.string.vertex_input_dynamic_state_description ) ) put( SwitchSetting( - BooleanSetting.RENDERER_VERTEX_INPUT_DYNAMIC_STATE, - titleId = R.string.vertex_input_dynamic_state, - descriptionId = R.string.vertex_input_dynamic_state_description + BooleanSetting.RENDERER_PROVOKING_VERTEX, + titleId = R.string.provoking_vertex, + descriptionId = R.string.provoking_vertex_description ) ) put( diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 2d071b45d5..143517134e 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -95,7 +95,7 @@ Controls the number of features that can be used in Extended Dynamic State. Higher numbers allow for more features and can increase performance, but may cause issues with some drivers and vendors. The default value may vary depending on your system and hardware capabilities. This value can be changed until stability and a better visual quality are achieved. Disabled Vertex Input Dynamic State - Enables vertex input dynamic state feature for better quality and performance. + Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs. Provoking Vertex Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs. Descriptor Indexing diff --git a/src/qt_common/config/shared_translation.cpp b/src/qt_common/config/shared_translation.cpp index f8295e61c0..103bd7549f 100644 --- a/src/qt_common/config/shared_translation.cpp +++ b/src/qt_common/config/shared_translation.cpp @@ -332,7 +332,7 @@ std::unique_ptr InitializeTranslations(QObject* parent) INSERT(Settings, vertex_input_dynamic_state, tr("Vertex Input Dynamic State"), - tr("Enables vertex input dynamic state feature for better quality and performance.")); + tr("Improves lighting and vertex handling in certain games. Only supported on Vulkan 1.0+ GPUs.")); INSERT(Settings, provoking_vertex, diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index e643e98ead..e5fbe03b75 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -58,12 +58,57 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; raw1 = 0; - extended_dynamic_state.Assign(features.has_extended_dynamic_state ? 1 : 0); - extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2 ? 1 : 0); - extended_dynamic_state_2_extra.Assign(features.has_extended_dynamic_state_2_extra ? 1 : 0); - extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0); - extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0); - dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0); + raw1_eds3_extended = 0; + pad_align_u64 = 0; + + // EDS1 + extended_dynamic_state.Assign(features.has_extended_dynamic_state); + + // EDS2 + extended_dynamic_state_2.Assign(features.has_extended_dynamic_state_2); + extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op); + extended_dynamic_state_2_patch_control_points.Assign( + features.has_extended_dynamic_state_2_patch_control_points); + + // EDS3 - Blending/Enables + extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend); + extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables); + + // EDS3 - Granular features + extended_dynamic_state_3_depth_clamp.Assign(features.has_extended_dynamic_state_3_depth_clamp); + extended_dynamic_state_3_logic_op_enable.Assign( + features.has_extended_dynamic_state_3_logic_op_enable); + extended_dynamic_state_3_tessellation_domain_origin.Assign( + features.has_extended_dynamic_state_3_tessellation_domain_origin); + extended_dynamic_state_3_polygon_mode.Assign( + features.has_extended_dynamic_state_3_polygon_mode); + extended_dynamic_state_3_rasterization_samples.Assign( + features.has_extended_dynamic_state_3_rasterization_samples); + extended_dynamic_state_3_sample_mask.Assign(features.has_extended_dynamic_state_3_sample_mask); + extended_dynamic_state_3_alpha_to_coverage_enable.Assign( + features.has_extended_dynamic_state_3_alpha_to_coverage_enable); + extended_dynamic_state_3_alpha_to_one_enable.Assign( + features.has_extended_dynamic_state_3_alpha_to_one_enable); + extended_dynamic_state_3_depth_clip_enable.Assign( + features.has_extended_dynamic_state_3_depth_clip_enable); + extended_dynamic_state_3_depth_clip_negative_one_to_one.Assign( + features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one); + extended_dynamic_state_3_line_rasterization_mode.Assign( + features.has_extended_dynamic_state_3_line_rasterization_mode); + extended_dynamic_state_3_line_stipple_enable.Assign( + features.has_extended_dynamic_state_3_line_stipple_enable); + extended_dynamic_state_3_provoking_vertex_mode.Assign( + features.has_extended_dynamic_state_3_provoking_vertex_mode); + extended_dynamic_state_3_conservative_rasterization_mode.Assign( + features.has_extended_dynamic_state_3_conservative_rasterization_mode); + extended_dynamic_state_3_sample_locations_enable.Assign( + features.has_extended_dynamic_state_3_sample_locations_enable); + extended_dynamic_state_3_rasterization_stream.Assign( + features.has_extended_dynamic_state_3_rasterization_stream); + + // Vertex Input + dynamic_vertex_input.Assign(features.has_dynamic_vertex_input); + xfb_enabled.Assign(regs.transform_feedback_enabled != 0); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); polygon_mode.Assign(PackPolygonMode(VideoCore::EffectivePolygonMode(regs))); @@ -158,7 +203,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe return static_cast(array.stride.Value()); }); } - if (!extended_dynamic_state_2_extra) { + if (!extended_dynamic_state_2_logic_op) { dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2); } if (!extended_dynamic_state_3_blend) { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index f0b021ca08..a443266ebd 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -18,12 +21,35 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct DynamicFeatures { - bool has_extended_dynamic_state; - bool has_extended_dynamic_state_2; - bool has_extended_dynamic_state_2_extra; - bool has_extended_dynamic_state_3_blend; - bool has_extended_dynamic_state_3_enables; - bool has_dynamic_vertex_input; + // VK_EXT_extended_dynamic_state (EDS1) - Bit 0 + bool has_extended_dynamic_state : 1; + // VK_EXT_extended_dynamic_state2 (EDS2) - Bits 1-3 + bool has_extended_dynamic_state_2 : 1; // Core EDS2 + bool has_extended_dynamic_state_2_logic_op : 1; // LogicOp + bool has_extended_dynamic_state_2_patch_control_points : 1; // Tessellation + // VK_EXT_extended_dynamic_state3 (EDS3) - Bits 4-5 + bool has_extended_dynamic_state_3_blend : 1; // Blending composite + bool has_extended_dynamic_state_3_enables : 1; // Enables composite + // VK_EXT_vertex_input_dynamic_state - Bit 6 + bool has_dynamic_vertex_input : 1; + // EDS3 Granular Features - Bits 7-15 + bool has_extended_dynamic_state_3_depth_clamp; + bool has_extended_dynamic_state_3_logic_op_enable; + bool has_extended_dynamic_state_3_tessellation_domain_origin; + bool has_extended_dynamic_state_3_polygon_mode; + bool has_extended_dynamic_state_3_rasterization_samples; + bool has_extended_dynamic_state_3_sample_mask; + bool has_extended_dynamic_state_3_alpha_to_coverage_enable : 1; + bool has_extended_dynamic_state_3_alpha_to_one_enable : 1; + bool has_extended_dynamic_state_3_depth_clip_enable : 1; + // EDS3 Additional Features - Bits 16-22 + bool has_extended_dynamic_state_3_depth_clip_negative_one_to_one : 1; + bool has_extended_dynamic_state_3_line_rasterization_mode : 1; + bool has_extended_dynamic_state_3_line_stipple_enable : 1; + bool has_extended_dynamic_state_3_provoking_vertex_mode : 1; + bool has_extended_dynamic_state_3_conservative_rasterization_mode : 1; + bool has_extended_dynamic_state_3_sample_locations_enable : 1; + bool has_extended_dynamic_state_3_rasterization_stream : 1; }; struct FixedPipelineState { @@ -184,23 +210,56 @@ struct FixedPipelineState { union { u32 raw1; + // EDS1 - Bit 0 BitField<0, 1, u32> extended_dynamic_state; - BitField<1, 1, u32> extended_dynamic_state_2; - BitField<2, 1, u32> extended_dynamic_state_2_extra; - BitField<3, 1, u32> extended_dynamic_state_3_blend; - BitField<4, 1, u32> extended_dynamic_state_3_enables; - BitField<5, 1, u32> dynamic_vertex_input; - BitField<6, 1, u32> xfb_enabled; - BitField<7, 1, u32> ndc_minus_one_to_one; - BitField<8, 2, u32> polygon_mode; - BitField<10, 2, u32> tessellation_primitive; - BitField<12, 2, u32> tessellation_spacing; - BitField<14, 1, u32> tessellation_clockwise; - BitField<15, 5, u32> patch_control_points_minus_one; + // EDS2 - Bits 1-3 + BitField<1, 1, u32> extended_dynamic_state_2; + BitField<2, 1, u32> extended_dynamic_state_2_logic_op; + BitField<3, 1, u32> extended_dynamic_state_2_patch_control_points; + + // EDS3 Blending/Enables - Bits 4-5 + BitField<4, 1, u32> extended_dynamic_state_3_blend; + BitField<5, 1, u32> extended_dynamic_state_3_enables; + + // Vertex Input - Bit 6 + BitField<6, 1, u32> dynamic_vertex_input; + + // Other state - Bits 7-19 + BitField<7, 1, u32> xfb_enabled; + BitField<8, 1, u32> ndc_minus_one_to_one; + BitField<9, 2, u32> polygon_mode; + BitField<11, 2, u32> tessellation_primitive; + BitField<13, 2, u32> tessellation_spacing; + BitField<15, 1, u32> tessellation_clockwise; + BitField<16, 5, u32> patch_control_points_minus_one; + + // Topology and MSAA - Bits 24-31 BitField<24, 4, Maxwell::PrimitiveTopology> topology; BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; }; + + union { + u32 raw1_eds3_extended; + // EDS3 Additional Features - Bits 0-15 + BitField<0, 1, u32> extended_dynamic_state_3_depth_clamp; + BitField<1, 1, u32> extended_dynamic_state_3_logic_op_enable; + BitField<2, 1, u32> extended_dynamic_state_3_tessellation_domain_origin; + BitField<3, 1, u32> extended_dynamic_state_3_polygon_mode; + BitField<4, 1, u32> extended_dynamic_state_3_rasterization_samples; + BitField<5, 1, u32> extended_dynamic_state_3_sample_mask; + BitField<6, 1, u32> extended_dynamic_state_3_alpha_to_coverage_enable; + BitField<7, 1, u32> extended_dynamic_state_3_alpha_to_one_enable; + BitField<8, 1, u32> extended_dynamic_state_3_depth_clip_enable; + BitField<9, 1, u32> extended_dynamic_state_3_depth_clip_negative_one_to_one; + BitField<10, 1, u32> extended_dynamic_state_3_line_rasterization_mode; + BitField<11, 1, u32> extended_dynamic_state_3_line_stipple_enable; + BitField<12, 1, u32> extended_dynamic_state_3_provoking_vertex_mode; + BitField<13, 1, u32> extended_dynamic_state_3_conservative_rasterization_mode; + BitField<14, 1, u32> extended_dynamic_state_3_sample_locations_enable; + BitField<15, 1, u32> extended_dynamic_state_3_rasterization_stream; + }; + union { u32 raw2; BitField<1, 3, u32> alpha_test_func; @@ -215,12 +274,13 @@ struct FixedPipelineState { BitField<16, 1, u32> alpha_to_one_enabled; BitField<17, 3, Tegra::Engines::Maxwell3D::EngineHint> app_stage; }; - std::array color_formats; - u32 alpha_test_ref; u32 point_size; + std::array color_formats; std::array viewport_swizzles; + u32 pad_align_u64; + union { u64 attribute_types; // Used with VK_EXT_vertex_input_dynamic_state u64 enabled_divisors; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index ca58e3fb4c..0f778b316c 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -837,13 +837,39 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .pAttachments = cb_attachments.data(), .blendConstants = {} }; - static_vector dynamic_states{ - VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR, - VK_DYNAMIC_STATE_DEPTH_BIAS, VK_DYNAMIC_STATE_BLEND_CONSTANTS, - VK_DYNAMIC_STATE_DEPTH_BOUNDS, VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK, - VK_DYNAMIC_STATE_STENCIL_WRITE_MASK, VK_DYNAMIC_STATE_STENCIL_REFERENCE, - VK_DYNAMIC_STATE_LINE_WIDTH, - }; + // Base Vulkan Dynamic States - Always active (independent of EDS) + // Granular fallback: Each state added only if device supports it (protection against broken + // drivers) + static_vector dynamic_states; + if (device.SupportsDynamicViewport()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); + } + if (device.SupportsDynamicScissor()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); + } + if (device.SupportsDynamicLineWidth()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); + } + if (device.SupportsDynamicDepthBias()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); + } + if (device.SupportsDynamicBlendConstants()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); + } + if (device.SupportsDynamicDepthBounds()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); + } + if (device.SupportsDynamicStencilCompareMask()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); + } + if (device.SupportsDynamicStencilWriteMask()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); + } + if (device.SupportsDynamicStencilReference()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); + } + + // EDS1 - Extended Dynamic State if (key.state.extended_dynamic_state) { std::vector extended{ VK_DYNAMIC_STATE_CULL_MODE_EXT, @@ -855,54 +881,67 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, VK_DYNAMIC_STATE_STENCIL_OP_EXT, }; - if (!device.IsExtVertexInputDynamicStateSupported()) { - extended.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); - } - if (key.state.dynamic_vertex_input) { - dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); - } dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); - if (key.state.extended_dynamic_state_2) { - static constexpr std::array extended2{ - VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT, - VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT, - VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end()); - } - if (key.state.extended_dynamic_state_2_extra) { - dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); - } - if (key.state.extended_dynamic_state_3_blend) { - static constexpr std::array extended3{ - VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, - VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, - VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, - - // VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); - } - if (key.state.extended_dynamic_state_3_enables) { - static constexpr std::array extended3{ - VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT, - VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT, - - // additional state3 extensions - VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT, - - VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT, - - VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT, - VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT, - VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT, - VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + + // Note: VERTEX_INPUT_BINDING_STRIDE is part of EDS1, not VIDS + // When VIDS is disabled, we still need dynamic stride with BindVertexBuffers2EXT + if (!key.state.dynamic_vertex_input) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); } } + // Vertex Input Dynamic State (independent toggle, replaces VERTEX_INPUT_BINDING_STRIDE when + // enabled) + if (key.state.dynamic_vertex_input) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); + } + + // EDS2 - Extended Dynamic State 2 Core (3 states) + if (key.state.extended_dynamic_state_2) { + static constexpr std::array extended2{ + VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT, + VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT, + VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended2.begin(), extended2.end()); + } + + // EDS2 - Logic Op (granular feature) + if (key.state.extended_dynamic_state_2_logic_op) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); + } + + // EDS3 - Blending (composite: ColorBlendEnable + Equation + WriteMask) + if (key.state.extended_dynamic_state_3_blend) { + static constexpr std::array extended3{ + VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT, VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT, + VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT, + + // VK_DYNAMIC_STATE_COLOR_BLEND_ADVANCED_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + } + + // EDS3 - Enables (granular: DepthClamp + LogicOpEnable + ...) + if (key.state.extended_dynamic_state_3_enables) { + static constexpr std::array extended3{ + VK_DYNAMIC_STATE_DEPTH_CLAMP_ENABLE_EXT, + VK_DYNAMIC_STATE_LOGIC_OP_ENABLE_EXT, + + // additional state3 extensions + VK_DYNAMIC_STATE_LINE_RASTERIZATION_MODE_EXT, + + VK_DYNAMIC_STATE_CONSERVATIVE_RASTERIZATION_MODE_EXT, + + VK_DYNAMIC_STATE_LINE_STIPPLE_ENABLE_EXT, + VK_DYNAMIC_STATE_ALPHA_TO_COVERAGE_ENABLE_EXT, + VK_DYNAMIC_STATE_ALPHA_TO_ONE_ENABLE_EXT, + VK_DYNAMIC_STATE_DEPTH_CLIP_ENABLE_EXT, + VK_DYNAMIC_STATE_PROVOKING_VERTEX_MODE_EXT, + }; + dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + } + const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index f71d5300da..75ef817dff 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -404,14 +404,63 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.GetMaxVertexInputBindings(), Maxwell::NumVertexArrays); } - dynamic_features = DynamicFeatures{ - .has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(), - .has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(), - .has_extended_dynamic_state_2_extra = device.IsExtExtendedDynamicState2ExtrasSupported(), - .has_extended_dynamic_state_3_blend = device.IsExtExtendedDynamicState3BlendingSupported(), - .has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(), - .has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported(), - }; + const u8 dynamic_state = Settings::values.dyna_state.GetValue(); + + LOG_INFO(Render_Vulkan, "DynamicState value is set to {}", (u32)dynamic_state); + + dynamic_features = {}; + // EDS1 - All-or-nothing (enabled if driver supports AND setting > 0) + dynamic_features.has_extended_dynamic_state = + device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0; + // EDS2 - Core features (enabled if driver supports AND setting > 1) + dynamic_features.has_extended_dynamic_state_2 = + device.IsExtExtendedDynamicState2Supported() && dynamic_state > 1; + dynamic_features.has_extended_dynamic_state_2_logic_op = + device.IsExtExtendedDynamicState2LogicOpSupported() && dynamic_state > 1; + dynamic_features.has_extended_dynamic_state_2_patch_control_points = + device.IsExtExtendedDynamicState2PatchControlPointsSupported() && dynamic_state > 1; + // EDS3 - Granular features (enabled if driver supports AND setting > 2) + dynamic_features.has_extended_dynamic_state_3_blend = + device.IsExtExtendedDynamicState3BlendingSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_enables = + device.IsExtExtendedDynamicState3EnablesSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_depth_clamp = + device.IsExtExtendedDynamicState3DepthClampEnableSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_logic_op_enable = + device.IsExtExtendedDynamicState3LogicOpEnableSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin = + device.IsExtExtendedDynamicState3TessellationDomainOriginSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_polygon_mode = + device.IsExtExtendedDynamicState3PolygonModeSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_rasterization_samples = + device.IsExtExtendedDynamicState3RasterizationSamplesSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_sample_mask = + device.IsExtExtendedDynamicState3SampleMaskSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable = + device.IsExtExtendedDynamicState3AlphaToCoverageEnableSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable = + device.IsExtExtendedDynamicState3AlphaToOneEnableSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_depth_clip_enable = + device.IsExtExtendedDynamicState3DepthClipEnableSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one = + device.IsExtExtendedDynamicState3DepthClipNegativeOneToOneSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode = + device.IsExtExtendedDynamicState3LineRasterizationModeSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_line_stipple_enable = + device.IsExtExtendedDynamicState3LineStippleEnableSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode = + device.IsExtExtendedDynamicState3ProvokingVertexModeSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode = + device.IsExtExtendedDynamicState3ConservativeRasterizationModeSupported() && + dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_sample_locations_enable = + device.IsExtExtendedDynamicState3SampleLocationsEnableSupported() && dynamic_state > 2; + dynamic_features.has_extended_dynamic_state_3_rasterization_stream = + device.IsExtExtendedDynamicState3RasterizationStreamSupported() && dynamic_state > 2; + // Vertex input dynamic state (independent toggle) + dynamic_features.has_dynamic_vertex_input = + device.IsExtVertexInputDynamicStateSupported() && + Settings::values.vertex_input_dynamic_state.GetValue(); } PipelineCache::~PipelineCache() { @@ -512,16 +561,51 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading GraphicsPipelineCacheKey key; file.read(reinterpret_cast(&key), sizeof(key)); + // Validate dynamic features compatibility - granular per-feature check if ((key.state.extended_dynamic_state != 0) != dynamic_features.has_extended_dynamic_state || (key.state.extended_dynamic_state_2 != 0) != dynamic_features.has_extended_dynamic_state_2 || - (key.state.extended_dynamic_state_2_extra != 0) != - dynamic_features.has_extended_dynamic_state_2_extra || + (key.state.extended_dynamic_state_2_logic_op != 0) != + dynamic_features.has_extended_dynamic_state_2_logic_op || + (key.state.extended_dynamic_state_2_patch_control_points != 0) != + dynamic_features.has_extended_dynamic_state_2_patch_control_points || (key.state.extended_dynamic_state_3_blend != 0) != dynamic_features.has_extended_dynamic_state_3_blend || (key.state.extended_dynamic_state_3_enables != 0) != dynamic_features.has_extended_dynamic_state_3_enables || + (key.state.extended_dynamic_state_3_depth_clamp != 0) != + dynamic_features.has_extended_dynamic_state_3_depth_clamp || + (key.state.extended_dynamic_state_3_logic_op_enable != 0) != + dynamic_features.has_extended_dynamic_state_3_logic_op_enable || + (key.state.extended_dynamic_state_3_tessellation_domain_origin != 0) != + dynamic_features.has_extended_dynamic_state_3_tessellation_domain_origin || + (key.state.extended_dynamic_state_3_polygon_mode != 0) != + dynamic_features.has_extended_dynamic_state_3_polygon_mode || + (key.state.extended_dynamic_state_3_rasterization_samples != 0) != + dynamic_features.has_extended_dynamic_state_3_rasterization_samples || + (key.state.extended_dynamic_state_3_sample_mask != 0) != + dynamic_features.has_extended_dynamic_state_3_sample_mask || + (key.state.extended_dynamic_state_3_alpha_to_coverage_enable != 0) != + dynamic_features.has_extended_dynamic_state_3_alpha_to_coverage_enable || + (key.state.extended_dynamic_state_3_alpha_to_one_enable != 0) != + dynamic_features.has_extended_dynamic_state_3_alpha_to_one_enable || + (key.state.extended_dynamic_state_3_depth_clip_enable != 0) != + dynamic_features.has_extended_dynamic_state_3_depth_clip_enable || + (key.state.extended_dynamic_state_3_depth_clip_negative_one_to_one != 0) != + dynamic_features.has_extended_dynamic_state_3_depth_clip_negative_one_to_one || + (key.state.extended_dynamic_state_3_line_rasterization_mode != 0) != + dynamic_features.has_extended_dynamic_state_3_line_rasterization_mode || + (key.state.extended_dynamic_state_3_line_stipple_enable != 0) != + dynamic_features.has_extended_dynamic_state_3_line_stipple_enable || + (key.state.extended_dynamic_state_3_provoking_vertex_mode != 0) != + dynamic_features.has_extended_dynamic_state_3_provoking_vertex_mode || + (key.state.extended_dynamic_state_3_conservative_rasterization_mode != 0) != + dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode || + (key.state.extended_dynamic_state_3_sample_locations_enable != 0) != + dynamic_features.has_extended_dynamic_state_3_sample_locations_enable || + (key.state.extended_dynamic_state_3_rasterization_stream != 0) != + dynamic_features.has_extended_dynamic_state_3_rasterization_stream || (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { return; } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 7909bd8cf0..2ce79c4df5 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -113,6 +116,10 @@ public: void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback); + [[nodiscard]] const DynamicFeatures& GetDynamicFeatures() const noexcept { + return dynamic_features; + } + private: [[nodiscard]] GraphicsPipeline* CurrentGraphicsPipelineSlowPath(); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 7bd8c57118..e49a6bbb72 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -933,6 +933,7 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info, void RasterizerVulkan::UpdateDynamicStates() { auto& regs = maxwell3d->regs; + const auto& dynamic_features = pipeline_cache.GetDynamicFeatures(); UpdateViewportsState(regs); UpdateScissorsState(regs); UpdateDepthBias(regs); @@ -940,7 +941,8 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateDepthBounds(regs); UpdateStencilFaces(regs); UpdateLineWidth(regs); - if (device.IsExtExtendedDynamicStateSupported()) { + // EDS1 - Extended Dynamic State 1 + if (dynamic_features.has_extended_dynamic_state) { UpdateCullMode(regs); UpdateDepthCompareOp(regs); UpdateFrontFace(regs); @@ -950,41 +952,74 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateDepthTestEnable(regs); UpdateDepthWriteEnable(regs); UpdateStencilTestEnable(regs); - if (device.IsExtExtendedDynamicState2Supported()) { - UpdatePrimitiveRestartEnable(regs); - UpdateRasterizerDiscardEnable(regs); - UpdateDepthBiasEnable(regs); - } - if (device.IsExtExtendedDynamicState3EnablesSupported()) { - using namespace Tegra::Engines; - if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) { - const auto has_float = std::any_of( - regs.vertex_attrib_format.begin(), - regs.vertex_attrib_format.end(), - [](const auto& attrib) { - return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float; - } - ); - if (regs.logic_op.enable) { - regs.logic_op.enable = static_cast(!has_float); + } + if (device.IsExtExtendedDynamicState3EnablesSupported()) { + UpdateLineStippleEnable(regs); + UpdateConservativeRasterizationMode(regs); + } + } + // EDS2 - Extended Dynamic State 2 Core + if (dynamic_features.has_extended_dynamic_state_2) { + if (state_tracker.TouchStateEnable()) { + UpdatePrimitiveRestartEnable(regs); + UpdateRasterizerDiscardEnable(regs); + UpdateDepthBiasEnable(regs); + } + } + // EDS2 - LogicOp (granular feature) + if (dynamic_features.has_extended_dynamic_state_2_logic_op) { + UpdateLogicOp(regs); + } + // EDS3 - Depth Clamp Enable (granular) + if (dynamic_features.has_extended_dynamic_state_3_depth_clamp || + dynamic_features.has_extended_dynamic_state_3_enables) { + if (state_tracker.TouchStateEnable()) { + UpdateDepthClampEnable(regs); + } + } + // EDS3 - Logic Op Enable (granular) + if (dynamic_features.has_extended_dynamic_state_3_logic_op_enable || + dynamic_features.has_extended_dynamic_state_3_enables) { + if (state_tracker.TouchStateEnable()) { + using namespace Tegra::Engines; + // AMD workaround for logic op with float vertex attributes + if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || + device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) { + struct In { + const Maxwell3D::Regs::VertexAttribute::Type d; + In(Maxwell3D::Regs::VertexAttribute::Type n) : d(n) {} + bool operator()(Maxwell3D::Regs::VertexAttribute n) const { + return n.type == d; } + }; + auto has_float = + std::any_of(regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), + In(Maxwell3D::Regs::VertexAttribute::Type::Float)); + if (regs.logic_op.enable) { + regs.logic_op.enable = static_cast(!has_float); } - UpdateLogicOpEnable(regs); - UpdateDepthClampEnable(regs); } + UpdateLogicOpEnable(regs); } - if (device.IsExtExtendedDynamicState2ExtrasSupported()) { - UpdateLogicOp(regs); - } - if (device.IsExtExtendedDynamicState3BlendingSupported()) { - UpdateBlending(regs); - } - if (device.IsExtExtendedDynamicState3EnablesSupported()) { + } + // EDS3 - Line Stipple Enable (granular) + if (dynamic_features.has_extended_dynamic_state_3_line_stipple_enable) { + if (state_tracker.TouchStateEnable()) { UpdateLineStippleEnable(regs); + } + } + // EDS3 - Conservative Rasterization Mode (granular) + if (dynamic_features.has_extended_dynamic_state_3_conservative_rasterization_mode) { + if (state_tracker.TouchStateEnable()) { UpdateConservativeRasterizationMode(regs); } } - if (device.IsExtVertexInputDynamicStateSupported()) { + // EDS3 - Blending (composite feature: ColorBlendEnable + ColorBlendEquation + ColorWriteMask) + if (dynamic_features.has_extended_dynamic_state_3_blend) { + UpdateBlending(regs); + } + // Vertex Input Dynamic State + if (dynamic_features.has_dynamic_vertex_input) { if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) { UpdateVertexInput(regs); } diff --git a/src/video_core/vulkan_common/vulkan.h b/src/video_core/vulkan_common/vulkan.h index 13f679ff54..0cf183bd27 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 93a87e1956..83a820d985 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -416,7 +416,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const bool is_suitable = GetSuitability(surface != nullptr); const VkDriverId driver_id = properties.driver.driverID; - const auto device_id = properties.properties.deviceID; + // uncomment this if you want per-device overrides :P + // const u32 device_id = properties.properties.deviceID; + const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; const bool is_amd_driver = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; @@ -427,7 +429,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK; const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; const bool is_turnip = driver_id == VK_DRIVER_ID_MESA_TURNIP; - const bool is_s8gen2 = device_id == 0x43050a01; const bool is_arm = driver_id == VK_DRIVER_ID_ARM_PROPRIETARY; if ((is_mvk || is_qualcomm || is_turnip || is_arm) && !is_suitable) { @@ -480,11 +481,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR is_warp_potentially_bigger = !extensions.subgroup_size_control || properties.subgroup_size_control.maxSubgroupSize > GuestWarpSize; - is_integrated = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; - is_virtual = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU; - is_non_gpu = properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_OTHER || - properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU; - supports_d24_depth = IsFormatSupported(VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT, FormatType::Optimal); @@ -494,7 +490,32 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR CollectPhysicalMemoryInfo(); CollectToolingInfo(); + // Driver-specific handling for VK_EXT_custom_border_color + // On some Qualcomm/Turnip/ARM drivers the extension may be partially implemented. + // Disable completely if no feature bits are reported to avoid crashes/undefined behavior. + if (is_qualcomm || is_turnip || is_arm) { + const bool has_any_custom_border_color = + features.custom_border_color.customBorderColors || + features.custom_border_color.customBorderColorWithoutFormat; + if (!has_any_custom_border_color) { + LOG_WARNING( + Render_Vulkan, + "Disabling VK_EXT_custom_border_color on '{}' — no usable features reported", + properties.driver.driverName); + RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color, + VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); + } else { + LOG_INFO(Render_Vulkan, + "VK_EXT_custom_border_color enabled on '{}' (partial support detected)", + properties.driver.driverName); + } + } + if (is_qualcomm) { + 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); @@ -533,37 +554,18 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR if (arch >= NvidiaArchitecture::Arch_AmpereOrNewer) { LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math"); features.shader_float16_int8.shaderFloat16 = false; + } else if (arch <= NvidiaArchitecture::Arch_Volta) { + if (nv_major_version < 527) { + LOG_WARNING(Render_Vulkan, "Volta and older have broken VK_KHR_push_descriptor"); + //RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); + } } - if (nv_major_version >= 510) { LOG_WARNING(Render_Vulkan, "NVIDIA Drivers >= 510 do not support MSAA image blits"); cant_blit_msaa = true; } } - - if (extensions.extended_dynamic_state3 && is_radv) { - LOG_WARNING(Render_Vulkan, "RADV has broken extendedDynamicState3ColorBlendEquation"); - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; - dynamic_state3_blending = false; - - const u32 version = (properties.properties.driverVersion << 3) >> 3; - if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) { - LOG_WARNING(Render_Vulkan, - "RADV versions older than 23.1.0 have broken depth clamp dynamic state"); - features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false; - dynamic_state3_enables = false; - } - } - - if (extensions.extended_dynamic_state3 && (is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) { - // AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation - LOG_WARNING(Render_Vulkan, - "AMD and Samsung drivers have broken extendedDynamicState3ColorBlendEquation"); - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; - features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; - dynamic_state3_blending = false; - } + // Dynamic state blacklists moved to GetSuitability() for proper ordering sets_per_pool = 64; if (is_amd_driver) { @@ -576,14 +578,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR has_broken_cube_compatibility = true; } } - if (is_qualcomm) { const u32 version = (properties.properties.driverVersion << 3) >> 3; if (version < VK_MAKE_API_VERSION(0, 255, 615, 512)) { has_broken_parallel_compiling = true; } } - if (extensions.sampler_filter_minmax && is_amd) { // Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken. if (!features.shader_float16_int8.shaderFloat16) { @@ -594,23 +594,77 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR } } + // VertexInputDynamicState blacklist moved to GetSuitability() for proper ordering if (features.shader_float16_int8.shaderFloat16 && is_intel_windows) { // Intel's compiler crashes when using fp16 on Astral Chain, disable it for the time being. LOG_WARNING(Render_Vulkan, "Intel has broken float16 math"); features.shader_float16_int8.shaderFloat16 = false; } - if (is_intel_windows) { 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"); + must_emulate_bgr565 = false; // Default: assume emulation isn't 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; + } + } else if (is_turnip) { + // Mesa Turnip added support for maintenance5 in Mesa 25.0 + if (extensions.maintenance5) { + LOG_INFO(Render_Vulkan, + "Turnip driver supports VK_KHR_maintenance5, disabling BGR emulation"); + must_emulate_bgr565 = false; + } else { + LOG_WARNING(Render_Vulkan, + "Turnip driver doesn't support native BGR, emulating formats"); + must_emulate_bgr565 = true; + } + } else if (is_arm) { + // ARM Mali: stop emulating BGR5 formats when VK_KHR_maintenance5 is available + if (extensions.maintenance5) { + LOG_INFO(Render_Vulkan, + "ARM driver supports VK_KHR_maintenance5, disabling BGR emulation"); + must_emulate_bgr565 = false; + } else { + LOG_WARNING(Render_Vulkan, "ARM 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) && + version < VK_MAKE_API_VERSION(0, 23, 2, 0)) { + // Disable VK_KHR_push_descriptor due to + // mesa/mesa/-/commit/ff91c5ca42bc80aa411cb3fd8f550aa6fdd16bdc + LOG_WARNING(Render_Vulkan, + "ANV drivers 22.3.0 to 23.1.0 have broken VK_KHR_push_descriptor"); + // RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); + } + } else if (extensions.push_descriptor && is_nvidia) { + const auto arch = GetNvidiaArch(); + if (arch <= NvidiaArchitecture::Arch_Pascal) { + LOG_WARNING(Render_Vulkan, + "Pascal and older architectures have broken VK_KHR_push_descriptor"); + // RemoveExtension(extensions.push_descriptor, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME); + } } if (is_mvk) { @@ -622,8 +676,10 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR (std::min)(properties.properties.limits.maxVertexInputBindings, 16U); } - if (is_turnip) { - LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits"); + if (is_turnip || is_qualcomm) { + // Ensure proper vertex input bindings limit for Qualcomm hardware + LOG_WARNING(Render_Vulkan, "{}: Ensuring maxVertexInputBindings = 32", + is_turnip ? "Turnip" : "Qualcomm"); properties.properties.limits.maxVertexInputBindings = 32; } @@ -634,45 +690,22 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); } - if (!extensions.extended_dynamic_state2 && extensions.extended_dynamic_state3) { - LOG_INFO(Render_Vulkan, - "Removing extendedDynamicState3 due to missing extendedDynamicState2"); - RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, - VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - dynamic_state3_blending = false; - dynamic_state3_enables = false; - } - - // Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it affects other iGPUs - // ALSO affects ALL versions of UHD drivers on Windows 10+, seems to cause even worse issues like straight up crashing - // So... Yeah, UHD drivers fucking suck -- maybe one day we can work past this, maybe; some driver hacking? - // And then we can rest in peace by doing `< VK_MAKE_API_VERSION(26, 0, 0)` for our beloved mesa drivers... one day - if ((is_mvk || (is_integrated && is_intel_anv) || (is_integrated && is_intel_windows)) && Settings::values.dyna_state.GetValue() != 0) { - LOG_WARNING(Render_Vulkan, "Driver has broken dynamic state, forcing to 0 to prevent graphical issues"); - Settings::values.dyna_state.SetValue(0); - } - - switch (Settings::values.dyna_state.GetValue()) { - case 0: - RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); - [[fallthrough]]; - case 1: - RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); - [[fallthrough]]; - case 2: - RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); - dynamic_state3_blending = false; - dynamic_state3_enables = false; - break; - } - - if (!extensions.extended_dynamic_state) { - Settings::values.vertex_input_dynamic_state.SetValue(false); - } + // Intel iGPU/MoltenVK blacklist moved to GetSuitability() for proper ordering - if (!Settings::values.vertex_input_dynamic_state.GetValue()) { - RemoveExtensionFeature(extensions.vertex_input_dynamic_state, features.vertex_input_dynamic_state, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); +#ifdef ANDROID + // Stock Qualcomm and ARM Mali drivers don't report VK_FORMAT_*_SSCALED/USCALED formats + // Turnip implements them in software, so only force emulation for stock drivers + if ((is_qualcomm && !is_turnip) || is_arm) { + must_emulate_scaled_formats = true; + LOG_INFO(Render_Vulkan, + "Mobile GPU detected: forcing scaled format emulation (hardware limitation)"); + } else { + must_emulate_scaled_formats = false; } +#else + // Desktop GPUs support scaled formats natively + must_emulate_scaled_formats = false; +#endif logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld); @@ -687,6 +720,8 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR if (extensions.memory_budget) { flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; } + const bool is_integrated = + properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; const VmaAllocatorCreateInfo allocator_info{ .flags = flags, .physicalDevice = physical, @@ -713,15 +748,32 @@ Device::~Device() { VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, FormatType format_type) const { if (IsFormatSupported(wanted_format, wanted_usage, format_type)) { - return wanted_format; + // Critical: Even if format is "supported", check for STORAGE + HDR + no MSAA support + // Driver may report STORAGE_IMAGE_BIT but shaderStorageImageMultisample=false means + // it will fail at runtime when used with MSAA (CopyImageMSAA silently fails) + const bool requests_storage = (wanted_usage & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) != 0; + const bool is_hdr_format = wanted_format == VK_FORMAT_B10G11R11_UFLOAT_PACK32; + + // If driver doesn't support shader storage image with MSAA, and we're requesting storage + // for an HDR format (which will likely be used with MSAA), force fallback + if (requests_storage && is_hdr_format && !features.features.shaderStorageImageMultisample) { + LOG_WARNING(Render_Vulkan, + "Format {} reports STORAGE_IMAGE_BIT but driver doesn't support " + "shaderStorageImageMultisample. Forcing fallback for MSAA compatibility.", + wanted_format); + // Continue to alternatives search below + } else { + return wanted_format; + } } // The wanted format is not supported by hardware, search for alternatives const VkFormat* alternatives = GetFormatAlternatives(wanted_format); if (alternatives == nullptr) { LOG_ERROR(Render_Vulkan, - "Format={} with usage={} and type={} has no defined alternatives and host " - "hardware does not support it", - wanted_format, wanted_usage, format_type); + "Format={} (0x{:X}) with usage={} and type={} has no defined alternatives and host " + "hardware does not support it. Driver: {} Device: {}", + wanted_format, static_cast(wanted_format), wanted_usage, format_type, + GetDriverName(), properties.properties.deviceName); return wanted_format; } @@ -1115,6 +1167,153 @@ bool Device::GetSuitability(bool requires_swapchain) { } } + // CRITICAL: Apply driver-specific feature workarounds BEFORE validation + // These blacklists disable broken features on specific drivers to prevent + // rendering issues and crashes. + // MUST execute before RemoveUnsuitableExtensions() calculates feature flags. + + const VkDriverId driver_id = properties.driver.driverID; + const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; + const bool is_amd_driver = + driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; + const bool is_intel_windows = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS; + const bool is_intel_anv = driver_id == VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA; + const bool is_qualcomm = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY; + const bool is_mvk = driver_id == VK_DRIVER_ID_MOLTENVK; + const bool is_integrated = + properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + + // VK_DYNAMIC_STATE + + // Mesa Intel drivers on UHD 620 have broken EDS causing extreme flickering - unknown if it + // affects other iGPUs ALSO affects ALL versions of UHD drivers on Windows 10+, seems to cause + // even worse issues like straight up crashing So... Yeah, UHD drivers fucking suck -- maybe one + // day we can work past this, maybe; some driver hacking? And then we can rest in peace by doing + // `< VK_MAKE_API_VERSION(26, 0, 0)` for our beloved mesa drivers... one day Disable dynamic + // state on affected drivers + if ((is_mvk || (is_integrated && is_intel_anv) || (is_integrated && is_intel_windows)) && + Settings::values.dyna_state.GetValue() != 0) { + LOG_WARNING( + Render_Vulkan, + "Intel iGPU/MoltenVK: Forcing dyna_state=0 due to broken dynamic state implementation"); + Settings::values.dyna_state.SetValue(0); + } + + // VK_EXT_extended_dynamic_state + + // RADV < 21.2.0: Broken ExtendedDynamicState implementation + // Disable entire extension on old drivers + if (extensions.extended_dynamic_state && is_radv) { + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(0, 21, 2, 0)) { + LOG_WARNING(Render_Vulkan, + "RADV < 21.2.0: Disabling broken VK_EXT_extended_dynamic_state"); + features.extended_dynamic_state.extendedDynamicState = false; + } + } + + // VK_EXT_extended_dynamic_state2 + + // RADV < 22.3.1: Broken ExtendedDynamicState2 implementation + // Disable entire extension on old drivers + if (extensions.extended_dynamic_state2 && is_radv) { + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(0, 22, 3, 1)) { + LOG_WARNING(Render_Vulkan, + "RADV < 22.3.1: Disabling broken VK_EXT_extended_dynamic_state2"); + features.extended_dynamic_state2.extendedDynamicState2 = false; + } + } + + // Qualcomm Adreno 7xx (drivers 676.0 - 679.x): Broken ExtendedDynamicState2 + // Disable ExtendedDynamicState2 on affected driver versions + if (extensions.extended_dynamic_state2 && is_qualcomm) { + 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)) { + LOG_WARNING( + Render_Vulkan, + "Qualcomm Adreno 7xx (676-679): Disabling broken VK_EXT_extended_dynamic_state2"); + features.extended_dynamic_state2.extendedDynamicState2 = false; + } + } + + // VK_EXT_extended_dynamic_state3 + + // AMD/Samsung: Broken extendedDynamicState3ColorBlendEquation + // Disable blend equation dynamic state, force static pipeline state + if (extensions.extended_dynamic_state3 && + (is_amd_driver || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) { + LOG_WARNING(Render_Vulkan, + "AMD/Samsung: Disabling broken extendedDynamicState3ColorBlendEquation"); + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; + } + + // RADV: Broken extendedDynamicState3ColorBlendEquation + // Disable problematic features based on driver version < 23.1.0 + if (extensions.extended_dynamic_state3 && is_radv) { + LOG_WARNING(Render_Vulkan, + "RADV: Disabling broken extendedDynamicState3ColorBlendEquation"); + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; + + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(0, 23, 1, 0)) { + LOG_WARNING(Render_Vulkan, "RADV < 23.1.0: Disabling broken depth clamp dynamic state"); + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false; + } + } + + // VK_EXT_vertex_input_dynamic_state + + // RADV + RDNA2: Broken VertexInputDynamicState on RDNA2 hardware + // Disable VertexInputDynamicState on RDNA2 + if (extensions.vertex_input_dynamic_state && is_radv) { + const bool is_rdna2 = + supported_extensions.contains(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); + if (is_rdna2) { + LOG_WARNING(Render_Vulkan, + "RADV + RDNA2: Disabling broken VK_EXT_vertex_input_dynamic_state"); + features.vertex_input_dynamic_state.vertexInputDynamicState = false; + } + } + + // Qualcomm: Broken VertexInputDynamicState implementation + // Disable VertexInputDynamicState on all Qualcomm drivers + if (extensions.vertex_input_dynamic_state && is_qualcomm) { + LOG_WARNING(Render_Vulkan, "Qualcomm: Disabling broken VK_EXT_vertex_input_dynamic_state"); + features.vertex_input_dynamic_state.vertexInputDynamicState = false; + } + + // Intel Windows < 27.20.100.0: Broken VertexInputDynamicState + // Disable VertexInputDynamicState on old Intel Windows drivers + if (extensions.vertex_input_dynamic_state && is_intel_windows) { + const u32 version = (properties.properties.driverVersion << 3) >> 3; + if (version < VK_MAKE_API_VERSION(27, 20, 100, 0)) { + LOG_WARNING( + Render_Vulkan, + "Intel Windows < 27.20.100.0: Disabling broken VK_EXT_vertex_input_dynamic_state"); + features.vertex_input_dynamic_state.vertexInputDynamicState = false; + } + } + + // If user setting is dyna_state=0, disable all dynamic state features + if (Settings::values.dyna_state.GetValue() == 0) { + LOG_INFO(Render_Vulkan, + "Dynamic state disabled by user setting, clearing all EDS features"); + features.custom_border_color.customBorderColors = false; + features.custom_border_color.customBorderColorWithoutFormat = false; + features.extended_dynamic_state.extendedDynamicState = false; + features.extended_dynamic_state2.extendedDynamicState2 = false; + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = false; + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = false; + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = false; + // Note: vertex_input_dynamic_state has independent toggle, NOT affected by dyna_state=0 + } + // Return whether we were suitable. return suitable; } @@ -1168,15 +1367,13 @@ void Device::RemoveUnsuitableExtensions() { // VK_EXT_provoking_vertex if (Settings::values.provoking_vertex.GetValue()) { - extensions.provoking_vertex = features.provoking_vertex.provokingVertexLast - && features.provoking_vertex - .transformFeedbackPreservesProvokingVertex; - RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, - features.provoking_vertex, + extensions.provoking_vertex = + features.provoking_vertex.provokingVertexLast && + features.provoking_vertex.transformFeedbackPreservesProvokingVertex; + RemoveExtensionFeatureIfUnsuitable(extensions.provoking_vertex, features.provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); } else { - RemoveExtensionFeature(extensions.provoking_vertex, - features.provoking_vertex, + RemoveExtensionFeature(extensions.provoking_vertex, features.provoking_vertex, VK_EXT_PROVOKING_VERTEX_EXTENSION_NAME); } @@ -1213,12 +1410,36 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); + // VK_EXT_robustness2 + extensions.robustness_2 = + features.robustness2.robustBufferAccess2 && features.robustness2.robustImageAccess2; + RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2, + VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + + // VK_EXT_image_robustness + extensions.image_robustness = features.image_robustness.robustImageAccess; + RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, + VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); + + // VK_EXT_swapchain_maintenance1 + extensions.swapchain_maintenance1 = + loaded_extensions.contains(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.swapchain_maintenance1, + VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); + // VK_EXT_vertex_input_dynamic_state - extensions.vertex_input_dynamic_state = - features.vertex_input_dynamic_state.vertexInputDynamicState; - RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state, - features.vertex_input_dynamic_state, - VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + if (Settings::values.vertex_input_dynamic_state.GetValue()) { + extensions.vertex_input_dynamic_state = + features.vertex_input_dynamic_state.vertexInputDynamicState; + RemoveExtensionFeatureIfUnsuitable(extensions.vertex_input_dynamic_state, + features.vertex_input_dynamic_state, + VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + } else { + RemoveExtensionFeature(extensions.vertex_input_dynamic_state, + features.vertex_input_dynamic_state, + VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME); + LOG_INFO(Render_Vulkan, "Vertex Input Dynamic State disabled by user setting"); + } // VK_KHR_pipeline_executable_properties if (Settings::values.renderer_shader_feedback.GetValue()) { @@ -1296,6 +1517,8 @@ void Device::CollectPhysicalMemoryInfo() { // Calculate limits using memory budget VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{}; budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; + const bool is_integrated = + properties.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; const auto mem_info = physical.GetMemoryProperties(extensions.memory_budget ? &budget : nullptr); const auto& mem_properties = mem_info.memoryProperties; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cb13f28523..d62028e2ca 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -58,6 +58,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, Robustness2, ROBUSTNESS_2, robustness2) \ FEATURE(EXT, TransformFeedback, TRANSFORM_FEEDBACK, transform_feedback) \ FEATURE(EXT, VertexInputDynamicState, VERTEX_INPUT_DYNAMIC_STATE, vertex_input_dynamic_state) \ + FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \ FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ pipeline_executable_properties) \ FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \ @@ -75,6 +76,7 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, SHADER_VIEWPORT_INDEX_LAYER, shader_viewport_index_layer) \ EXTENSION(EXT, TOOLING_INFO, tooling_info) \ EXTENSION(EXT, VERTEX_ATTRIBUTE_DIVISOR, vertex_attribute_divisor) \ + EXTENSION(EXT, SWAPCHAIN_MAINTENANCE_1, swapchain_maintenance1) \ EXTENSION(KHR, DRAW_INDIRECT_COUNT, draw_indirect_count) \ EXTENSION(KHR, DRIVER_PROPERTIES, driver_properties) \ EXTENSION(KHR, PUSH_DESCRIPTOR, push_descriptor) \ @@ -84,6 +86,15 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(KHR, SWAPCHAIN, swapchain) \ EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \ EXTENSION(KHR, IMAGE_FORMAT_LIST, image_format_list) \ + 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) \ EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \ EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ EXTENSION(NV, VIEWPORT_ARRAY2, viewport_array2) \ @@ -365,6 +376,12 @@ public: return properties.subgroup_properties.supportedOperations & feature; } + /// Returns true if subgroup operations are supported in the specified shader stage. + /// Mobile GPUs (Qualcomm Adreno) often only support subgroups in fragment/compute stages. + bool IsSubgroupSupportedForStage(VkShaderStageFlagBits stage) const { + return properties.subgroup_properties.supportedStages & stage; + } + /// Returns the maximum number of push descriptors. u32 MaxPushDescriptors() const { return properties.push_descriptor.maxPushDescriptors; @@ -520,6 +537,39 @@ public: return extensions.custom_border_color; } + /// Base Vulkan Dynamic State support checks. + /// These provide granular control over each base dynamic state, allowing individual states + /// to be disabled if broken driver implementations are detected at device initialization. + /// By default all states are enabled. If a specific driver has issues with certain states, + /// they can be disabled in vulkan_device.cpp constructor (see has_broken_compute pattern). + bool SupportsDynamicViewport() const { + return supports_dynamic_viewport; + } + bool SupportsDynamicScissor() const { + return supports_dynamic_scissor; + } + bool SupportsDynamicLineWidth() const { + return supports_dynamic_line_width; + } + bool SupportsDynamicDepthBias() const { + return supports_dynamic_depth_bias; + } + bool SupportsDynamicBlendConstants() const { + return supports_dynamic_blend_constants; + } + bool SupportsDynamicDepthBounds() const { + return supports_dynamic_depth_bounds; + } + bool SupportsDynamicStencilCompareMask() const { + return supports_dynamic_stencil_compare; + } + bool SupportsDynamicStencilWriteMask() const { + return supports_dynamic_stencil_write; + } + bool SupportsDynamicStencilReference() const { + return supports_dynamic_stencil_reference; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state; @@ -554,6 +604,98 @@ public: return dynamic_state3_enables; } + // EDS2 granular feature checks + bool IsExtExtendedDynamicState2LogicOpSupported() const { + return extensions.extended_dynamic_state2 && + features.extended_dynamic_state2.extendedDynamicState2LogicOp; + } + + bool IsExtExtendedDynamicState2PatchControlPointsSupported() const { + return extensions.extended_dynamic_state2 && + features.extended_dynamic_state2.extendedDynamicState2PatchControlPoints; + } + + // EDS3 granular feature checks + bool IsExtExtendedDynamicState3DepthClampEnableSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable; + } + + bool IsExtExtendedDynamicState3LogicOpEnableSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable; + } + + bool IsExtExtendedDynamicState3TessellationDomainOriginSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3TessellationDomainOrigin; + } + + bool IsExtExtendedDynamicState3PolygonModeSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3PolygonMode; + } + + bool IsExtExtendedDynamicState3RasterizationSamplesSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3RasterizationSamples; + } + + bool IsExtExtendedDynamicState3SampleMaskSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3SampleMask; + } + + bool IsExtExtendedDynamicState3AlphaToCoverageEnableSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable; + } + + bool IsExtExtendedDynamicState3AlphaToOneEnableSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable; + } + + bool IsExtExtendedDynamicState3DepthClipEnableSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3DepthClipEnable; + } + + bool IsExtExtendedDynamicState3DepthClipNegativeOneToOneSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3DepthClipNegativeOneToOne; + } + + bool IsExtExtendedDynamicState3LineRasterizationModeSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode; + } + + bool IsExtExtendedDynamicState3LineStippleEnableSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable; + } + + bool IsExtExtendedDynamicState3ProvokingVertexModeSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ProvokingVertexMode; + } + + bool IsExtExtendedDynamicState3ConservativeRasterizationModeSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode; + } + + bool IsExtExtendedDynamicState3SampleLocationsEnableSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3SampleLocationsEnable; + } + + bool IsExtExtendedDynamicState3RasterizationStreamSupported() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3RasterizationStream; + } + /// Returns true if the device supports VK_EXT_filter_cubic bool IsExtFilterCubicSupported() const { return extensions.filter_cubic; @@ -833,9 +975,6 @@ private: bool is_blit_depth24_stencil8_supported{}; ///< Support for blitting from and to D24S8. bool is_blit_depth32_stencil8_supported{}; ///< Support for blitting from and to D32S8. bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. - bool is_integrated{}; ///< Is GPU an iGPU. - bool is_virtual{}; ///< Is GPU a virtual GPU. - bool is_non_gpu{}; ///< Is SoftwareRasterizer, FPGA, non-GPU device. bool has_broken_compute{}; ///< Compute shaders can cause crashes bool has_broken_cube_compatibility{}; ///< Has broken cube compatibility bit bool has_broken_parallel_compiling{}; ///< Has broken parallel shader compiling. @@ -851,6 +990,22 @@ private: bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow. u64 device_access_memory{}; ///< Total size of device local memory in bytes. u32 sets_per_pool{}; ///< Sets per Description Pool + + /// Base Vulkan Dynamic State support flags (granular fallback for broken drivers). + /// All default to true. These can be individually disabled in vulkan_device.cpp + /// if specific broken driver implementations are detected during initialization. + /// This provides emergency protection against drivers that report support but crash/misbehave. + /// Pattern: Check driver/device and set to false in vulkan_device.cpp constructor. + bool supports_dynamic_viewport{true}; ///< VK_DYNAMIC_STATE_VIEWPORT + bool supports_dynamic_scissor{true}; ///< VK_DYNAMIC_STATE_SCISSOR + bool supports_dynamic_line_width{true}; ///< VK_DYNAMIC_STATE_LINE_WIDTH + bool supports_dynamic_depth_bias{true}; ///< VK_DYNAMIC_STATE_DEPTH_BIAS + bool supports_dynamic_blend_constants{true}; ///< VK_DYNAMIC_STATE_BLEND_CONSTANTS + bool supports_dynamic_depth_bounds{true}; ///< VK_DYNAMIC_STATE_DEPTH_BOUNDS + bool supports_dynamic_stencil_compare{true}; ///< VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK + bool supports_dynamic_stencil_write{true}; ///< VK_DYNAMIC_STATE_STENCIL_WRITE_MASK + bool supports_dynamic_stencil_reference{true}; ///< VK_DYNAMIC_STATE_STENCIL_REFERENCE + NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer}; // Telemetry parameters