From 3310f3b8b184d64a62b89fd59088c799432badf5 Mon Sep 17 00:00:00 2001 From: Exzap <13877693+Exzap@users.noreply.github.com> Date: Thu, 10 Sep 2026 02:35:50 +0200 Subject: [PATCH] Vulkan: Experimental barrier skip when the game does not request it --- src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp | 4 ++++ src/Cafe/HW/Latte/ISA/LatteReg.h | 4 +++- src/Cafe/HW/Latte/Renderer/Renderer.h | 1 + src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h | 2 ++ .../HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp | 13 +++++++++++++ src/Cafe/OS/libs/gx2/GX2_Misc.cpp | 6 +----- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp index 462b5b87..5a4f1ce5 100644 --- a/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp +++ b/src/Cafe/HW/Latte/Core/LatteCommandProcessor.cpp @@ -214,6 +214,10 @@ LatteCMDPtr LatteCP_itSurfaceSync(LatteCMDPtr cmd) MPTR addressPhys = LatteReadCMD() << 8; uint32 pollInterval = LatteReadCMD(); + // let the renderer know about colorbuffer invalidation + if (static_cast(invalidationFlags & (Latte::E_COHER_CNTL::CB_ACTION_ENA | Latte::E_COHER_CNTL::CB_ALL_DEST_BASE_ENA)) != 0) + g_renderer->SurfaceSync(invalidationFlags, addressPhys, size); + if (addressPhys == MPTR_NULL || size == 0xFFFFFFFF) return cmd; // block global invalidations because they are too expensive diff --git a/src/Cafe/HW/Latte/ISA/LatteReg.h b/src/Cafe/HW/Latte/ISA/LatteReg.h index 5826fd17..2d7be790 100644 --- a/src/Cafe/HW/Latte/ISA/LatteReg.h +++ b/src/Cafe/HW/Latte/ISA/LatteReg.h @@ -359,7 +359,7 @@ namespace Latte SO1_DEST_BASE_ENA = (1<<3), SO2_DEST_BASE_ENA = (1<<4), SO3_DEST_BASE_ENA = (1<<5), - CB0_DEST_BASE_ENA = (1<<6), + CB0_DEST_BASE_ENA = (1<<6), // flush color buffer 0 CB1_DEST_BASE_ENA = (1<<7), CB2_DEST_BASE_ENA = (1<<8), CB3_DEST_BASE_ENA = (1<<9), @@ -367,6 +367,8 @@ namespace Latte CB5_DEST_BASE_ENA = (1<<11), CB6_DEST_BASE_ENA = (1<<12), CB7_DEST_BASE_ENA = (1<<13), + CB_ALL_DEST_BASE_ENA = (0xFF<<6), // CB0-CB7 combined + DB_DEST_BASE_ENA = (1<<14), FULL_CACHE_ENA = (1<<20), diff --git a/src/Cafe/HW/Latte/Renderer/Renderer.h b/src/Cafe/HW/Latte/Renderer/Renderer.h index 87b5f7ea..bc9e6360 100644 --- a/src/Cafe/HW/Latte/Renderer/Renderer.h +++ b/src/Cafe/HW/Latte/Renderer/Renderer.h @@ -82,6 +82,7 @@ public: // flush control virtual void Flush(bool waitIdle = false) = 0; // called when explicit flush is required (e.g. by imgui) virtual void NotifyLatteCommandProcessorIdle() = 0; // called when command processor has no more commands available or when stalled + virtual void SurfaceSync(Latte::E_COHER_CNTL coher, MPTR address, uint32 size) {} // triggered by game via GX2Invalidate, can be utilized by the render backend as an optimization hint // imgui virtual bool ImguiBegin(bool mainWindow); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index b3e0625f..3b78d3c3 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -240,6 +240,7 @@ public: void Flush(bool waitIdle = false) override; void NotifyLatteCommandProcessorIdle() override; + void SurfaceSync(Latte::E_COHER_CNTL coher, MPTR address, uint32 size) override; uint64 GenUniqueId(); // return unique id (uses incrementing counter) @@ -419,6 +420,7 @@ private: // invalidation / flushing uint64 currentFlushIndex{0}; + bool colorBufferSyncPending{false}; // guest color-buffer sync since the previous draw; survives command-buffer resets bool requestFlush{ false }; // flush after every draw operation. The renderpass dependencies dont handle dependencies across multiple drawcalls inside a single renderpass // draw sequence diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp index 0870b863..8ea16a8e 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRendererCore.cpp @@ -995,6 +995,12 @@ VkDescriptorSetInfo* VulkanRenderer::draw_getOrCreateDescriptorSet(PipelineInfo* return dsInfo; } +void VulkanRenderer::SurfaceSync(Latte::E_COHER_CNTL coher, MPTR address, uint32 size) +{ + if (static_cast(coher & Latte::E_COHER_CNTL::CB_ALL_DEST_BASE_ENA) != 0) + m_state.colorBufferSyncPending = true; +} + void VulkanRenderer::sync_inputTexturesChanged(bool withinFeedbackLoopRenderPass) { bool writeFlushRequired = withinFeedbackLoopRenderPass; // feedback loop still requires us to emit a barrier @@ -1029,6 +1035,12 @@ void VulkanRenderer::sync_inputTexturesChanged(bool withinFeedbackLoopRenderPass // barrier here if (writeFlushRequired) { + // Continued draws with unchanged descriptors in the same renderpass only introduce feedback hazards. + // Relax color feedback without a guest sync, but keep the read indices above updated for later passes. + if (withinFeedbackLoopRenderPass && !m_state.descriptorSetsChanged && !m_state.colorBufferSyncPending + && m_state.m_curRenderpassSelfDependencyInfo.GetAspectMask() == VK_IMAGE_ASPECT_COLOR_BIT) + return; + VkMemoryBarrier memoryBarrier{}; memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; memoryBarrier.srcAccessMask = 0; @@ -1740,6 +1752,7 @@ void VulkanRenderer::draw_execute(uint32 baseVertex, uint32 baseInstance, uint32 draw_execute_first(baseVertex, baseInstance, instanceCount, count, indexDataMPTR, indexType, drawcallContext); else draw_execute_continued(baseVertex, baseInstance, instanceCount, count, indexDataMPTR, indexType, drawcallContext); + m_state.colorBufferSyncPending = false; LatteGPUState.drawCallCounter++; } diff --git a/src/Cafe/OS/libs/gx2/GX2_Misc.cpp b/src/Cafe/OS/libs/gx2/GX2_Misc.cpp index 78202693..83209514 100644 --- a/src/Cafe/OS/libs/gx2/GX2_Misc.cpp +++ b/src/Cafe/OS/libs/gx2/GX2_Misc.cpp @@ -286,11 +286,7 @@ namespace GX2 if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_COLOR_BUFFER)) { - coherCntl |= Latte::E_COHER_CNTL::CB_ACTION_ENA - | Latte::E_COHER_CNTL::CB0_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB1_DEST_BASE_ENA - | Latte::E_COHER_CNTL::CB2_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB3_DEST_BASE_ENA - | Latte::E_COHER_CNTL::CB4_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB5_DEST_BASE_ENA - | Latte::E_COHER_CNTL::CB6_DEST_BASE_ENA | Latte::E_COHER_CNTL::CB7_DEST_BASE_ENA; + coherCntl |= Latte::E_COHER_CNTL::CB_ACTION_ENA | Latte::E_COHER_CNTL::CB_ALL_DEST_BASE_ENA; } if (HAS_FLAG(invalidationFlags, GX2InvalidationFlag::GPU_DEPTH_BUFFER))