From 1bb15b285ef384ae5803996d4e4ec48713a58d5c Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 12 Apr 2026 03:41:09 -0400 Subject: [PATCH] [vulkan] Adjustment Viewport/Scissor in DynamicState --- .../renderer_vulkan/fixed_pipeline_state.cpp | 30 +++++---- .../renderer_vulkan/fixed_pipeline_state.h | 8 ++- .../renderer_vulkan/vk_pipeline_cache.cpp | 6 ++ .../renderer_vulkan/vk_rasterizer.cpp | 63 +++++++++++++++++-- .../renderer_vulkan/vk_state_tracker.cpp | 1 + 5 files changed, 86 insertions(+), 22 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 866b721a84..d53cf70b37 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -190,9 +190,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } } } - if (!extended_dynamic_state_3_enables) { - dynamic_state.Refresh3(regs); - } + dynamic_state.Refresh3(regs, features); if (xfb_enabled) { RefreshXfbState(xfb_state, regs); } @@ -295,16 +293,22 @@ void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); } -void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) { - logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); - depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::Passthrough || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumZ); - - line_stipple_enable.Assign(regs.line_stipple_enable); +void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs, + const DynamicFeatures& features) { + if (!features.has_dynamic_state3_logic_op_enable) { + logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_depth_clamp_enable) { + depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ); + } + if (!features.has_dynamic_state3_line_stipple_enable) { + line_stipple_enable.Assign(regs.line_stipple_enable); + } } size_t FixedPipelineState::Hash() const noexcept { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index ffc91e9a55..44157d686d 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -27,6 +27,9 @@ struct DynamicFeatures { 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_state3_depth_clamp_enable; + bool has_dynamic_state3_logic_op_enable; + bool has_dynamic_state3_line_stipple_enable; bool has_dynamic_vertex_input; bool has_provoking_vertex; bool has_provoking_vertex_first_mode; @@ -175,7 +178,7 @@ struct FixedPipelineState { void Refresh(const Maxwell& regs); void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology, bool base_features_supported); - void Refresh3(const Maxwell& regs); + void Refresh3(const Maxwell& regs, const DynamicFeatures& features); Maxwell::ComparisonOp DepthTestFunc() const noexcept { return UnpackComparisonOp(depth_test_func); @@ -265,8 +268,7 @@ struct FixedPipelineState { return sizeof(*this); } if (dynamic_vertex_input && extended_dynamic_state_3_blend) { - // Exclude dynamic state and attributes - return offsetof(FixedPipelineState, dynamic_state); + return offsetof(FixedPipelineState, attachments); } if (dynamic_vertex_input) { // Exclude dynamic state diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 5f86506961..7b5ac7fac0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -485,6 +485,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.IsExtExtendedDynamicState3BlendingSupported(); dynamic_features.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(); + dynamic_features.has_dynamic_state3_depth_clamp_enable = + device.SupportsDynamicState3DepthClampEnable(); + dynamic_features.has_dynamic_state3_logic_op_enable = + device.SupportsDynamicState3LogicOpEnable(); + dynamic_features.has_dynamic_state3_line_stipple_enable = + device.SupportsDynamicState3LineStippleEnable(); // VIDS: Independent toggle (not affected by dyna_state levels) dynamic_features.has_dynamic_vertex_input = diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 8c01a3ad66..78337b3ebe 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -173,6 +173,28 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, } return params; } + +bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) { + switch (topology) { + case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: + return false; + default: + return true; + } +} + +bool IsPrimitiveRestartSupported(const Device& device, VkPrimitiveTopology topology) { + return ((topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsTopologyListPrimitiveRestartSupported()) || + SupportsPrimitiveRestart(topology) || + (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsPatchListPrimitiveRestartSupported())); +} } // Anonymous namespace RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, @@ -991,6 +1013,12 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info, void RasterizerVulkan::UpdateDynamicStates() { auto& regs = maxwell3d->regs; + auto& flags = maxwell3d->dirty.flags; + const auto topology = maxwell3d->draw_manager->GetDrawState().topology; + if (state_tracker.ChangePrimitiveTopology(topology)) { + flags[Dirty::DepthBiasEnable] = true; + flags[Dirty::PrimitiveRestartEnable] = true; + } // Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting UpdateViewportsState(regs); @@ -1099,6 +1127,9 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg if (!state_tracker.TouchViewports()) { return; } + + maxwell3d->dirty.flags[Dirty::Scissors] = true; + if (!regs.viewport_scale_offset_enabled) { float x = static_cast(regs.surface_clip.x); float y = static_cast(regs.surface_clip.y); @@ -1116,8 +1147,12 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg .minDepth = 0.0f, .maxDepth = 1.0f, }; - scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { - cmdbuf.SetViewport(0, viewport); + scheduler.Record([this, viewport](vk::CommandBuffer cmdbuf) { + const u32 num_viewports = std::min(device.GetMaxViewports(), Maxwell::NumViewports); + std::array viewport_list{}; + viewport_list.fill(viewport); + const vk::Span viewports(viewport_list.data(), num_viewports); + cmdbuf.SetViewport(0, viewports); }); return; } @@ -1157,8 +1192,12 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs scissor.offset.y = static_cast(y); scissor.extent.width = width; scissor.extent.height = height; - scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { - cmdbuf.SetScissor(0, scissor); + scheduler.Record([this, scissor](vk::CommandBuffer cmdbuf) { + const u32 num_scissors = std::min(device.GetMaxViewports(), Maxwell::NumViewports); + std::array scissor_list{}; + scissor_list.fill(scissor); + const vk::Span scissors(scissor_list.data(), num_scissors); + cmdbuf.SetScissor(0, scissors); }); return; } @@ -1403,7 +1442,17 @@ void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::R if (!state_tracker.TouchPrimitiveRestartEnable()) { return; } - scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) { + + bool enable = regs.primitive_restart.enabled != 0; + if (device.IsMoltenVK()) { + enable = true; + } else if (enable) { + const auto topology = + MaxwellToVK::PrimitiveTopology(device, maxwell3d->draw_manager->GetDrawState().topology); + enable = IsPrimitiveRestartSupported(device, topology); + } + + scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetPrimitiveRestartEnableEXT(enable); }); } @@ -1742,7 +1791,9 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { auto& dirty{maxwell3d->dirty.flags}; - if (!dirty[Dirty::VertexInput]) { + const bool vertex_input_dirty = dirty[Dirty::VertexInput]; + const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers]; + if (!vertex_input_dirty && !vertex_buffers_dirty) { return; } dirty[Dirty::VertexInput] = false; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 79967d540a..1e8530f288 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -87,6 +87,7 @@ Flags MakeInvalidationFlags() { void SetupDirtyViewports(Tables& tables) { FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); + FillBlock(tables[1], OFF(surface_clip), NUM(surface_clip), Viewports); tables[0][OFF(viewport_scale_offset_enabled)] = Viewports; tables[1][OFF(window_origin)] = Viewports; }