From a8f4fe55cb82c7e3438e5d54c33a51b289948504 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 9 Apr 2026 02:11:13 -0400 Subject: [PATCH] =?UTF-8?q?[vulkan]=20Adjustment=20ExtendedDynamicState=20?= =?UTF-8?q?System=20N=C2=AA1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/video_core/buffer_cache/buffer_cache.h | 64 ++++++++++++++++++- .../renderer_vulkan/vk_buffer_cache.h | 25 +++++++- .../renderer_vulkan/vk_graphics_pipeline.h | 3 + .../renderer_vulkan/vk_rasterizer.cpp | 25 +++----- .../renderer_vulkan/vk_rasterizer.h | 3 +- .../renderer_vulkan/vk_scheduler.cpp | 19 ++++-- .../renderer_vulkan/vk_state_tracker.h | 33 +++++++++- src/video_core/vulkan_common/vulkan_device.h | 4 ++ 8 files changed, 147 insertions(+), 29 deletions(-) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 014b4a318e..f0c253dffb 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -806,6 +806,58 @@ void BufferCache

::UpdateVertexBufferSlot(u32 index, const Binding& binding) { template void BufferCache

::BindHostVertexBuffers() { + bool needs_vertex_input_refresh = false; + bool use_vertex_input_dynamic_state = false; + bool use_dynamic_stride = false; + u32 max_dynamic_stride = 0; + std::array min_dynamic_stride{}; + if constexpr (!IS_OPENGL) { + use_vertex_input_dynamic_state = runtime.UsesVertexInputDynamicState(); + if (use_vertex_input_dynamic_state) { + for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) { + if (maxwell3d->dirty.flags[Dirty::VertexBuffer0 + index]) { + needs_vertex_input_refresh = true; + break; + } + } + } + + use_dynamic_stride = runtime.UsesDynamicVertexBindingStride(); + if (use_dynamic_stride) { + max_dynamic_stride = runtime.GetMaxVertexInputBindingStride(); + for (const auto& attribute : maxwell3d->regs.vertex_attrib_format) { + if (attribute.constant != 0) { + continue; + } + const u32 binding = attribute.buffer; + if (binding >= NUM_VERTEX_BUFFERS) { + continue; + } + const u32 extent = attribute.offset + attribute.SizeInBytes(); + min_dynamic_stride[binding] = (std::max)(min_dynamic_stride[binding], extent); + } + } + } + + const auto sanitize_stride = [&](u32 binding, u32 stride) -> u32 { + if constexpr (IS_OPENGL) { + return stride; + } else { + if (!use_dynamic_stride || stride == 0) { + return stride; + } + const u32 min_stride = min_dynamic_stride[binding]; + const u32 required_stride = (std::max)(stride, min_stride); + if (required_stride <= max_dynamic_stride) { + return required_stride; + } + if (min_stride > max_dynamic_stride) { + return 0; + } + return max_dynamic_stride; + } + }; + #ifdef ANDROID const bool use_optimized_vertex_buffers = Settings::values.use_optimized_vertex_buffers.GetValue(); #else @@ -838,7 +890,8 @@ void BufferCache

::BindHostVertexBuffers() { continue; } flags[Dirty::VertexBuffer0 + index] = false; - const u32 stride = maxwell3d->regs.vertex_streams[index].stride; + const u32 stride = + sanitize_stride(index, maxwell3d->regs.vertex_streams[index].stride); const u32 offset = buffer.Offset(binding.device_addr); buffer.MarkUsage(offset, binding.size); if (!bindings.buffers.empty() && index != last_index + 1) { @@ -881,7 +934,8 @@ void BufferCache

::BindHostVertexBuffers() { const Binding& binding = channel_state->vertex_buffers[index]; Buffer& buffer = slot_buffers[binding.buffer_id]; - const u32 stride = maxwell3d->regs.vertex_streams[index].stride; + const u32 stride = + sanitize_stride(index, maxwell3d->regs.vertex_streams[index].stride); const u32 offset = buffer.Offset(binding.device_addr); buffer.MarkUsage(offset, binding.size); @@ -893,6 +947,12 @@ void BufferCache

::BindHostVertexBuffers() { runtime.BindVertexBuffers(host_bindings); } } + + if constexpr (!IS_OPENGL) { + if (needs_vertex_input_refresh) { + runtime.NotifyVertexInputBindingChange(); + } + } } template diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index c8dd68e3dc..9f19f802f3 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -161,6 +161,28 @@ public: return max_dynamic_storage_buffers; } + bool UsesDynamicVertexBindingStride() const { + return device.IsExtExtendedDynamicStateSupported() && !use_vertex_input_dynamic_state; + } + + bool UsesVertexInputDynamicState() const { + return use_vertex_input_dynamic_state; + } + + void NotifyVertexInputBindingChange() { + needs_vertex_input_refresh = true; + } + + bool ConsumeVertexInputBindingChange() { + const bool refresh = needs_vertex_input_refresh; + needs_vertex_input_refresh = false; + return refresh; + } + + u32 GetMaxVertexInputBindingStride() const { + return device.GetMaxVertexInputBindingStride(); + } + private: void BindBuffer(VkBuffer buffer, u32 offset, u32 size) { guest_descriptor_queue.AddBuffer(buffer, offset, size); @@ -184,6 +206,7 @@ private: QuadIndexedPass quad_index_pass; bool use_vertex_input_dynamic_state = false; + bool needs_vertex_input_refresh = false; bool limit_dynamic_storage_buffers = false; u32 max_dynamic_storage_buffers = (std::numeric_limits::max)(); }; diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h index 1a41e50a36..e3e2737e3b 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h @@ -82,6 +82,9 @@ public: const std::array& infos); bool HasDynamicVertexInput() const noexcept { return key.state.dynamic_vertex_input; } + bool UsesVertexAttribute(size_t index) const noexcept { + return index < Maxwell::NumVertexAttributes && stage_infos[0].loads.Generic(index); + } bool SupportsAlphaToCoverage() const noexcept { return fragment_has_color0_output; } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ad431c9ac5..108e6d649e 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1014,20 +1014,6 @@ void RasterizerVulkan::UpdateDynamicStates() { // EDS3 Enables: LogicOpEnable, DepthClamp, LineStipple, ConservativeRaster if (device.IsExtExtendedDynamicState3EnablesSupported()) { - using namespace Tegra::Engines; - // AMD Workaround: LogicOp incompatible with float render targets - if (device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_OPEN_SOURCE || - device.GetDriverID() == VkDriverIdKHR::VK_DRIVER_ID_AMD_PROPRIETARY) { - const auto has_float = std::any_of( - regs.vertex_attrib_format.begin(), regs.vertex_attrib_format.end(), - [](const auto& attrib) { - return attrib.type == Maxwell3D::Regs::VertexAttribute::Type::Float; - } - ); - if (regs.logic_op.enable) { - regs.logic_op.enable = static_cast(!has_float); - } - } UpdateLogicOpEnable(regs); UpdateDepthClampEnable(regs); UpdateLineRasterizationMode(regs); @@ -1045,7 +1031,7 @@ void RasterizerVulkan::UpdateDynamicStates() { // Vertex Input Dynamic State: Independent from EDS levels if (device.IsExtVertexInputDynamicStateSupported()) { if (auto* gp = pipeline_cache.CurrentGraphicsPipeline(); gp && gp->HasDynamicVertexInput()) { - UpdateVertexInput(regs); + UpdateVertexInput(regs, *gp); } } } @@ -1725,9 +1711,11 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& }); } -void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { +void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline) { auto& dirty{maxwell3d->dirty.flags}; - if (!dirty[Dirty::VertexInput]) { + const bool force_vertex_input_refresh = buffer_cache_runtime.ConsumeVertexInputBindingChange(); + if (!dirty[Dirty::VertexInput] && !force_vertex_input_refresh) { return; } dirty[Dirty::VertexInput] = false; @@ -1758,6 +1746,9 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) if (attribute.constant) { continue; } + if (!pipeline.UsesVertexAttribute(index)) { + continue; + } const size_t binding{attribute.buffer}; if (binding >= max_vertex_bindings) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b689c6b660..b3bf6c8b7c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -191,7 +191,8 @@ private: void UpdateLogicOp(Tegra::Engines::Maxwell3D::Regs& regs); void UpdateBlending(Tegra::Engines::Maxwell3D::Regs& regs); - void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs); + void UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs, + const GraphicsPipeline& pipeline); Tegra::GPU& gpu; Tegra::MaxwellDeviceMemoryManager& device_memory; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 226619d8d6..f974505b2d 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -153,11 +153,17 @@ void Scheduler::RequestOutsideRenderPassOperationContext() { } bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) { + const auto consume_eds_refresh = [this] { + if (!state.needs_state_enable_refresh) { + return; + } + state_tracker.InvalidateExtendedDynamicStateFlags(); + state.needs_state_enable_refresh = false; + }; + if (state.graphics_pipeline == pipeline) { - if (pipeline && pipeline->UsesExtendedDynamicState() && - state.needs_state_enable_refresh) { - state_tracker.InvalidateStateEnableFlag(); - state.needs_state_enable_refresh = false; + if (pipeline && pipeline->UsesExtendedDynamicState()) { + consume_eds_refresh(); } return false; } @@ -170,9 +176,8 @@ bool Scheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) { if (!pipeline->UsesExtendedDynamicState()) { state.needs_state_enable_refresh = true; - } else if (state.needs_state_enable_refresh) { - state_tracker.InvalidateStateEnableFlag(); - state.needs_state_enable_refresh = false; + } else { + consume_eds_refresh(); } return true; diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 74bae9e181..f7c61ed7ef 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -101,6 +101,37 @@ public: (*flags)[Dirty::StateEnable] = true; } + void InvalidateExtendedDynamicStateFlags() { + InvalidateStateEnableFlag(); + + (*flags)[Dirty::CullMode] = true; + (*flags)[Dirty::DepthCompareOp] = true; + (*flags)[Dirty::FrontFace] = true; + (*flags)[Dirty::StencilOp] = true; + (*flags)[Dirty::DepthBoundsEnable] = true; + (*flags)[Dirty::DepthTestEnable] = true; + (*flags)[Dirty::DepthWriteEnable] = true; + (*flags)[Dirty::StencilTestEnable] = true; + + (*flags)[Dirty::PrimitiveRestartEnable] = true; + (*flags)[Dirty::RasterizerDiscardEnable] = true; + (*flags)[Dirty::DepthBiasEnable] = true; + (*flags)[Dirty::LogicOp] = true; + + (*flags)[Dirty::LogicOpEnable] = true; + (*flags)[Dirty::DepthClampEnable] = true; + (*flags)[Dirty::LineRasterizationMode] = true; + (*flags)[Dirty::LineStippleEnable] = true; + (*flags)[Dirty::ConservativeRasterizationMode] = true; + (*flags)[Dirty::AlphaToCoverageEnable] = true; + (*flags)[Dirty::AlphaToOneEnable] = true; + + (*flags)[Dirty::Blending] = true; + (*flags)[Dirty::ColorMask] = true; + (*flags)[Dirty::BlendEnable] = true; + (*flags)[Dirty::BlendEquations] = true; + } + bool TouchViewports() { const bool dirty_viewports = Exchange(Dirty::Viewports, false); const bool rescale_viewports = Exchange(VideoCommon::Dirty::RescaleViewports, false); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 8914ef0eb3..7777f945f4 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -783,6 +783,10 @@ public: return properties.properties.limits.maxVertexInputBindings; } + u32 GetMaxVertexInputBindingStride() const { + return properties.properties.limits.maxVertexInputBindingStride; + } + u32 GetMaxViewports() const { return properties.properties.limits.maxViewports; }