diff options
| author | Alex <78863999+Sssnowyyy@users.noreply.github.com> | 2026-11-03 01:29:23 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-11-03 07:29:23 +0100 |
| commit | f50099038ee4c95901821e137471a8ce87bf52a4 (patch) | |
| tree | 25dc841aa63c9958687a7d28fa84bd7d226598f9 /src | |
| parent | ad7282cd736ae75b126088d7d6986efebd048c15 (diff) | |
nx2: fewer render pass splits on tiled GPUs, log failed GPU waits (Pokopia) (#361)
* buffer_cache: don't end the render pass after mapped buffer uploads
Every upload set any_buffer_uploaded, and the next draw or dispatch then
recorded a transfer-to-shader barrier outside the render pass. On Vulkan
the uploads are mapped: each copy either records its own barriers
(CopyBuffer with barrier set) or goes to the upload command buffer,
which ends with a barrier before the frame's commands run. The extra
barrier only ended the render pass.
On tiled GPUs every such split writes the attachments out and reads
them back. Pokopia's first gameplay scene had ~50 of these splits a
frame. Without them, an M3 MacBook Air (MoltenVK, 1x docked) goes from
~35 to ~29.5 ms of GPU time a frame and from 28.5 to 32 fps.
OpenGL uploads immediately and keeps the barrier.
Assisted-by: Claude Opus 5.5 (profiling, implementation and commit message)
* vulkan: log a failed GPU wait before it ends the process
When the device is lost, vkWaitSemaphores in MasterSemaphore::Wait
returns VK_ERROR_DEVICE_LOST and the exception goes uncaught on the
fence thread or the GPU thread, so the process aborts without a line in
the log: ReportLoss is only reached from submit and present. A Pokopia
crash on macOS left the log ending a second before the abort, with no
hint that the GPU was lost.
Log the wait's result before rethrowing. On macOS, MoltenVK prints the
Metal reason (a GPU hang or page fault) to stderr only.
Assisted-by: Claude Opus 5.5 (diagnosis, implementation and commit message)
Diffstat (limited to 'src')
| -rw-r--r-- | src/video_core/buffer_cache/buffer_cache.h | 7 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_master_semaphore.cpp | 8 |
2 files changed, 13 insertions, 2 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 40369b16ef..2e0f0d4623 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -2159,7 +2159,12 @@ bool BufferCache<P>::SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 si } const std::span<BufferCopy> copies_span(upload_copies.data(), upload_copies.size()); UploadMemory(buffer, total_size_bytes, largest_copy, copies_span); - any_buffer_uploaded = true; + // Mapped uploads carry their own barriers (or run in the upload command buffer, which + // ends with one); another barrier here only ended the render pass (~50 times a frame in + // Pokopia). + if constexpr (!USE_MEMORY_MAPS_FOR_UPLOADS) { + any_buffer_uploaded = true; + } return false; } else { boost::container::small_vector<BufferCopy, 4> copies; diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp index be5a1144ce..ba364d99a0 100644 --- a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp +++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp @@ -104,7 +104,13 @@ void MasterSemaphore::Wait(u64 tick) { } // If none of the above is hit, fallback to a regular wait - while (!semaphore.Wait(tick)) { + try { + while (!semaphore.Wait(tick)) { + } + } catch (const vk::Exception& exception) { + // The throw ends the process; say why first (a device loss reached nothing else here). + LOG_CRITICAL(Render_Vulkan, "Waiting for GPU tick {} failed: {}", tick, exception.what()); + throw; } Refresh(); |
