From 48fa6d0e0374b35c28852e24e4619ec0e5e8dc7c Mon Sep 17 00:00:00 2001 From: collecting Date: Tue, 10 Feb 2026 02:03:57 -0500 Subject: [PATCH 1/3] fix(vulkan): MP4 Mountain Jitter --- .../present/window_adapt_pass.cpp | 31 +++++++++---------- .../renderer_vulkan/vk_buffer_cache.cpp | 6 ++-- .../renderer_vulkan/vk_query_cache.cpp | 22 ++++++------- .../renderer_vulkan/vk_rasterizer.cpp | 14 +++++---- 4 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp index 232844413..6408e1433 100644 --- a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp @@ -163,11 +163,10 @@ void WindowAdaptPass::CreateDescriptorSetLayout() { } void WindowAdaptPass::CreatePipelineLayout() { - // Support up to 3 push constant ranges: + // Support up to 2 push constant ranges: // 0: PresentPushConstants (vertex shader) - // 1: Lanczos quality (fragment shader) - optional - // 2: CRT parameters (fragment shader) - optional - std::array ranges{}; + // 1: Fragment shader parameters (Lanczos + CRT) + std::array ranges{}; // Range 0: The existing constants for the Vertex Shader ranges[0] = { @@ -176,16 +175,14 @@ void WindowAdaptPass::CreatePipelineLayout() { .size = sizeof(PresentPushConstants), }; - // Range 1: Lanczos quality for the Fragment Shader - ranges[1] = { - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .offset = sizeof(PresentPushConstants), - .size = sizeof(s32), - }; - - // Range 2: CRT parameters for the Fragment Shader - // Offset after PresentPushConstants + Lanczos (if used) - // CRT constants: 8 floats + 1 int = 36 bytes + // Range 1: All parameters for the Fragment Shader (Lanczos + CRT) + // We combine them into a single range because Vulkan does not allow multiple ranges + // for the same stage if they are provided separately in some drivers/configs. + // Spec says: "For each shader stage, there must be at most one push constant range + // that includes that stage in its stageFlags." - actually the spec says: + // "Each element of pPushConstantRanges must contain at least one stage flag in stageFlags" + // and "Any two elements of pPushConstantRanges must not include the same stage flag in + // stageFlags" struct CRTPushConstants { float scanline_strength; float curvature; @@ -197,10 +194,10 @@ void WindowAdaptPass::CreatePipelineLayout() { float screen_width; float screen_height; }; - ranges[2] = { + ranges[1] = { .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .offset = sizeof(PresentPushConstants) + sizeof(s32), - .size = sizeof(CRTPushConstants), + .offset = sizeof(PresentPushConstants), + .size = sizeof(s32) + sizeof(CRTPushConstants), }; pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index e7e8fd58b..aea7c2890 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -8,10 +8,11 @@ #include #include "common/alignment.h" -#include "common/literals.h" #include "common/common_types.h" +#include "common/literals.h" #include "video_core/renderer_vulkan/vk_buffer_cache.h" + #include "video_core/renderer_vulkan/maxwell_to_vk.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" @@ -660,7 +661,8 @@ vk::Buffer BufferCacheRuntime::CreateNullBuffer() { .flags = 0, .size = 4, .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | - VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT, + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT | + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index b215e9f06..608cb131f 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -873,17 +873,16 @@ private: return; } has_flushed_end_pending = true; + UpdateBuffers(); if (!has_started || buffers_count == 0) { scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); - UpdateBuffers(); return; } scheduler.Record([this, total = static_cast(buffers_count)](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); }); - UpdateBuffers(); } void FlushEndTFB() { @@ -899,15 +898,17 @@ private: cmdbuf.EndTransformFeedbackEXT(0, 0, nullptr, nullptr); }); } else { - scheduler.Record([this, - total = static_cast(buffers_count)](vk::CommandBuffer cmdbuf) { - cmdbuf.EndTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); - }); + scheduler.Record( + [this, total = static_cast(buffers_count)](vk::CommandBuffer cmdbuf) { + cmdbuf.EndTransformFeedbackEXT(0, total, counter_buffers.data(), + offsets.data()); + }); } } catch (...) { // If query ending fails, we'll log it but continue // This prevents crashes from malformed query states - LOG_WARNING(Render_Vulkan, "Failed to end transform feedback query, continuing execution"); + LOG_WARNING(Render_Vulkan, + "Failed to end transform feedback query, continuing execution"); } } @@ -1188,10 +1189,9 @@ struct QueryCacheRuntimeImpl { StagingBufferPool& staging_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue, DescriptorPool& descriptor_pool) - : rasterizer{rasterizer_}, device_memory{device_memory_}, - buffer_cache{buffer_cache_}, device{device_}, - memory_allocator{memory_allocator_}, scheduler{scheduler_}, staging_pool{staging_pool_}, - guest_streamer(0, runtime), + : rasterizer{rasterizer_}, device_memory{device_memory_}, buffer_cache{buffer_cache_}, + device{device_}, memory_allocator{memory_allocator_}, scheduler{scheduler_}, + staging_pool{staging_pool_}, guest_streamer(0, runtime), sample_streamer(static_cast(QueryType::ZPassPixelCount64), runtime, rasterizer, device, scheduler, memory_allocator, compute_pass_descriptor_queue, descriptor_pool), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e0f90c402..d3ca9c51f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -9,6 +9,7 @@ #include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "citron/util/title_ids.h" #include "common/assert.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -38,7 +39,7 @@ #include "video_core/texture_cache/texture_cache_base.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" -#include "citron/util/title_ids.h" + namespace Vulkan { @@ -66,13 +67,13 @@ struct DrawParams { VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index, float scale) { const auto& src = regs.viewport_transform[index]; const auto conv = [scale](float value) { - float new_value = value * scale; + const double new_value = static_cast(value) * static_cast(scale); if (scale < 1.0f) { const bool sign = std::signbit(value); - new_value = std::round(std::abs(new_value)); - new_value = sign ? -new_value : new_value; + double rounded = std::round(std::abs(new_value)); + return static_cast(sign ? -rounded : rounded); } - return new_value; + return static_cast(new_value); }; const float x = conv(src.translate_x - src.scale_x); const float width = conv(src.scale_x * 2.0f); @@ -1107,7 +1108,8 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg }; } scheduler.Record([this, viewport_list](vk::CommandBuffer cmdbuf) { - const u32 num_viewports = std::min(device.GetMaxViewports(), Maxwell::NumViewports); + const u32 num_viewports = + std::min(device.GetMaxViewports(), Maxwell::NumViewports); const vk::Span viewports(viewport_list.data(), num_viewports); cmdbuf.SetViewport(0, viewports); }); From 66ca68a52efee3ee3259b643da05d66cb0816bc5 Mon Sep 17 00:00:00 2001 From: Zephyron Date: Tue, 10 Feb 2026 17:19:50 +1000 Subject: [PATCH 2/3] perf(vulkan): use double viewport math only when scale < 1 Signed-off-by: Zephyron --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d3ca9c51f..ddc935503 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -67,13 +67,14 @@ struct DrawParams { VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index, float scale) { const auto& src = regs.viewport_transform[index]; const auto conv = [scale](float value) { - const double new_value = static_cast(value) * static_cast(scale); - if (scale < 1.0f) { - const bool sign = std::signbit(value); - double rounded = std::round(std::abs(new_value)); - return static_cast(sign ? -rounded : rounded); + if (scale >= 1.0f) { + return value * scale; } - return static_cast(new_value); + // Use double only when scaling down to avoid sub-pixel jitter (MP4 fix) + const double new_value = static_cast(value) * static_cast(scale); + const bool sign = std::signbit(value); + const double rounded = std::round(std::abs(new_value)); + return static_cast(sign ? -rounded : rounded); }; const float x = conv(src.translate_x - src.scale_x); const float width = conv(src.scale_x * 2.0f); From c116f001b5c437830562c47b117a820892b521e4 Mon Sep 17 00:00:00 2001 From: Zephyron Date: Tue, 10 Feb 2026 17:42:15 +1000 Subject: [PATCH 3/3] fix(vulkan): revert query cache FlushBeginTFB order to fix menu freeze Signed-off-by: Zephyron --- src/video_core/renderer_vulkan/vk_query_cache.cpp | 3 ++- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 13 ++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 608cb131f..78e97ca25 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -873,16 +873,17 @@ private: return; } has_flushed_end_pending = true; - UpdateBuffers(); if (!has_started || buffers_count == 0) { scheduler.Record([](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, 0, nullptr, nullptr); }); + UpdateBuffers(); return; } scheduler.Record([this, total = static_cast(buffers_count)](vk::CommandBuffer cmdbuf) { cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); }); + UpdateBuffers(); } void FlushEndTFB() { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ddc935503..d3ca9c51f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -67,14 +67,13 @@ struct DrawParams { VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index, float scale) { const auto& src = regs.viewport_transform[index]; const auto conv = [scale](float value) { - if (scale >= 1.0f) { - return value * scale; - } - // Use double only when scaling down to avoid sub-pixel jitter (MP4 fix) const double new_value = static_cast(value) * static_cast(scale); - const bool sign = std::signbit(value); - const double rounded = std::round(std::abs(new_value)); - return static_cast(sign ? -rounded : rounded); + if (scale < 1.0f) { + const bool sign = std::signbit(value); + double rounded = std::round(std::abs(new_value)); + return static_cast(sign ? -rounded : rounded); + } + return static_cast(new_value); }; const float x = conv(src.translate_x - src.scale_x); const float width = conv(src.scale_x * 2.0f);