diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index e643e98ead..2cf9ee9dd6 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -60,7 +60,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe 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_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 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); @@ -158,7 +158,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..49fcf281e9 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -20,7 +20,8 @@ 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_2_logic_op; + bool has_extended_dynamic_state_2_patch_control_points; bool has_extended_dynamic_state_3_blend; bool has_extended_dynamic_state_3_enables; bool has_dynamic_vertex_input; @@ -186,7 +187,7 @@ struct FixedPipelineState { u32 raw1; 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<2, 1, u32> extended_dynamic_state_2_logic_op; 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; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index ca58e3fb4c..cb9451ded9 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -845,7 +845,7 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { VK_DYNAMIC_STATE_LINE_WIDTH, }; if (key.state.extended_dynamic_state) { - std::vector extended{ + static constexpr std::array extended{ VK_DYNAMIC_STATE_CULL_MODE_EXT, VK_DYNAMIC_STATE_FRONT_FACE_EXT, VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, @@ -855,52 +855,58 @@ 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()); - } + + // VERTEX_INPUT_BINDING_STRIDE is part of EDS1, not VIDS + if (!key.state.dynamic_vertex_input) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); + } + } + + // Vertex Input Dynamic State (replaces VERTEX_INPUT_BINDING_STRIDE) + if (key.state.dynamic_vertex_input) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_EXT); + } + + // EDS2 - 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 - LogicOp (granular) + if (key.state.extended_dynamic_state_2_logic_op) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LOGIC_OP_EXT); + } + + // EDS3 - Blending (composite: 3 states) + 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, + }; + dynamic_states.insert(dynamic_states.end(), extended3.begin(), extended3.end()); + } + + // EDS3 - Enables (composite: 9 states) + 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, + 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{ diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index f71d5300da..a8ffe3c151 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -404,14 +404,33 @@ 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 {}", static_cast(dynamic_state)); + + dynamic_features = {}; + + // EDS1 - Level 1 (all-or-nothing, enabled if driver supports AND setting > 0) + dynamic_features.has_extended_dynamic_state = + device.IsExtExtendedDynamicStateSupported() && dynamic_state > 0; + + // EDS2 - Level 2 (core + granular 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.IsExtExtendedDynamicState2ExtrasSupported() && dynamic_state > 1; + dynamic_features.has_extended_dynamic_state_2_patch_control_points = false; + + // EDS3 - Level 3 (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; + + // VIDS - Independent toggle (not affected by dyna_state levels) + dynamic_features.has_dynamic_vertex_input = + device.IsExtVertexInputDynamicStateSupported() && + Settings::values.vertex_input_dynamic_state.GetValue(); } PipelineCache::~PipelineCache() { @@ -516,8 +535,8 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading 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_3_blend != 0) != dynamic_features.has_extended_dynamic_state_3_blend || (key.state.extended_dynamic_state_3_enables != 0) != diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 93a87e1956..32037d0a2a 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1115,6 +1115,95 @@ bool Device::GetSuitability(bool requires_swapchain) { } } + // VK_DYNAMIC_STATE + + // 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/RADV: Broken extendedDynamicState3ColorBlendEquation + // Disable blend equation dynamic state, force static pipeline state + if (extensions.extended_dynamic_state3 && + (is_amd_driver || is_radv || driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY)) { + LOG_WARNING(Render_Vulkan, + "AMD/Samsung/RADV: Disabling broken extendedDynamicState3ColorBlendEquation"); + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = false; + } + + // VK_EXT_vertex_input_dynamic_state + // No RADV workarounds - assume modern drivers + + // 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; }