From eeed9bc8d66a05dfeee646f94b0c470672026b26 Mon Sep 17 00:00:00 2001 From: TheLastRar Date: Mon, 1 Dec 2025 22:21:18 +0000 Subject: [PATCH] GS/VK: Restart present pass when uploading large textures --- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp | 34 +++++++++++++++++++++++ pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h | 3 ++ pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp | 28 ++++++++++++++++--- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index fb9cc45e3d..0aaf4663a8 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -2354,6 +2354,7 @@ GSDevice::PresentResult GSDeviceVK::BeginPresent(bool frame_skip) {0, 0}, {static_cast(swap_chain_texture->GetWidth()), static_cast(swap_chain_texture->GetHeight())}}; vkCmdSetViewport(GetCurrentCommandBuffer(), 0, 1, &vp); vkCmdSetScissor(GetCurrentCommandBuffer(), 0, 1, &scissor); + m_is_presenting = true; return PresentResult::OK; } @@ -2363,6 +2364,7 @@ void GSDeviceVK::EndPresent() VkCommandBuffer cmdbuffer = GetCurrentCommandBuffer(); vkCmdEndRenderPass(cmdbuffer); + m_is_presenting = false; m_swap_chain->GetCurrentTexture()->TransitionToLayout(cmdbuffer, GSTextureVK::Layout::PresentSrc); g_perfmon.Put(GSPerfMon::RenderPasses, 1); @@ -2372,6 +2374,11 @@ void GSDeviceVK::EndPresent() InvalidateCachedState(); } +bool GSDeviceVK::IsPresenting() const +{ + return m_is_presenting; +} + #ifdef ENABLE_OGL_DEBUG static std::array Palette(float phase, const std::array& a, const std::array& b, const std::array& c, const std::array& d) @@ -5001,6 +5008,33 @@ void GSDeviceVK::ExecuteCommandBufferAndRestartRenderPass(bool wait_for_completi } } +void GSDeviceVK::ExecuteCommandBufferAndRestartPresent(bool wait_for_completion, const char* reason, ...) +{ + std::va_list ap; + va_start(ap, reason); + const std::string reason_str(StringUtil::StdStringFromFormatV(reason, ap)); + va_end(ap); + + Console.Warning("VK: Executing command buffer due to '%s'", reason_str.c_str()); + + pxAssert(m_is_presenting); + vkCmdEndRenderPass(GetCurrentCommandBuffer()); + ExecuteCommandBuffer(wait_for_completion); + + GSTextureVK* swap_chain_texture = m_swap_chain->GetCurrentTexture(); + + const VkFramebuffer fb = swap_chain_texture->GetFramebuffer(false); + pxAssert(fb); + + const VkRenderPassBeginInfo rp = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, + GetRenderPass(swap_chain_texture->GetVkFormat(), VK_FORMAT_UNDEFINED, VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_STORE_OP_STORE), + fb, + {{0, 0}, {static_cast(swap_chain_texture->GetWidth()), static_cast(swap_chain_texture->GetHeight())}}, + 0u, nullptr}; + vkCmdBeginRenderPass(GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE); +} + void GSDeviceVK::ExecuteCommandBufferForReadback() { ExecuteCommandBuffer(true); diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h index d696d4addd..bde1d79d16 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.h @@ -367,6 +367,7 @@ public: private: std::unique_ptr m_swap_chain; + bool m_is_presenting = false; VkDescriptorSetLayout m_utility_ds_layout = VK_NULL_HANDLE; VkPipelineLayout m_utility_pipeline_layout = VK_NULL_HANDLE; @@ -513,6 +514,7 @@ public: PresentResult BeginPresent(bool frame_skip) override; void EndPresent() override; + bool IsPresenting() const; bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override; @@ -587,6 +589,7 @@ public: void ExecuteCommandBuffer(bool wait_for_completion); void ExecuteCommandBuffer(bool wait_for_completion, const char* reason, ...); void ExecuteCommandBufferAndRestartRenderPass(bool wait_for_completion, const char* reason); + void ExecuteCommandBufferAndRestartPresent(bool wait_for_completion, const char* reason, ...); void ExecuteCommandBufferForReadback(); /// Set dirty flags on everything to force re-bind at next draw time. diff --git a/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp index a0aa8bcca5..964fcc8054 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSTextureVK.cpp @@ -349,8 +349,18 @@ bool GSTextureVK::Update(const GSVector4i& r, const void* data, int pitch, int l VKStreamBuffer& sbuffer = GSDeviceVK::GetInstance()->GetTextureUploadBuffer(); if (!sbuffer.ReserveMemory(required_size, GSDeviceVK::GetInstance()->GetBufferCopyOffsetAlignment())) { - GSDeviceVK::GetInstance()->ExecuteCommandBuffer( - false, "While waiting for %u bytes in texture upload buffer", required_size); + GSDeviceVK* dev = GSDeviceVK::GetInstance(); + if (!dev->IsPresenting()) + { + dev->ExecuteCommandBuffer( + false, "While waiting for %u bytes in texture upload buffer", required_size); + } + else + { + dev->ExecuteCommandBufferAndRestartPresent( + false, "While waiting for %u bytes in texture upload buffer", required_size); + } + if (!sbuffer.ReserveMemory(required_size, GSDeviceVK::GetInstance()->GetBufferCopyOffsetAlignment())) { Console.Error("Failed to reserve texture upload memory (%u bytes).", required_size); @@ -410,8 +420,18 @@ bool GSTextureVK::Map(GSMap& m, const GSVector4i* r, int layer) if (!buffer.ReserveMemory(required_size, GSDeviceVK::GetInstance()->GetBufferCopyOffsetAlignment())) { - GSDeviceVK::GetInstance()->ExecuteCommandBuffer( - false, "While waiting for %u bytes in texture upload buffer", required_size); + GSDeviceVK* dev = GSDeviceVK::GetInstance(); + if (!dev->IsPresenting()) + { + dev->ExecuteCommandBuffer( + false, "While waiting for %u bytes in texture upload buffer", required_size); + } + else + { + dev->ExecuteCommandBufferAndRestartPresent( + false, "While waiting for %u bytes in texture upload buffer", required_size); + } + if (!buffer.ReserveMemory(required_size, GSDeviceVK::GetInstance()->GetBufferCopyOffsetAlignment())) pxFailRel("Failed to reserve texture upload memory"); }