diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/nx2_gsv.h | 65 | ||||
| -rw-r--r-- | src/video_core/control/scheduler.cpp | 2 | ||||
| -rw-r--r-- | src/video_core/control/scheduler.h | 6 | ||||
| -rw-r--r-- | src/video_core/gpu.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/gpu.h | 3 |
5 files changed, 58 insertions, 22 deletions
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h index bc354b4d8f..259681a2a2 100644 --- a/src/core/hle/service/nx2_gsv.h +++ b/src/core/hle/service/nx2_gsv.h @@ -1711,37 +1711,41 @@ private: LOG_INFO(Service, "nx2:vi2 229 present #{} token={:#x} slot={} image={}", m_presents, u64{w[2]} | (u64{w[3]} << 32), w[4], w[5]); } - // Wait for the previous frame's fence instead of this one, so the game prepares frame N+1 - // while the GPU thread runs frame N (the console keeps several frames in flight). Waiting - // for this frame serialized the two: 8-56 ms of every 34-105 ms frame on title 1. - // EDEN_NX2_FRAMES_AHEAD=0 restores the wait for the current frame. - static const bool frames_ahead = [] { + // Wait for this frame's fence, so the game doesn't write the next frame's data (constant + // buffers holding texture handles, vertex data, Pokopia's glyph atlas) while the GPU + // thread still reads this frame's. But title 1's GPU waits on semaphores its CPU releases + // only after this present returns: while a channel stays suspended on one, wait for the + // previous frame's fence instead and let the game prepare frame N+1 meanwhile (waiting + // for this frame there cost 8-56 ms of every 34-105 ms frame, or never finished). + // EDEN_NX2_FRAMES_AHEAD=1 always waits for the previous frame, =0 always for this one. + static const int frames_ahead = [] { const char* value = std::getenv("EDEN_NX2_FRAMES_AHEAD"); - return value == nullptr || value[0] != '0'; + return value == nullptr ? -1 : value[0] == '1' ? 1 : 0; }(); const auto entered = std::chrono::steady_clock::now(); - if (frames_ahead) { + if (frames_ahead == 1 || (frames_ahead == -1 && !WaitForFrame(~w[22], w[24], true))) { LOG_DEBUG(Service, "nx2:vi2 present #{} fence ({}, {:#x}), waiting for ({}, {:#x}); now {:#x}", m_presents, ~w[22], w[24], m_previous_fence_id, m_previous_fence_value, ~w[22] < 192 ? system.Host1x().GetSyncpointManager().GetGuestSyncpointValue(~w[22]) : 0U); - // Only run a frame ahead while the GPU keeps up. With the previous frame still on the - // GPU here, the GPU is the limit and running ahead gains nothing; and Splatoon Raiders, - // which reuses its command memory as if the GPU were at most a frame behind, rewrote - // command lists Eden had not read yet (misread commands, then crashes, with a slow - // GPU). Wait for this frame too then. - const bool gpu_behind = - m_previous_fence_id < 192 && - !system.Host1x().GetSyncpointManager().IsReadyGuest(m_previous_fence_id, - m_previous_fence_value); + // With EDEN_NX2_FRAMES_AHEAD=1, only run a frame ahead while the GPU keeps up. With + // the previous frame still on the GPU here, the GPU is the limit and running ahead + // gains nothing; and Splatoon Raiders, which reuses its command memory as if the GPU + // were at most a frame behind, rewrote command lists Eden had not read yet (misread + // commands, then crashes, with a slow GPU). Wait for this frame too then. Not when a + // suspended channel brought us here: this frame can't finish before the present + // returns. + const bool gpu_behind = frames_ahead == 1 && m_previous_fence_id < 192 && + !system.Host1x().GetSyncpointManager().IsReadyGuest( + m_previous_fence_id, m_previous_fence_value); WaitForFrame(m_previous_fence_id, m_previous_fence_value); if (gpu_behind) { WaitForFrame(~w[22], w[24]); } - m_previous_fence_id = ~w[22]; - m_previous_fence_value = w[24]; - } else { + } else if (frames_ahead == 0) { WaitForFrame(~w[22], w[24]); } + m_previous_fence_id = ~w[22]; + m_previous_fence_value = w[24]; const auto waited = std::chrono::steady_clock::now(); Composite(w[5], ~w[22], w[24], w[7]); const auto composited = std::chrono::steady_clock::now(); @@ -1838,9 +1842,12 @@ private: m_next_vblank += interval; } - void WaitForFrame(u32 fence_id, u32 fence_value) { + /// Waits until the fence is reached. With `unless_gpu_waits`, gives up after a few ms once a + /// GPU channel was seen suspended on a semaphore (it may be one this thread releases later). + /// Returns whether the fence was reached. + bool WaitForFrame(u32 fence_id, u32 fence_value, bool unless_gpu_waits = false) { if (fence_id >= 192) { - return; + return true; } auto& syncpoints = system.Host1x().GetSyncpointManager(); const auto start = std::chrono::steady_clock::now(); @@ -1865,11 +1872,24 @@ private: wake->cv.notify_all(); }); std::unique_lock lk{wake->mutex}; + // Title 1's channels resume and suspend again while they wait on this thread, so any + // suspension counts, not only an unbroken one. + bool gpu_waited = false; while (!wake->done && system.IsPoweredOn() && std::chrono::steady_clock::now() < deadline) { - wake->cv.wait_for(lk, std::chrono::milliseconds{50}); + if (unless_gpu_waits) { + gpu_waited |= system.GPU().HasSuspendedChannels(); + if (gpu_waited && + std::chrono::steady_clock::now() - start > std::chrono::milliseconds{3}) { + return false; + } + } + wake->cv.wait_for(lk, std::chrono::milliseconds{unless_gpu_waits ? 1 : 50}); } } + if (unless_gpu_waits && !syncpoints.IsReadyGuest(fence_id, fence_value)) { + return false; + } if (!syncpoints.IsReadyGuest(fence_id, fence_value) && m_wait_timeouts++ < 64) { LOG_WARNING(Service, "nx2:vi2 present #{} fence ({}, {}) not reached in 4 s (now {})", m_presents, fence_id, fence_value, syncpoints.GetGuestSyncpointValue(fence_id)); @@ -1881,6 +1901,7 @@ private: std::chrono::steady_clock::now() - start) .count()); } + return syncpoints.IsReadyGuest(fence_id, fence_value); } // Shows a presented native image: word 5 is the image id, ~word 22 / word 24 the GPU fence diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp index dc42906752..97d2dcd482 100644 --- a/src/video_core/control/scheduler.cpp +++ b/src/video_core/control/scheduler.cpp @@ -48,6 +48,7 @@ void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) { std::find(pending_channels.begin(), pending_channels.end(), channel_state) == pending_channels.end()) { pending_channels.push_back(channel_state); + any_pending.store(true, std::memory_order_release); } if (!pending_channels.empty()) { ResumePending(gpu); @@ -77,6 +78,7 @@ void Scheduler::ResumePending(GPU& gpu) { std::erase_if(pending_channels, [](const auto& state) { return !state->payload->dma_pusher.HasPendingAcquire(); }); + any_pending.store(!pending_channels.empty(), std::memory_order_release); } void Scheduler::DeclareChannel(std::shared_ptr<ChannelState> new_channel) { diff --git a/src/video_core/control/scheduler.h b/src/video_core/control/scheduler.h index 9a0f1b919f..b1aaf46b60 100644 --- a/src/video_core/control/scheduler.h +++ b/src/video_core/control/scheduler.h @@ -6,6 +6,7 @@ #pragma once +#include <atomic> #include <memory> #include <mutex> #include <vector> @@ -30,6 +31,10 @@ public: [[nodiscard]] bool HasPendingChannels() const { return !pending_channels.empty(); } + /// HasPendingChannels for threads other than the GPU thread. + [[nodiscard]] bool HasPendingChannelsAnyThread() const { + return any_pending.load(std::memory_order_acquire); + } void DeclareChannel(std::shared_ptr<ChannelState> new_channel); @@ -39,6 +44,7 @@ private: ::Common::unordered_map<s32, std::shared_ptr<ChannelState>> channels; std::mutex scheduling_guard; std::vector<std::shared_ptr<ChannelState>> pending_channels; ///< Suspended on an acquire + std::atomic<bool> any_pending{}; }; } // namespace Control diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 53f8f063f7..a6f31359a3 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -505,6 +505,10 @@ bool GPU::IsAsync() const { return impl->is_async; } +bool GPU::HasSuspendedChannels() const { + return impl->scheduler.HasPendingChannelsAnyThread(); +} + bool GPU::UseNvdec() const { return impl->use_nvdec; } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 3a6546b41b..4afc89f0e6 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -217,6 +217,9 @@ public: [[nodiscard]] bool IsAsync() const; + /// Whether a channel is suspended on a semaphore acquire (callable from any thread). + [[nodiscard]] bool HasSuspendedChannels() const; + [[nodiscard]] bool UseNvdec() const; void RendererFrameEndNotify(); |
