diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-02 11:24:19 -0500 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-02 11:24:19 -0500 |
| commit | 8f1435f8db104351fa3388551eee9d12d2043b40 (patch) | |
| tree | 1cbaa0434f77ae07ba04ed41504ba48cbdd246a2 /src/video_core/renderer_vulkan | |
| parent | 75bdb925dfb77db2370cb027eda81352c9622bbb (diff) | |
nx2: read GPU-written command lists from copies instead of draining
The trees fix (#52) drains the split and flushes for every command list
on a GPU-written page: in a Mario Kart World race ~13,500 lists and ~400
GPU round trips a second, during which the back has nothing to do. The
replay ran at ~40 fps instead of ~45.
The front now marks the pages those lists are on. The buffer cache notes
writes to them; after a compute dispatch or DMA copy that writes one it
copies the page into host memory and submits. The front waits for the
back only up to the last compute or copy work it sent (not everything
queued), then reads the copy once the GPU is done with it.
Pages the GPU wrote once, long before, and the CPU now writes lists to
have no copy: their first list drains and flushes the whole page, which
is then read from guest memory until GPU work writes it again. CPU
writes and inline uploads reach guest memory, so they keep such a page
synced but make a copy stale. A page a draw writes always drains.
Mario Kart World time-trial replay, Radeon 780M: ~40 -> ~45 fps, trees
drawn, no list drains left.
Diffstat (limited to 'src/video_core/renderer_vulkan')
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_buffer_cache.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_buffer_cache.h | 3 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.cpp | 40 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.h | 6 |
4 files changed, 52 insertions, 1 deletions
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index fe0ab3a28e..2449265509 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -453,6 +453,10 @@ void BufferCacheRuntime::Wait(u64 tick) { scheduler.Wait(tick); } +void BufferCacheRuntime::WaitSubmittedTick(u64 tick) { + scheduler.GetMasterSemaphore().Wait(tick); +} + void BufferCacheRuntime::Finish() { scheduler.Finish(); } diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h index ff854445b5..d3295ec6ed 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.h +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h @@ -122,6 +122,9 @@ public: void Wait(u64 tick); + /// Waits for a submitted tick without flushing: safe on threads other than the GPU thread. + void WaitSubmittedTick(u64 tick); + void Finish(); u64 GetDeviceLocalMemory() const; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d3dbba6163..978f8be547 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -922,6 +922,11 @@ void RasterizerVulkan::DispatchCompute() { return; } std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex}; + // Command list pages the dispatch writes are copied for the split's front right after it. + buffer_cache.Nx2CollectListCopies(true); + SCOPE_EXIT { + buffer_cache.Nx2CollectListCopies(false); + }; if (!pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache)) { if (kepler_compute->IsNativeMode()) throw Shader::RuntimeError("Native compute resources unavailable"); @@ -944,6 +949,7 @@ void RasterizerVulkan::DispatchCompute() { } cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset); }); + SubmitNx2ListCopies(); return; } const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; @@ -966,6 +972,7 @@ void RasterizerVulkan::DispatchCompute() { } cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); + SubmitNx2ListCopies(); // Log compute dispatch if (GPU::Logging::IsActive() && @@ -1007,6 +1014,29 @@ void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 in void RasterizerVulkan::FlushAll() {} +void RasterizerVulkan::SubmitNx2ListCopies() { + if (buffer_cache.Nx2CopyListPages(scheduler.CurrentTick())) { + scheduler.Flush(); + } +} + +VideoCore::RasterizerInterface::GpuWrittenList RasterizerVulkan::ReadGpuWrittenCommandList( + DAddr addr, std::span<u8> out) { + // No cache lock: the front only reads the copies, under their own lock. + switch (buffer_cache.Nx2ReadListCopy(addr, out)) { + case BufferCache::Nx2ListRead::Copied: + return GpuWrittenList::Copied; + case BufferCache::Nx2ListRead::Synced: + return GpuWrittenList::Synced; + default: + return GpuWrittenList::Unknown; + } +} + +void RasterizerVulkan::NoteGpuWrittenCommandListSynced(DAddr page) { + buffer_cache.Nx2NoteListSynced(page); +} + void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType which) { if (addr == 0 || size == 0) { return; @@ -1319,6 +1349,8 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si bool uploaded; { std::scoped_lock lock{buffer_cache.mutex}; + // Guest memory has the words now: a command list copy of the page no longer does. + buffer_cache.Nx2NoteListHostWrite(*cpu_addr, copy_size); uploaded = buffer_cache.InlineMemory(*cpu_addr, copy_size, memory); if (!uploaded && !defer) { buffer_cache.WriteMemory(*cpu_addr, copy_size); @@ -1414,7 +1446,13 @@ bool AccelerateDMA::BufferClear(GPUVAddr src_address, u64 amount, u32 value) { bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) { std::scoped_lock lock{buffer_cache.mutex}; - return buffer_cache.DMACopy(src_address, dest_address, amount); + buffer_cache.Nx2CollectListCopies(true); + const bool copied = buffer_cache.DMACopy(src_address, dest_address, amount); + if (buffer_cache.Nx2CopyListPages(scheduler.CurrentTick())) { + scheduler.Flush(); + } + buffer_cache.Nx2CollectListCopies(false); + return copied; } template <bool IS_IMAGE_UPLOAD> diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index fc282fbe8d..dbb6ebdca8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -99,6 +99,8 @@ public: void FlushAll() override; void FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; + GpuWrittenList ReadGpuWrittenCommandList(DAddr addr, std::span<u8> out) override; + void NoteGpuWrittenCommandListSynced(DAddr page) override; bool MustFlushRegion(DAddr addr, u64 size, VideoCommon::CacheType which = VideoCommon::CacheType::All) override; VideoCore::RasterizerDownloadArea GetFlushArea(DAddr addr, u64 size) override; @@ -165,6 +167,10 @@ private: void FlushWork(); + /// Records the copies of the command list pages the work just recorded wrote and submits, so + /// the GPU makes them now (BufferCache::Nx2CopyListPages). + void SubmitNx2ListCopies(); + void UpdateDynamicStates(); void HandleTransformFeedback(); |
