From 6e17a471207d2e436d30ed18accd591e04da64d9 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 26 Jan 2026 15:30:44 -0400 Subject: [PATCH] [vulkan] Fixing DynamicState wrong core feature callings --- src/video_core/renderer_vulkan/blit_image.cpp | 53 +++++++++++++++++-- .../renderer_vulkan/fixed_pipeline_state.cpp | 9 ++++ .../renderer_vulkan/fixed_pipeline_state.h | 9 ++++ .../renderer_vulkan/vk_graphics_pipeline.cpp | 36 ++++++++++--- .../renderer_vulkan/vk_pipeline_cache.cpp | 25 ++++++--- .../vulkan_common/vulkan_device.cpp | 10 +--- src/video_core/vulkan_common/vulkan_device.h | 37 +++++++++++++ 7 files changed, 151 insertions(+), 28 deletions(-) diff --git a/src/video_core/renderer_vulkan/blit_image.cpp b/src/video_core/renderer_vulkan/blit_image.cpp index 5b4b44db53..07295e65c6 100644 --- a/src/video_core/renderer_vulkan/blit_image.cpp +++ b/src/video_core/renderer_vulkan/blit_image.cpp @@ -5,6 +5,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include "video_core/renderer_vulkan/vk_texture_cache.h" @@ -761,10 +762,24 @@ void BlitImageHelper::ClearDepthStencil(const Framebuffer* dst_framebuffer, bool const VkPipeline pipeline = FindOrEmplaceClearStencilPipeline(key); const VkPipelineLayout layout = *clear_color_pipeline_layout; scheduler.RequestRenderpass(dst_framebuffer); - scheduler.Record([pipeline, layout, clear_depth, dst_region](vk::CommandBuffer cmdbuf) { + scheduler.Record([pipeline, layout, clear_depth, dst_region, stencil_mask, stencil_ref, + stencil_compare_mask, dyn_stencil_compare, dyn_stencil_write, + dyn_stencil_ref, this](vk::CommandBuffer cmdbuf) { constexpr std::array blend_constants{0.0f, 0.0f, 0.0f, 0.0f}; cmdbuf.SetBlendConstants(blend_constants.data()); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + if (dyn_stencil_compare) { + cmdbuf.SetStencilCompareMask(VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, + stencil_compare_mask); + } + if (dyn_stencil_write) { + cmdbuf.SetStencilWriteMask(VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, + stencil_mask); + } + if (dyn_stencil_ref) { + cmdbuf.SetStencilReference(VK_STENCIL_FACE_FRONT_BIT | VK_STENCIL_FACE_BACK_BIT, + stencil_ref); + } BindBlitState(cmdbuf, dst_region); cmdbuf.PushConstants(layout, VK_SHADER_STAGE_FRAGMENT_BIT, clear_depth); cmdbuf.Draw(3, 1, 0, 0); @@ -1010,14 +1025,19 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( } clear_stencil_keys.push_back(key); const std::array stages = MakeStages(*clear_color_vert, *clear_stencil_frag); + // Allow using dynamic stencil masks if the device supports them. + const bool dyn_stencil_compare = device.SupportsDynamicStateStencilCompareMask(); + const bool dyn_stencil_write = device.SupportsDynamicStateStencilWriteMask(); + const bool dyn_stencil_ref = device.SupportsDynamicStateStencilReference(); + const auto stencil = VkStencilOpState{ .failOp = VK_STENCIL_OP_KEEP, .passOp = VK_STENCIL_OP_REPLACE, .depthFailOp = VK_STENCIL_OP_KEEP, .compareOp = VK_COMPARE_OP_ALWAYS, - .compareMask = key.stencil_compare_mask, - .writeMask = key.stencil_mask, - .reference = key.stencil_ref, + .compareMask = dyn_stencil_compare ? 0u : key.stencil_compare_mask, + .writeMask = dyn_stencil_write ? 0u : key.stencil_mask, + .reference = dyn_stencil_ref ? 0u : key.stencil_ref, }; const VkPipelineDepthStencilStateCreateInfo depth_stencil_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, @@ -1034,6 +1054,29 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( .maxDepthBounds = 0.0f, }; const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci = GetPipelineInputAssemblyStateCreateInfo(device); + + // Build dynamic state list for this pipeline (base + optional stencil/linewidth) + std::vector dyn_states(DYNAMIC_STATES.begin(), DYNAMIC_STATES.end()); + if (device.SupportsDynamicStateLineWidth()) { + dyn_states.push_back(VK_DYNAMIC_STATE_LINE_WIDTH); + } + if (dyn_stencil_compare) { + dyn_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); + } + if (dyn_stencil_write) { + dyn_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); + } + if (dyn_stencil_ref) { + dyn_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); + } + const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .dynamicStateCount = static_cast(dyn_states.size()), + .pDynamicStates = dyn_states.data(), + }; + clear_stencil_pipelines.push_back(device.GetLogical().CreateGraphicsPipeline({ .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = nullptr, @@ -1048,7 +1091,7 @@ VkPipeline BlitImageHelper::FindOrEmplaceClearStencilPipeline( .pMultisampleState = &PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pDepthStencilState = &depth_stencil_ci, .pColorBlendState = &PIPELINE_COLOR_BLEND_STATE_GENERIC_CREATE_INFO, - .pDynamicState = &PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .pDynamicState = &dynamic_state_ci, .layout = *clear_color_pipeline_layout, .renderPass = key.renderpass, .subpass = 0, diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 06cbd9e6da..b79875a3a8 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -171,6 +171,15 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe if (!extended_dynamic_state_3_enables) { dynamic_state.Refresh3(regs); } + if (features.has_depth_bounds) { + depth_bounds_min = 0; + depth_bounds_max = 0; + dynamic_state.depth_bounds_enable.Assign(0); + } + + if (features.has_depth_bias) { + dynamic_state.depth_bias_enable.Assign(0); + } 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 c5bc14f448..4c615dbc7a 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -21,6 +21,15 @@ namespace Vulkan { using Maxwell = Tegra::Engines::Maxwell3D::Regs; struct DynamicFeatures { + bool has_viewport; + bool has_scissor; + bool has_depth_bias; + bool has_blend_constants; + bool has_depth_bounds; + bool has_stencil_compare_mask; + bool has_stencil_write_mask; + bool has_stencil_reference; + bool has_line_width; bool has_extended_dynamic_state; bool has_extended_dynamic_state_2; bool has_extended_dynamic_state_2_logic_op; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index fca235b58e..4e1a24559b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -815,13 +815,35 @@ 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; + + if (device.SupportsDynamicStateViewport()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); + } + if (device.SupportsDynamicStateScissor()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); + } + if (device.SupportsDynamicStateDepthBias()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS); + } + if (device.SupportsDynamicStateBlendConstants()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_BLEND_CONSTANTS); + } + if (device.SupportsDynamicStateDepthBounds()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_DEPTH_BOUNDS); + } + if (device.SupportsDynamicStateStencilCompareMask()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK); + } + if (device.SupportsDynamicStateStencilWriteMask()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_WRITE_MASK); + } + if (device.SupportsDynamicStateStencilReference()) { + dynamic_states.push_back(VK_DYNAMIC_STATE_STENCIL_REFERENCE); + } + if (device.SupportsDynamicStateLineWidth()) { + 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, diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index f81b63ef44..9690cc47bb 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -418,12 +418,24 @@ 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_viewport = + device.SupportsDynamicStateViewport(); + dynamic_features.has_scissor = + device.SupportsDynamicStateScissor(); + dynamic_features.has_depth_bias = + device.SupportsDynamicStateDepthBias(); + dynamic_features.has_blend_constants = + device.SupportsDynamicStateBlendConstants(); + dynamic_features.has_depth_bounds = + device.SupportsDynamicStateDepthBounds(); + dynamic_features.has_stencil_compare_mask = + device.SupportsDynamicStateStencilCompareMask(); + dynamic_features.has_stencil_write_mask = + device.SupportsDynamicStateStencilWriteMask(); + dynamic_features.has_stencil_reference = + device.SupportsDynamicStateStencilReference(); + dynamic_features.has_line_width = + device.SupportsDynamicStateLineWidth(); dynamic_features.has_extended_dynamic_state = device.IsExtExtendedDynamicStateSupported(); @@ -439,7 +451,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, dynamic_features.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(); - // VIDS: Independent toggle (not affected by dyna_state levels) dynamic_features.has_dynamic_vertex_input = device.IsExtVertexInputDynamicStateSupported() && Settings::values.vertex_input_dynamic_state.GetValue(); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 17da698458..eb310ce944 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -682,13 +682,6 @@ 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 @@ -723,8 +716,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR 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 is independent from ExtendedDynamicState level 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); } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a772cc84e2..9fff58b359 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -704,6 +704,43 @@ public: return dynamic_state3_alpha_to_one; } + // Core dynamic state support checks + bool SupportsDynamicStateViewport() const { + return dld.vkCmdSetViewport != nullptr; + } + + bool SupportsDynamicStateScissor() const { + return dld.vkCmdSetScissor != nullptr; + } + + bool SupportsDynamicStateDepthBias() const { + return dld.vkCmdSetDepthBias != nullptr || dld.vkCmdSetDepthBias2EXT != nullptr; + } + + bool SupportsDynamicStateBlendConstants() const { + return dld.vkCmdSetBlendConstants != nullptr; + } + + bool SupportsDynamicStateDepthBounds() const { + return dld.vkCmdSetDepthBounds != nullptr && IsDepthBoundsSupported(); + } + + bool SupportsDynamicStateStencilCompareMask() const { + return dld.vkCmdSetStencilCompareMask != nullptr; + } + + bool SupportsDynamicStateStencilWriteMask() const { + return dld.vkCmdSetStencilWriteMask != nullptr; + } + + bool SupportsDynamicStateStencilReference() const { + return dld.vkCmdSetStencilReference != nullptr; + } + + bool SupportsDynamicStateLineWidth() const { + return dld.vkCmdSetLineWidth != nullptr; + } + /// Returns true if the device supports VK_EXT_vertex_input_dynamic_state. bool IsExtVertexInputDynamicStateSupported() const { return extensions.vertex_input_dynamic_state;