From 58c28bc9291bd0e9440cfaf9b24b06c9d576753f Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Wed, 15 Apr 2026 02:24:19 -0400 Subject: [PATCH] [vulkan] Rework DynamicState - ExtendedDynamicState --- .../renderer_vulkan/fixed_pipeline_state.cpp | 55 +++++- .../renderer_vulkan/fixed_pipeline_state.h | 59 ++++-- .../renderer_vulkan/vk_graphics_pipeline.cpp | 106 +++++++---- .../renderer_vulkan/vk_pipeline_cache.cpp | 71 +++++-- .../vulkan_common/vulkan_device.cpp | 173 +++++++++++++++++- src/video_core/vulkan_common/vulkan_device.h | 114 +++++++++++- 6 files changed, 486 insertions(+), 92 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index d53cf70b37..67d3affacb 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -57,6 +57,11 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe const auto topology_ = maxwell3d.draw_manager->GetDrawState().topology; raw1 = 0; + core_dynamic_viewport_scissor.Assign(features.has_core_dynamic_viewport_scissor ? 1 : 0); + core_dynamic_depth_bias.Assign(features.has_core_dynamic_depth_bias ? 1 : 0); + core_dynamic_depth_bounds.Assign(features.has_core_dynamic_depth_bounds ? 1 : 0); + core_dynamic_line_width.Assign(features.has_core_dynamic_line_width ? 1 : 0); + core_dynamic_stencil_masks.Assign(features.has_core_dynamic_stencil_masks ? 1 : 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_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0); @@ -76,6 +81,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe raw2 = 0; + const auto test_func = regs.alpha_test_enabled != 0 ? regs.alpha_test_func : Maxwell::ComparisonOp::Always_GL; alpha_test_func.Assign(PackComparisonOp(test_func)); @@ -106,10 +112,18 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } provoking_vertex_last.Assign(use_last_provoking_vertex ? 1 : 0); - conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); - smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); - alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); - alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); + if (!features.has_dynamic_state3_conservative_rasterization_mode) { + conservative_raster_enable.Assign(regs.conservative_raster_enable != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_line_rasterization_mode) { + smooth_lines.Assign(regs.line_anti_alias_enable != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_alpha_to_coverage_enable) { + alpha_to_coverage_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_coverage != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_alpha_to_one_enable) { + alpha_to_one_enabled.Assign(regs.anti_alias_alpha_control.alpha_to_one != 0 ? 1 : 0); + } app_stage.Assign(maxwell3d.engine_state); depth_bounds_min = static_cast(regs.depth_bounds[0]); @@ -173,24 +187,49 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } dynamic_state.raw1 = 0; dynamic_state.raw2 = 0; - if (!extended_dynamic_state) { + + const bool any_eds1_baked = !features.has_extended_dynamic_state || + !features.has_eds1_cull_mode || !features.has_eds1_front_face || + !features.has_eds1_depth_test_enable || !features.has_eds1_depth_write_enable || + !features.has_eds1_depth_compare_op || + !(features.has_eds1_depth_bounds_test_enable || features.has_core_dynamic_depth_bounds) || + !features.has_eds1_stencil_test_enable || !features.has_eds1_stencil_op; + + if (any_eds1_baked) { dynamic_state.Refresh(regs); + } + + if (!features.has_extended_dynamic_state || !features.has_eds1_vertex_input_binding_stride) { std::ranges::transform(regs.vertex_streams, vertex_strides.begin(), [](const auto& array) { return static_cast(array.stride.Value()); }); } - if (!extended_dynamic_state_2_logic_op) { + + const bool any_eds2_baked = !features.has_extended_dynamic_state_2 || + !(features.has_eds2_depth_bias_enable || features.has_core_dynamic_depth_bias) || + !features.has_eds2_primitive_restart_enable || + !features.has_eds2_rasterizer_discard_enable || !features.has_extended_dynamic_state_2_logic_op; + + if (any_eds2_baked) { dynamic_state.Refresh2(regs, topology_, extended_dynamic_state_2); } - if (!extended_dynamic_state_3_blend) { + + const bool any_eds3_baked = !features.has_extended_dynamic_state_3_blend || + !features.has_dynamic_state3_depth_clamp_enable || !features.has_dynamic_state3_logic_op_enable || + !features.has_dynamic_state3_line_stipple_enable || !features.has_dynamic_state3_alpha_to_coverage_enable || + !features.has_dynamic_state3_alpha_to_one_enable || !features.has_dynamic_state3_line_rasterization_mode || + !features.has_dynamic_state3_conservative_rasterization_mode; + + if (any_eds3_baked) { if (maxwell3d.dirty.flags[Dirty::Blending]) { maxwell3d.dirty.flags[Dirty::Blending] = false; for (size_t index = 0; index < attachments.size(); ++index) { attachments[index].Refresh(regs, index); } } + dynamic_state.Refresh3(regs, features); } - dynamic_state.Refresh3(regs, features); + if (xfb_enabled) { RefreshXfbState(xfb_state, regs); } diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index 44157d686d..28fe2ccb5c 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,8 +21,25 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct DynamicFeatures { + bool has_core_dynamic_viewport_scissor; + bool has_core_dynamic_depth_bias; + bool has_core_dynamic_depth_bounds; + bool has_core_dynamic_line_width; + bool has_core_dynamic_stencil_masks; bool has_extended_dynamic_state; + bool has_eds1_cull_mode; + bool has_eds1_front_face; + bool has_eds1_depth_test_enable; + bool has_eds1_depth_write_enable; + bool has_eds1_depth_compare_op; + bool has_eds1_depth_bounds_test_enable; + bool has_eds1_stencil_test_enable; + bool has_eds1_stencil_op; + bool has_eds1_vertex_input_binding_stride; bool has_extended_dynamic_state_2; + bool has_eds2_depth_bias_enable; + bool has_eds2_primitive_restart_enable; + bool has_eds2_rasterizer_discard_enable; bool has_extended_dynamic_state_2_logic_op; bool has_extended_dynamic_state_2_patch_control_points; bool has_extended_dynamic_state_3_blend; @@ -30,6 +47,10 @@ struct DynamicFeatures { bool has_dynamic_state3_depth_clamp_enable; bool has_dynamic_state3_logic_op_enable; bool has_dynamic_state3_line_stipple_enable; + bool has_dynamic_state3_alpha_to_coverage_enable; + bool has_dynamic_state3_alpha_to_one_enable; + bool has_dynamic_state3_line_rasterization_mode; + bool has_dynamic_state3_conservative_rasterization_mode; bool has_dynamic_vertex_input; bool has_provoking_vertex; bool has_provoking_vertex_first_mode; @@ -194,23 +215,27 @@ struct FixedPipelineState { }; union { - 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_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; - 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; - - BitField<24, 4, Maxwell::PrimitiveTopology> topology; - BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; + u64 raw1; + BitField<0, 1, u64> core_dynamic_viewport_scissor; + BitField<1, 1, u64> core_dynamic_depth_bias; + BitField<2, 1, u64> core_dynamic_depth_bounds; + BitField<3, 1, u64> core_dynamic_line_width; + BitField<4, 1, u64> core_dynamic_stencil_masks; + BitField<5, 1, u64> extended_dynamic_state; + BitField<6, 1, u64> extended_dynamic_state_2; + BitField<7, 1, u64> extended_dynamic_state_2_logic_op; + BitField<8, 1, u64> extended_dynamic_state_3_blend; + BitField<9, 1, u64> extended_dynamic_state_3_enables; + BitField<10, 1, u64> dynamic_vertex_input; + BitField<11, 1, u64> xfb_enabled; + BitField<12, 1, u64> ndc_minus_one_to_one; + BitField<13, 2, u64> polygon_mode; + BitField<15, 2, u64> tessellation_primitive; + BitField<17, 2, u64> tessellation_spacing; + BitField<19, 1, u64> tessellation_clockwise; + BitField<20, 5, u64> patch_control_points_minus_one; + BitField<25, 4, Maxwell::PrimitiveTopology> topology; + BitField<29, 4, Tegra::Texture::MsaaMode> msaa_mode; }; union { u32 raw2; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 43fbefe425..8f231a4352 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -848,62 +848,94 @@ 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, - }; + static_vector dynamic_states{}; + // Core dynamic states (Vulkan 1.0) + if (device.SupportsCoreDynamicViewportScissor()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); + dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); + } + if (device.SupportsCoreDynamicDepthBias()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); + } + // Blend constants are commonly available; keep as default dynamic state. + dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); + if (device.SupportsCoreDynamicDepthBounds()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); + } + if (device.SupportsCoreDynamicStencilMasks()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); + } + if (device.SupportsCoreDynamicLineWidth()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); + } if (key.state.extended_dynamic_state) { - static constexpr std::array extended{ - VK_DYNAMIC_STATE_CULL_MODE_EXT, - VK_DYNAMIC_STATE_FRONT_FACE_EXT, - VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, - VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, - VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT, - VK_DYNAMIC_STATE_STENCIL_OP_EXT, - }; - dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); + if (device.SupportsEds1CullMode()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_CULL_MODE_EXT); + } + if (device.SupportsEds1FrontFace()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_FRONT_FACE_EXT); + } + if (device.SupportsEds1DepthTestEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT); + } + if (device.SupportsEds1DepthWriteEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT); + } + if (device.SupportsEds1DepthCompareOp()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT); + } + if (device.SupportsEds1DepthBoundsTestEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT); + } + if (device.SupportsEds1StencilTestEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT); + } + if (device.SupportsEds1StencilOp()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_OP_EXT); + } - // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT is part of EDS1 - // Only use it if VIDS is not active (VIDS replaces it with full vertex input control) - if (!key.state.dynamic_vertex_input) { + // VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT + if (!key.state.dynamic_vertex_input && device.SupportsEds1VertexInputBindingStride()) { dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); } } - // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) - Independent from EDS - // Provides full dynamic vertex input control, replaces VERTEX_INPUT_BINDING_STRIDE + // VK_DYNAMIC_STATE_VERTEX_INPUT_EXT (VIDS) 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) + // EDS2 - Core (3 states) — include only supported subfeatures 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 (device.SupportsEds2DepthBiasEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE_EXT); + } + if (device.SupportsEds2PrimitiveRestartEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE_EXT); + } + if (device.SupportsEds2RasterizerDiscardEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_RASTERIZER_DISCARD_ENABLE_EXT); + } } // EDS2 - LogicOp (granular) - if (key.state.extended_dynamic_state_2_logic_op) { + if (key.state.extended_dynamic_state_2_logic_op && device.SupportsDynamicState2LogicOp()) { 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()); + if (device.SupportsDynamicState3ColorBlendEnable()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_ENABLE_EXT); + } + if (device.SupportsDynamicState3ColorBlendEquation()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_BLEND_EQUATION_EXT); + } + if (device.SupportsDynamicState3ColorWriteMask()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_COLOR_WRITE_MASK_EXT); + } } // EDS3 - Enables (composite: per-feature) diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 7b5ac7fac0..bcfc4b8b4d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -58,7 +58,7 @@ using VideoCommon::FileEnvironment; using VideoCommon::GenericEnvironment; using VideoCommon::GraphicsEnvironment; -constexpr u32 CACHE_VERSION = 16; +constexpr u32 CACHE_VERSION = 17; constexpr std::array VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; template @@ -465,18 +465,46 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, dynamic_features = {}; - // User granularity enforced in vulkan_device.cpp switch statement: - // Level 0: Core Dynamic States only - // Level 1: Core + EDS1 - // Level 2: Core + EDS1 + EDS2 (accumulative) - // Level 3: Core + EDS1 + EDS2 + EDS3 (accumulative) - // Here we only verify if extensions were successfully loaded by the device + dynamic_features.has_core_dynamic_viewport_scissor = + device.SupportsCoreDynamicViewportScissor(); + dynamic_features.has_core_dynamic_depth_bias = + device.SupportsCoreDynamicDepthBias(); + dynamic_features.has_core_dynamic_depth_bounds = + device.SupportsCoreDynamicDepthBounds(); + dynamic_features.has_core_dynamic_line_width = + device.SupportsCoreDynamicLineWidth(); + dynamic_features.has_core_dynamic_stencil_masks = + device.SupportsCoreDynamicStencilMasks(); dynamic_features.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(); + dynamic_features.has_eds1_cull_mode = + device.SupportsEds1CullMode(); + dynamic_features.has_eds1_front_face = + device.SupportsEds1FrontFace(); + dynamic_features.has_eds1_depth_test_enable = + device.SupportsEds1DepthTestEnable(); + dynamic_features.has_eds1_depth_write_enable = + device.SupportsEds1DepthWriteEnable(); + dynamic_features.has_eds1_depth_compare_op = + device.SupportsEds1DepthCompareOp(); + dynamic_features.has_eds1_depth_bounds_test_enable = + device.SupportsEds1DepthBoundsTestEnable(); + dynamic_features.has_eds1_stencil_test_enable = + device.SupportsEds1StencilTestEnable(); + dynamic_features.has_eds1_stencil_op = + device.SupportsEds1StencilOp(); + dynamic_features.has_eds1_vertex_input_binding_stride = + device.SupportsEds1VertexInputBindingStride(); dynamic_features.has_extended_dynamic_state_2 = device.IsExtExtendedDynamicState2Supported(); + dynamic_features.has_eds2_depth_bias_enable = + device.SupportsEds2DepthBiasEnable(); + dynamic_features.has_eds2_primitive_restart_enable = + device.SupportsEds2PrimitiveRestartEnable(); + dynamic_features.has_eds2_rasterizer_discard_enable = + device.SupportsEds2RasterizerDiscardEnable(); dynamic_features.has_extended_dynamic_state_2_logic_op = device.IsExtExtendedDynamicState2ExtrasSupported(); dynamic_features.has_extended_dynamic_state_2_patch_control_points = false; @@ -491,13 +519,21 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.SupportsDynamicState3LogicOpEnable(); dynamic_features.has_dynamic_state3_line_stipple_enable = device.SupportsDynamicState3LineStippleEnable(); + dynamic_features.has_dynamic_state3_alpha_to_coverage_enable = + device.SupportsDynamicState3AlphaToCoverageEnable(); + dynamic_features.has_dynamic_state3_alpha_to_one_enable = + device.SupportsDynamicState3AlphaToOneEnable(); + dynamic_features.has_dynamic_state3_line_rasterization_mode = + device.SupportsDynamicState3LineRasterizationMode(); + dynamic_features.has_dynamic_state3_conservative_rasterization_mode = + device.SupportsDynamicState3ConservativeRasterizationMode(); - // VIDS: Independent toggle (not affected by dyna_state levels) dynamic_features.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && Settings::values.vertex_input_dynamic_state.GetValue(); - dynamic_features.has_provoking_vertex = device.IsExtProvokingVertexSupported(); + dynamic_features.has_provoking_vertex = + device.IsExtProvokingVertexSupported(); dynamic_features.has_provoking_vertex_first_mode = device.SupportsProvokingVertexFirstMode(); dynamic_features.has_provoking_vertex_last_mode = @@ -604,8 +640,20 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading GraphicsPipelineCacheKey key; file.read(reinterpret_cast(&key), sizeof(key)); - if ((key.state.extended_dynamic_state != 0) != + if ((key.state.core_dynamic_viewport_scissor != 0) != + dynamic_features.has_core_dynamic_viewport_scissor || + (key.state.core_dynamic_depth_bias != 0) != + dynamic_features.has_core_dynamic_depth_bias || + (key.state.core_dynamic_depth_bounds != 0) != + dynamic_features.has_core_dynamic_depth_bounds || + (key.state.core_dynamic_line_width != 0) != + dynamic_features.has_core_dynamic_line_width || + (key.state.core_dynamic_stencil_masks != 0) != + dynamic_features.has_core_dynamic_stencil_masks || + (key.state.extended_dynamic_state != 0) != dynamic_features.has_extended_dynamic_state || + (key.state.dynamic_vertex_input != 0) != + dynamic_features.has_dynamic_vertex_input || (key.state.extended_dynamic_state_2 != 0) != dynamic_features.has_extended_dynamic_state_2 || (key.state.extended_dynamic_state_2_logic_op != 0) != @@ -613,8 +661,7 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading (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.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { + dynamic_features.has_extended_dynamic_state_3_enables) { return; } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index f48fe39e4e..ba088441d2 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -648,16 +648,9 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const auto dyna_state = Settings::values.dyna_state.GetValue(); - // Base dynamic states (VIEWPORT, SCISSOR, DEPTH_BIAS, etc.) are ALWAYS active in vk_graphics_pipeline.cpp - // This slider controls EXTENDED dynamic states with accumulative levels per Vulkan specs: - // Level 0 = Core Dynamic States only (Vulkan 1.0) - // Level 1 = Core + VK_EXT_extended_dynamic_state - // Level 2 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 - // Level 3 = Core + VK_EXT_extended_dynamic_state + VK_EXT_extended_dynamic_state2 + VK_EXT_extended_dynamic_state3 - switch (dyna_state) { case Settings::ExtendedDynamicState::Disabled: - // Level 0: Disable all extended dynamic state extensions + // Level 0: Enable DynamicState (Static) + Disable all extended dynamic state extensions RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, @@ -666,9 +659,60 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; dynamic_state3_enables = false; + + core_dynamic_viewport_scissor = (dld.vkCmdSetViewport != nullptr) && (dld.vkCmdSetScissor != nullptr); + core_dynamic_depth_bias = (dld.vkCmdSetDepthBias != nullptr) || (dld.vkCmdSetDepthBias2EXT != nullptr) || + (features.depth_bias_control.depthBiasControl != VK_FALSE); + + if (features.core.features.depthBounds && dld.vkCmdSetDepthBounds == nullptr) { + features.core.features.depthBounds = VK_FALSE; + core_dynamic_depth_bounds = false; + } else { + core_dynamic_depth_bounds = (dld.vkCmdSetDepthBounds != nullptr) && features.core.features.depthBounds; + } + + if (features.core.features.wideLines && dld.vkCmdSetLineWidth == nullptr) { + features.core.features.wideLines = VK_FALSE; + core_dynamic_line_width = false; + } else { + core_dynamic_line_width = (dld.vkCmdSetLineWidth != nullptr) && features.core.features.wideLines; + } + + core_dynamic_stencil_masks = (dld.vkCmdSetStencilCompareMask != nullptr) && + (dld.vkCmdSetStencilWriteMask != nullptr) && + (dld.vkCmdSetStencilReference != nullptr); break; case Settings::ExtendedDynamicState::EDS1: // Level 1: Enable EDS1, disable EDS2 and EDS3 + if (dyna_state_enabled && extensions.extended_dynamic_state) { + eds1_cull_mode = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetCullModeEXT != nullptr); + eds1_front_face = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetFrontFaceEXT != nullptr); + eds1_depth_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthTestEnableEXT != nullptr); + eds1_depth_write_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthWriteEnableEXT != nullptr); + eds1_depth_compare_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthCompareOpEXT != nullptr); + eds1_depth_bounds_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetDepthBoundsTestEnableEXT != nullptr); + eds1_stencil_test_enable = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilTestEnableEXT != nullptr); + eds1_stencil_op = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdSetStencilOpEXT != nullptr); + + vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && (dld.vkCmdBindVertexBuffers2EXT != nullptr); + + const bool eds1_any = eds1_cull_mode || eds1_front_face || eds1_depth_test_enable || + eds1_depth_write_enable || eds1_depth_compare_op || eds1_depth_bounds_test_enable || + eds1_stencil_test_enable || eds1_stencil_op || vertex_input_binding_stride; + if (!eds1_any) { + extensions.extended_dynamic_state = false; + RemoveExtensionFeature(extensions.extended_dynamic_state, features.extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME); + // Clear flags + eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; + eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; + vertex_input_binding_stride = false; + } + } else { + extensions.extended_dynamic_state = false; + eds1_cull_mode = eds1_front_face = eds1_depth_test_enable = eds1_depth_write_enable = false; + eds1_depth_compare_op = eds1_depth_bounds_test_enable = eds1_stencil_test_enable = eds1_stencil_op = false; + vertex_input_binding_stride = false; + } RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, @@ -678,6 +722,32 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR break; case Settings::ExtendedDynamicState::EDS2: // Level 2: Enable EDS1 + EDS2, disable EDS3 + { + bool eds1_any = (features.extended_dynamic_state.extendedDynamicState && + ((dld.vkCmdBindVertexBuffers2EXT != nullptr) || (dld.vkCmdSetCullModeEXT != nullptr))); + if (!eds1_any) { + extensions.extended_dynamic_state2 = false; + } + } + if (dyna_state_enabled && extensions.extended_dynamic_state2) { + eds2_depth_bias_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetDepthBiasEnableEXT != nullptr); + eds2_primitive_restart_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr); + eds2_rasterizer_discard_enable = features.extended_dynamic_state2.extendedDynamicState2 && (dld.vkCmdSetRasterizerDiscardEnableEXT != nullptr); + + if (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT == nullptr)) { + features.extended_dynamic_state2.extendedDynamicState2LogicOp = VK_FALSE; + } + + const bool eds2_any = eds2_depth_bias_enable || eds2_primitive_restart_enable || eds2_rasterizer_discard_enable || + (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT != nullptr)); + if (!eds2_any) { + extensions.extended_dynamic_state2 = false; + RemoveExtensionFeature(extensions.extended_dynamic_state2, features.extended_dynamic_state2, VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); + eds2_depth_bias_enable = eds2_primitive_restart_enable = eds2_rasterizer_discard_enable = false; + } + } else { + extensions.extended_dynamic_state2 = false; + } RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); dynamic_state3_blending = false; @@ -686,13 +756,96 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR case Settings::ExtendedDynamicState::EDS3: default: // Level 3: Enable all (EDS1 + EDS2 + EDS3) + { + bool eds1_any = (features.extended_dynamic_state.extendedDynamicState && + ((dld.vkCmdBindVertexBuffers2EXT != nullptr) || (dld.vkCmdSetCullModeEXT != nullptr))); + bool eds2_any = ((features.extended_dynamic_state2.extendedDynamicState2 && + ((dld.vkCmdSetDepthBiasEnableEXT != nullptr) || (dld.vkCmdSetPrimitiveRestartEnableEXT != nullptr))) || + (features.extended_dynamic_state2.extendedDynamicState2LogicOp && (dld.vkCmdSetLogicOpEXT != nullptr))); + if (!(eds1_any || eds2_any)) { + extensions.extended_dynamic_state3 = false; + } + } + if (dyna_state_enabled && extensions.extended_dynamic_state3) { + if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable && (dld.vkCmdSetColorBlendEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation && (dld.vkCmdSetColorBlendEquationEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask && (dld.vkCmdSetColorWriteMaskEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable && (dld.vkCmdSetDepthClampEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable && (dld.vkCmdSetLogicOpEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable && (dld.vkCmdSetAlphaToCoverageEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable && (dld.vkCmdSetAlphaToOneEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode && (dld.vkCmdSetLineRasterizationModeEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode && (dld.vkCmdSetConservativeRasterizationModeEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode = VK_FALSE; + } + if (!(features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable && (dld.vkCmdSetLineStippleEnableEXT != nullptr))) { + features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable = VK_FALSE; + } + + bool blending = features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable || + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation || + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; + bool enables = features.extended_dynamic_state3.extendedDynamicState3DepthClampEnable || + features.extended_dynamic_state3.extendedDynamicState3LogicOpEnable || + features.extended_dynamic_state3.extendedDynamicState3LineStippleEnable || + features.extended_dynamic_state3.extendedDynamicState3AlphaToCoverageEnable || + features.extended_dynamic_state3.extendedDynamicState3AlphaToOneEnable || + features.extended_dynamic_state3.extendedDynamicState3LineRasterizationMode || + features.extended_dynamic_state3.extendedDynamicState3ConservativeRasterizationMode; + if (!blending && !enables) { + extensions.extended_dynamic_state3 = false; + RemoveExtensionFeature(extensions.extended_dynamic_state3, features.extended_dynamic_state3, VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); + } else { + dynamic_state3_blending = blending; + dynamic_state3_enables = enables; + } + } else { + extensions.extended_dynamic_state3 = false; + } break; } - // VK_EXT_vertex_input_dynamic_state is independent from EDS - // It can be enabled even without extended_dynamic_state + // VK_EXT_vertex_input_dynamic_state and features.vertex_input_binding_stride 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); + } else { + bool vids_entrypoint = dld.vkCmdSetVertexInputEXT != nullptr; + bool vb2_entrypoint = dld.vkCmdBindVertexBuffers2EXT != nullptr; + + if (!vids_entrypoint) { + features.vertex_input_dynamic_state.vertexInputDynamicState = VK_FALSE; + } + + if (vids_entrypoint && features.vertex_input_dynamic_state.vertexInputDynamicState) { + vertex_input_binding_stride = false; + } else { + // Only enable binding stride if EDS1 and the VB2 entrypoint are available. + vertex_input_binding_stride = features.extended_dynamic_state.extendedDynamicState && vb2_entrypoint; + } + + if (!features.vertex_input_dynamic_state.vertexInputDynamicState) { + extensions.vertex_input_dynamic_state = false; + } + } + + if (!features.extended_dynamic_state.extendedDynamicState) { + extensions.extended_dynamic_state = false; } logical = vk::Device::Create(physical, queue_cis, ExtensionListForVulkan(loaded_extensions), first_next, dld); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cf341726a7..d435126a2f 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -569,31 +569,112 @@ public: return features.custom_border_color.customBorderColorWithoutFormat; } + /// Core dynamic states availability (Vulkan 1.0) + bool SupportsCoreDynamicViewportScissor() const { + return core_dynamic_viewport_scissor; + } + bool SupportsCoreDynamicDepthBias() const { + return core_dynamic_depth_bias; + } + bool SupportsCoreDynamicDepthBounds() const { + return core_dynamic_depth_bounds; + } + bool SupportsCoreDynamicLineWidth() const { + return core_dynamic_line_width; + } + bool SupportsCoreDynamicStencilMasks() const { + return core_dynamic_stencil_masks; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state. bool IsExtExtendedDynamicStateSupported() const { return extensions.extended_dynamic_state; } + /// VK_EXT_extended_dynamic_state core dynamic states availability + bool SupportsEds1CullMode() const { + return eds1_cull_mode; + } + bool SupportsEds1FrontFace() const { + return eds1_front_face; + } + bool SupportsEds1DepthTestEnable() const { + return eds1_depth_test_enable; + } + bool SupportsEds1DepthWriteEnable() const { + return eds1_depth_write_enable; + } + bool SupportsEds1DepthCompareOp() const { + return eds1_depth_compare_op; + } + bool SupportsEds1DepthBoundsTestEnable() const { + return eds1_depth_bounds_test_enable; + } + bool SupportsEds1StencilTestEnable() const { + return eds1_stencil_test_enable; + } + bool SupportsEds1StencilOp() const { + return eds1_stencil_op; + } + bool SupportsEds1VertexInputBindingStride() const { + return eds1_vertex_input_binding_stride; + } + /// Returns true if the device supports VK_EXT_extended_dynamic_state2. bool IsExtExtendedDynamicState2Supported() const { return extensions.extended_dynamic_state2; } + /// VK_EXT_extended_dynamic_state2 core dynamic states availability + bool SupportsEds2DepthBiasEnable() const { + return eds2_depth_bias_enable; + } + bool SupportsEds2PrimitiveRestartEnable() const { + return eds2_primitive_restart_enable; + } + bool SupportsEds2RasterizerDiscardEnable() const { + return eds2_rasterizer_discard_enable; + } + bool IsExtExtendedDynamicState2ExtrasSupported() const { return features.extended_dynamic_state2.extendedDynamicState2LogicOp; } + bool SupportsDynamicState2LogicOp() const { + return extensions.extended_dynamic_state2 && + features.extended_dynamic_state2.extendedDynamicState2LogicOp; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3. bool IsExtExtendedDynamicState3Supported() const { return extensions.extended_dynamic_state3; } + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color blend enable. + bool SupportsDynamicState3ColorBlendEnable() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color blend equation. + bool SupportsDynamicState3ColorBlendEquation() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ColorBlendEquation; + } + + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 color write mask. + bool SupportsDynamicState3ColorWriteMask() const { + return extensions.extended_dynamic_state3 && + features.extended_dynamic_state3.extendedDynamicState3ColorWriteMask; + } + /// Returns true if the device supports VK_EXT_4444_formats. bool IsExt4444FormatsSupported() const { return features.format_a4b4g4r4.formatA4B4G4R4; } - /// Returns true if the device supports VK_EXT_extended_dynamic_state3. + /// Returns true if the device supports VK_EXT_extended_dynamic_state3 blending. bool IsExtExtendedDynamicState3BlendingSupported() const { return dynamic_state3_blending; } @@ -1022,15 +1103,32 @@ private: bool supports_d24_depth{}; ///< Supports D24 depth buffers. bool cant_blit_msaa{}; ///< Does not support MSAA<->MSAA blitting. bool must_emulate_scaled_formats{}; ///< Requires scaled vertex format emulation + bool core_dynamic_viewport_scissor{}; ///< vkCmdSetViewport && vkCmdSetScissor + bool core_dynamic_depth_bias{}; ///< vkCmdSetDepthBias (or alternative) + bool core_dynamic_depth_bounds{}; ///< vkCmdSetDepthBounds + bool core_dynamic_line_width{}; ///< vkCmdSetLineWidth + bool core_dynamic_stencil_masks{}; ///< vkCmdSetStencilCompareMask/WriteMask/Reference + bool eds1_cull_mode{}; ///< Has EDS1 support for dynamic cull mode state. + bool eds1_front_face{}; ///< Has EDS1 support for dynamic front face state. + bool eds1_depth_test_enable{}; ///< Has EDS1 support for dynamic depth test enable state. + bool eds1_depth_write_enable{}; ///< Has EDS1 support for dynamic depth write enable state. + bool eds1_depth_compare_op{}; ///< Has EDS1 support for dynamic depth compare op state. + bool eds1_depth_bounds_test_enable{}; ///< Has EDS1 support for dynamic depth bounds test enable state. + bool eds1_stencil_test_enable{}; ///< Has EDS1 support for dynamic stencil test enable state. + bool eds1_stencil_op{}; ///< Has EDS1 support for dynamic stencil op state. + bool eds1_vertex_input_binding_stride{}; ///< Has EDS1 support for dynamic vertex input binding stride state. + bool eds2_depth_bias_enable{}; ///< Has EDS2 support for depth bias enable state. + bool eds2_primitive_restart_enable{}; ///< Has EDS2 support for primitive restart enable state. + bool eds2_rasterizer_discard_enable{}; ///< Has EDS2 support for rasterizer discard enable state. bool dynamic_state3_blending{}; ///< Has blending features of dynamic_state3. bool dynamic_state3_enables{}; ///< Has at least one enable feature of dynamic_state3. - bool dynamic_state3_depth_clamp_enable{}; - bool dynamic_state3_logic_op_enable{}; - bool dynamic_state3_line_raster_mode{}; - bool dynamic_state3_conservative_raster_mode{}; - bool dynamic_state3_line_stipple_enable{}; - bool dynamic_state3_alpha_to_coverage{}; - bool dynamic_state3_alpha_to_one{}; + bool dynamic_state3_depth_clamp_enable{}; ///< Has dynamic_state3 support for depth clamp enable state. + bool dynamic_state3_logic_op_enable{}; ///< Has dynamic_state3 support for logic op enable state. + bool dynamic_state3_line_raster_mode{}; ///< Has dynamic_state3 support for line raster mode state. + bool dynamic_state3_conservative_raster_mode{}; ///< Has dynamic_state3 support for conservative raster mode state. + bool dynamic_state3_line_stipple_enable{}; ///< Has dynamic_state3 support for line stipple enable state. + bool dynamic_state3_alpha_to_coverage{}; ///< Has dynamic_state3 support for alpha to coverage state. + bool dynamic_state3_alpha_to_one{}; ///< Has dynamic_state3 support for alpha to one state. bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow. size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited). u64 device_access_memory{}; ///< Total size of device local memory in bytes.