diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-27 20:53:15 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-27 20:53:15 -0400 |
| commit | bf1de8f40130af5d3b490ee40dcc2fb20d460a14 (patch) | |
| tree | f57762bcc67491aa61288bd2eec107348756fb79 /src/video_core/control | |
| parent | 3f5bee600839a7593e98e0cb1231f09c29373ac8 (diff) | |
Switch 2: suspend channels on semaphore acquires; fix pushbuffer desyncs
NVN2 gates command buffers with semaphores the CPU releases after writing
them. Eden read past unmet SEMAPHORED acquires, so it executed stale commands
and desynchronised. Channels now suspend on an unmet acquire and resume from
the same word once it is met (scheduler keeps the waiting channels, the GPU
thread polls them), as in changes.patch.
Also: methods split across GPFIFO entries around a called command buffer are
set aside and resumed; subchannel 2 defaults to inline-to-memory; unbound
subchannels and out-of-range compute/DMA methods are dropped instead of
crashing; 4-byte semaphore releases no longer write 16 bytes; Ampere
SEM_PAYLOAD_HI/SEM_EXECUTE no longer spin as Kepler acquires; unmapped GPU
reads log instead of asserting. Debug aids: EDEN_NX2_PB_CHECK (pushbuffer
changed after submit), EDEN_NX2_QUERY_TRACE, a one-time pushbuffer dump.
Diffstat (limited to 'src/video_core/control')
| -rw-r--r-- | src/video_core/control/channel_state.cpp | 6 | ||||
| -rw-r--r-- | src/video_core/control/scheduler.cpp | 32 | ||||
| -rw-r--r-- | src/video_core/control/scheduler.h | 8 |
3 files changed, 45 insertions, 1 deletions
diff --git a/src/video_core/control/channel_state.cpp b/src/video_core/control/channel_state.cpp index 12a452dfc0..a1a8110510 100644 --- a/src/video_core/control/channel_state.cpp +++ b/src/video_core/control/channel_state.cpp @@ -21,6 +21,7 @@ namespace { // Match NVK/Nouveau's initial pushbuffer subchannel layout. constexpr u32 Nvk3DSubchannel = 0; constexpr u32 NvkComputeSubchannel = 1; +constexpr u32 NvkM2mfSubchannel = 2; constexpr u32 Nvk2DSubchannel = 3; constexpr u32 NvkCopySubchannel = 4; @@ -29,7 +30,10 @@ void BindNvkDefaultSubchannels(ChannelState::Payload& payload) { dma_pusher.BindSubchannel(&payload.maxwell_3d, Nvk3DSubchannel, Engines::EngineTypes::Maxwell3D); dma_pusher.BindSubchannel(&payload.kepler_compute, NvkComputeSubchannel, Engines::EngineTypes::KeplerCompute); - // Subchannel 2 is M2MF there; Eden does not expose a 0x9039 engine yet. + // Subchannel 2 is M2MF/inline-to-memory there (NVN uses it without binding a class); + // KeplerMemory has the same LINE_LENGTH_IN/OFFSET_OUT/LAUNCH_DMA/LOAD_INLINE_DATA layout. + dma_pusher.BindSubchannel(&payload.kepler_memory, NvkM2mfSubchannel, + Engines::EngineTypes::KeplerMemory); dma_pusher.BindSubchannel(&payload.fermi_2d, Nvk2DSubchannel, Engines::EngineTypes::Fermi2D); dma_pusher.BindSubchannel(&payload.maxwell_dma, NvkCopySubchannel, Engines::EngineTypes::MaxwellDMA); diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp index 8af13e415c..24f1db1f7c 100644 --- a/src/video_core/control/scheduler.cpp +++ b/src/video_core/control/scheduler.cpp @@ -4,7 +4,9 @@ // SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include <algorithm> #include <memory> +#include <vector> #include "common/assert.h" #include "video_core/control/channel_state.h" @@ -26,6 +28,36 @@ void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) { // Multiple channels can prepare their commands in parallel. channel_state->payload->dma_pusher.Push(std::move(entries)); channel_state->payload->dma_pusher.DispatchCalls(); + if (channel_state->payload->dma_pusher.HasPendingAcquire() && + std::find(pending_channels.begin(), pending_channels.end(), channel_state) == + pending_channels.end()) { + pending_channels.push_back(channel_state); + } + if (!pending_channels.empty()) { + ResumePending(gpu); + } +} + +void Scheduler::ResumePending(GPU& gpu) { + // Running one channel can release the semaphore another waits for: retry until none moves. + for (bool progress = true; progress;) { + progress = false; + for (const auto& state : pending_channels) { + auto& dma_pusher = state->payload->dma_pusher; + if (!dma_pusher.HasPendingAcquire() || !dma_pusher.CanResumeAcquire()) { + continue; + } + { + std::scoped_lock lk{scheduling_guard}; + gpu.BindChannel(state->bind_id); + } + dma_pusher.DispatchCalls(); + progress = true; + } + } + std::erase_if(pending_channels, [](const auto& state) { + return !state->payload->dma_pusher.HasPendingAcquire(); + }); } 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 342bea5025..3a3dcc58cf 100644 --- a/src/video_core/control/scheduler.h +++ b/src/video_core/control/scheduler.h @@ -8,6 +8,7 @@ #include <memory> #include <mutex> +#include <vector> #include "common/container/unordered_map.h" #include "video_core/dma_pusher.h" @@ -24,11 +25,18 @@ class Scheduler { public: void Push(GPU& gpu, s32 channel, CommandList&& entries); + /// Runs channels whose pending semaphore acquire is now met. + void ResumePending(GPU& gpu); + [[nodiscard]] bool HasPendingChannels() const { + return !pending_channels.empty(); + } + void DeclareChannel(std::shared_ptr<ChannelState> new_channel); 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 }; } // namespace Control |
