From 348433b4dc154d7032ed5805df3b5b788570b83b Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Sun, 15 Feb 2026 01:02:02 -0400 Subject: [PATCH] [vulkan] Fixing inconsistences within VK_EXT_extended_dynamic_state1 handling --- .../renderer_vulkan/fixed_pipeline_state.cpp | 36 ++++++++++++- .../renderer_vulkan/vk_graphics_pipeline.cpp | 7 ++- .../renderer_vulkan/vk_rasterizer.cpp | 50 ++++++++++++++++--- .../renderer_vulkan/vk_rasterizer.h | 1 + .../vulkan_common/vulkan_device.cpp | 18 +++---- .../vulkan_common/vulkan_wrapper.cpp | 11 ++++ src/video_core/vulkan_common/vulkan_wrapper.h | 10 ++++ 7 files changed, 112 insertions(+), 21 deletions(-) diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index c74f3824a0..b07f5927b3 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -50,6 +50,38 @@ void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& }); state.varyings = regs.stream_out_layout; } + +Maxwell::PrimitiveTopology NormalizeDynamicTopologyClass(Maxwell::PrimitiveTopology topology) { + switch (topology) { + case Maxwell::PrimitiveTopology::Points: + return Maxwell::PrimitiveTopology::Points; + + case Maxwell::PrimitiveTopology::Lines: + case Maxwell::PrimitiveTopology::LineStrip: + return Maxwell::PrimitiveTopology::Lines; + + case Maxwell::PrimitiveTopology::Triangles: + case Maxwell::PrimitiveTopology::TriangleStrip: + case Maxwell::PrimitiveTopology::TriangleFan: + case Maxwell::PrimitiveTopology::Quads: + case Maxwell::PrimitiveTopology::QuadStrip: + case Maxwell::PrimitiveTopology::Polygon: + case Maxwell::PrimitiveTopology::LineLoop: + return Maxwell::PrimitiveTopology::Triangles; + + case Maxwell::PrimitiveTopology::LinesAdjacency: + case Maxwell::PrimitiveTopology::LineStripAdjacency: + return Maxwell::PrimitiveTopology::LinesAdjacency; + + case Maxwell::PrimitiveTopology::TrianglesAdjacency: + case Maxwell::PrimitiveTopology::TriangleStripAdjacency: + return Maxwell::PrimitiveTopology::TrianglesAdjacency; + + case Maxwell::PrimitiveTopology::Patches: + return Maxwell::PrimitiveTopology::Patches; + } + return topology; +} } // Anonymous namespace void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFeatures& features) { @@ -71,7 +103,9 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == Maxwell::Tessellation::OutputPrimitives::Triangles_CW); patch_control_points_minus_one.Assign(regs.patch_vertices - 1); - topology.Assign(topology_); + const bool can_normalize_topology = + features.has_extended_dynamic_state && features.has_extended_dynamic_state_2; + topology.Assign(can_normalize_topology ? NormalizeDynamicTopologyClass(topology_) : topology_); msaa_mode.Assign(regs.anti_alias_samples_mode); raw2 = 0; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index bd6bd088cc..7c6f5da3d7 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -831,7 +831,6 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { .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, @@ -839,8 +838,11 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { }; if (key.state.extended_dynamic_state) { static constexpr std::array extended{ + VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT, + VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT, VK_DYNAMIC_STATE_CULL_MODE_EXT, VK_DYNAMIC_STATE_FRONT_FACE_EXT, + VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT, VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT, VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT, VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT, @@ -851,6 +853,9 @@ void GraphicsPipeline::MakePipeline(VkRenderPass render_pass) { dynamic_states.insert(dynamic_states.end(), extended.begin(), extended.end()); dynamic_states.push_back(VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT); + } else { + dynamic_states.push_back(VK_DYNAMIC_STATE_VIEWPORT); + dynamic_states.push_back(VK_DYNAMIC_STATE_SCISSOR); } // EDS2 - Core (3 states) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 5be3441f01..a722edbc6c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1032,6 +1032,7 @@ void RasterizerVulkan::UpdateDynamicStates() { UpdateCullMode(regs); UpdateDepthCompareOp(regs); UpdateFrontFace(regs); + UpdatePrimitiveTopology(regs); UpdateStencilOp(regs); if (state_tracker.TouchStateEnable()) { UpdateDepthBoundsTestEnable(regs); @@ -1106,8 +1107,14 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg .minDepth = 0.0f, .maxDepth = 1.0f, }; - scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { - cmdbuf.SetViewport(0, viewport); + const bool use_viewport_with_count = device.IsExtExtendedDynamicStateSupported(); + scheduler.Record([viewport, use_viewport_with_count](vk::CommandBuffer cmdbuf) { + if (use_viewport_with_count) { + std::array viewports{viewport}; + cmdbuf.SetViewportWithCountEXT(viewports); + } else { + cmdbuf.SetViewport(0, viewport); + } }); return; } @@ -1123,10 +1130,15 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale), GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale), }; - scheduler.Record([this, viewport_list](vk::CommandBuffer cmdbuf) { + const bool use_viewport_with_count = device.IsExtExtendedDynamicStateSupported(); + scheduler.Record([this, viewport_list, use_viewport_with_count](vk::CommandBuffer cmdbuf) { const u32 num_viewports = std::min(device.GetMaxViewports(), Maxwell::NumViewports); const vk::Span viewports(viewport_list.data(), num_viewports); - cmdbuf.SetViewport(0, viewports); + if (use_viewport_with_count) { + cmdbuf.SetViewportWithCountEXT(viewports); + } else { + cmdbuf.SetViewport(0, viewports); + } }); } @@ -1147,8 +1159,14 @@ 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); + const bool use_scissor_with_count = device.IsExtExtendedDynamicStateSupported(); + scheduler.Record([scissor, use_scissor_with_count](vk::CommandBuffer cmdbuf) { + if (use_scissor_with_count) { + std::array scissors{scissor}; + cmdbuf.SetScissorWithCountEXT(scissors); + } else { + cmdbuf.SetScissor(0, scissor); + } }); return; } @@ -1176,10 +1194,15 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs GetScissorState(regs, 14, up_scale, down_shift), GetScissorState(regs, 15, up_scale, down_shift), }; - scheduler.Record([this, scissor_list](vk::CommandBuffer cmdbuf) { + const bool use_scissor_with_count = device.IsExtExtendedDynamicStateSupported(); + scheduler.Record([this, scissor_list, use_scissor_with_count](vk::CommandBuffer cmdbuf) { const u32 num_scissors = std::min(device.GetMaxViewports(), Maxwell::NumViewports); const vk::Span scissors(scissor_list.data(), num_scissors); - cmdbuf.SetScissor(0, scissors); + if (use_scissor_with_count) { + cmdbuf.SetScissorWithCountEXT(scissors); + } else { + cmdbuf.SetScissor(0, scissors); + } }); } @@ -1451,6 +1474,17 @@ void RasterizerVulkan::UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& reg }); } +void RasterizerVulkan::UpdatePrimitiveTopology([[maybe_unused]] Tegra::Engines::Maxwell3D::Regs& regs) { + const auto topology = maxwell3d->draw_manager->GetDrawState().topology; + if (!state_tracker.ChangePrimitiveTopology(topology)) { + return; + } + const auto vk_topology = MaxwellToVK::PrimitiveTopology(device, topology); + scheduler.Record([vk_topology](vk::CommandBuffer cmdbuf) { + cmdbuf.SetPrimitiveTopologyEXT(vk_topology); + }); +} + void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) { if (!state_tracker.TouchFrontFace()) { return; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 2337ef644b..c8f50cd99b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -174,6 +174,7 @@ private: void UpdateDepthTestEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs); void UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateRasterizerDiscardEnable(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateDepthBiasEnable(Tegra::Engines::Maxwell3D::Regs& regs); diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index d2aebe72bb..ca6095f4ad 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -1150,8 +1150,6 @@ bool Device::GetSuitability(bool requires_swapchain) { if (u32(Settings::values.dyna_state.GetValue()) == 0) { LOG_INFO(Render_Vulkan, "Extended 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; } @@ -1187,7 +1185,7 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.depth_clip_control, features.depth_clip_control, VK_EXT_DEPTH_CLIP_CONTROL_EXTENSION_NAME); - /* */ // VK_EXT_extended_dynamic_state + // VK_EXT_extended_dynamic_state extensions.extended_dynamic_state = features.extended_dynamic_state.extendedDynamicState; RemoveExtensionFeatureIfUnsuitable(extensions.extended_dynamic_state, features.extended_dynamic_state, @@ -1200,7 +1198,6 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME); // VK_EXT_robustness2 - // Enable if at least one robustness2 feature is available extensions.robustness_2 = features.robustness2.robustBufferAccess2 || features.robustness2.robustImageAccess2 || features.robustness2.nullDescriptor; @@ -1209,7 +1206,6 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); // VK_EXT_image_robustness - // Enable if robustImageAccess is available extensions.image_robustness = features.image_robustness.robustImageAccess; RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); @@ -1305,15 +1301,15 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.swapchain_maintenance1, features.swapchain_maintenance1, VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); - // VK_KHR_maintenance1 (core in Vulkan 1.1, no features) + // VK_KHR_maintenance1 extensions.maintenance1 = loaded_extensions.contains(VK_KHR_MAINTENANCE_1_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance1, VK_KHR_MAINTENANCE_1_EXTENSION_NAME); - // VK_KHR_maintenance2 (core in Vulkan 1.1, no features) + // VK_KHR_maintenance2 extensions.maintenance2 = loaded_extensions.contains(VK_KHR_MAINTENANCE_2_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance2, VK_KHR_MAINTENANCE_2_EXTENSION_NAME); - // VK_KHR_maintenance3 (core in Vulkan 1.1, no features) + // VK_KHR_maintenance3 extensions.maintenance3 = loaded_extensions.contains(VK_KHR_MAINTENANCE_3_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance3, VK_KHR_MAINTENANCE_3_EXTENSION_NAME); @@ -1343,15 +1339,15 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6, VK_KHR_MAINTENANCE_6_EXTENSION_NAME); - // VK_KHR_maintenance7 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance7 extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME); - // VK_KHR_maintenance8 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance8 extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME); - // VK_KHR_maintenance9 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance9 extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME); RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME); } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 653f043963..fce0103eae 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -133,10 +133,12 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdSetDepthBounds); X(vkCmdSetEvent); X(vkCmdSetScissor); + X(vkCmdSetScissorWithCountEXT); X(vkCmdSetStencilCompareMask); X(vkCmdSetStencilReference); X(vkCmdSetStencilWriteMask); X(vkCmdSetViewport); + X(vkCmdSetViewportWithCountEXT); X(vkCmdWaitEvents); X(vkCmdBindVertexBuffers2EXT); X(vkCmdSetCullModeEXT); @@ -243,6 +245,15 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { Proc(dld.vkCmdDrawIndirectCount, dld, "vkCmdDrawIndirectCountKHR", device); Proc(dld.vkCmdDrawIndexedIndirectCount, dld, "vkCmdDrawIndexedIndirectCountKHR", device); } + if (!dld.vkCmdSetPrimitiveTopologyEXT) { + Proc(dld.vkCmdSetPrimitiveTopologyEXT, dld, "vkCmdSetPrimitiveTopology", device); + } + if (!dld.vkCmdSetViewportWithCountEXT) { + Proc(dld.vkCmdSetViewportWithCountEXT, dld, "vkCmdSetViewportWithCount", device); + } + if (!dld.vkCmdSetScissorWithCountEXT) { + Proc(dld.vkCmdSetScissorWithCountEXT, dld, "vkCmdSetScissorWithCount", device); + } #undef X } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 2f607ade7c..c7f459c0f4 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -248,12 +248,14 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdSetLineWidth vkCmdSetLineWidth{}; PFN_vkCmdSetPrimitiveTopologyEXT vkCmdSetPrimitiveTopologyEXT{}; PFN_vkCmdSetScissor vkCmdSetScissor{}; + PFN_vkCmdSetScissorWithCountEXT vkCmdSetScissorWithCountEXT{}; PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask{}; PFN_vkCmdSetStencilOpEXT vkCmdSetStencilOpEXT{}; PFN_vkCmdSetStencilReference vkCmdSetStencilReference{}; PFN_vkCmdSetStencilTestEnableEXT vkCmdSetStencilTestEnableEXT{}; PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask{}; PFN_vkCmdSetViewport vkCmdSetViewport{}; + PFN_vkCmdSetViewportWithCountEXT vkCmdSetViewportWithCountEXT{}; PFN_vkCmdWaitEvents vkCmdWaitEvents{}; PFN_vkCreateBuffer vkCreateBuffer{}; PFN_vkCreateBufferView vkCreateBufferView{}; @@ -1361,6 +1363,14 @@ public: dld->vkCmdSetScissor(handle, first, scissors.size(), scissors.data()); } + void SetViewportWithCountEXT(Span viewports) const noexcept { + dld->vkCmdSetViewportWithCountEXT(handle, viewports.size(), viewports.data()); + } + + void SetScissorWithCountEXT(Span scissors) const noexcept { + dld->vkCmdSetScissorWithCountEXT(handle, scissors.size(), scissors.data()); + } + void SetBlendConstants(const float blend_constants[4]) const noexcept { dld->vkCmdSetBlendConstants(handle, blend_constants); }