From 1c7c24683fd51c6135137c4ff2ee2d742c5fff36 Mon Sep 17 00:00:00 2001 From: Takoroka <137927095+Takoroka@users.noreply.github.com> Date: Thu, 29 Oct 2026 07:09:36 -0400 Subject: vulkan: defer the caches' invalidation for inline writes outside GPU-written data Title 1 makes thousands of small inline-to-memory writes between draws, and each one invalidated the buffer, texture, shader and query caches on its own (~7.5% of the GPU thread after 0fe581f). Guest memory is still written at once, query results pending there are still dropped at once, and writes into GPU-written buffer data are still uploaded in place; the buffer, texture and shader caches now get the merged ranges at the next FlushCaching, which runs before every draw, dispatch, clear, copy and 2D blit. Inline writes: 4.4% of the GPU thread (deferred invalidation included in FlushCaching, 3.9% -> 3.1%). EDEN_NX2_INLINE_DEFER=0 invalidates at once. --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'src/video_core/renderer_vulkan/vk_rasterizer.cpp') diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index b5837e6a55..0826820714 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1250,20 +1250,37 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si return; } gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size); + // EDEN_NX2_INLINE_DEFER=0: invalidate every cache at once (for comparisons). + static const bool defer = [] { + const char* value = std::getenv("EDEN_NX2_INLINE_DEFER"); + return value == nullptr || value[0] != '0'; + }(); // One cache at a time: holding both while waiting for either (std::lock) waited longer, the // fencing thread and CPU write traps holding one of them most of the time. + bool uploaded; { std::scoped_lock lock{buffer_cache.mutex}; - if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) { + uploaded = buffer_cache.InlineMemory(*cpu_addr, copy_size, memory); + if (!uploaded && !defer) { buffer_cache.WriteMemory(*cpu_addr, copy_size); } } + // Pending query results must not land over the new words later: drop them now. + query_cache.InvalidateRegion(*cpu_addr, copy_size); + if (!uploaded && defer) { + // Title 1 makes thousands of small inline writes between draws (~10% of the GPU + // thread invalidating every cache for each). Guest memory is written; the buffer, + // texture and shader caches see the merged ranges at the next FlushCaching, which runs + // before anything of theirs is used. Writes into GPU-written buffer data were uploaded + // in place above, as before. + gpu_memory->AccumulateInvalidation(address, copy_size); + return; + } { std::scoped_lock lock{texture_cache.mutex}; texture_cache.WriteMemory(*cpu_addr, copy_size); } pipeline_cache.InvalidateRegion(*cpu_addr, copy_size); - query_cache.InvalidateRegion(*cpu_addr, copy_size); } std::optional RasterizerVulkan::AccelerateDisplay( -- cgit v1.2.3