aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/control/scheduler.cpp
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 11:16:43 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 20:49:48 -0400
commit7a251d3a92f870649dab6068007ca3a1c910bbb5 (patch)
tree556633cb13fc4dc2f181f33989a3395919886b78 /src/video_core/control/scheduler.cpp
parent00c059cd39f4fd9d1244345d6727a171acec4f86 (diff)
video_core: split the GPU thread in two (EDEN_NX2_SPLIT, off by default)
The GPU thread (front) takes the CPU's commands, parses command lists and runs the puller; engine method calls and everything behind them (registers, draws, dispatches, the rasterizer and its caches) run in order on a second thread (back), fed through a single-producer single-consumer ring (gpu_split.h). Work the front did on back-owned state (end of command list flushes, channel binds, semaphore releases, syncpoint increments, flush/invalidate requests, presentation) is deferred into the same ring; the front waits for the back only where it needs a result from that state: semaphore acquires and MME DMA reads. EDEN_NX2_SPLIT=1 runs macros on the back. EDEN_NX2_SPLIT=2 runs the 3D and compute macros on the front against copies of the registers they read (Maxwell3D/KeplerCompute Front*), drops state writes that keep a register's value there, and sends the rest. EDEN_NX2_SPLIT_VERIFY=1 compares the copies with the back's registers every 4096 macro calls (0 differences in 40,193 checks in title 1); EDEN_NX2_SPLIT_CHECK=1 logs the stack of any other thread using the Vulkan scheduler while the back runs. Switch 2 titles, Vulkan, non-accurate GPU only. Title 1, 3D attract loop: ~44,000 draws a second on one thread, ~47,400 with EDEN_NX2_SPLIT=1, 50,000-66,500 with EDEN_NX2_SPLIT=2.
Diffstat (limited to 'src/video_core/control/scheduler.cpp')
-rw-r--r--src/video_core/control/scheduler.cpp22
1 files changed, 17 insertions, 5 deletions
diff --git a/src/video_core/control/scheduler.cpp b/src/video_core/control/scheduler.cpp
index 24f1db1f7c..c120533c37 100644
--- a/src/video_core/control/scheduler.cpp
+++ b/src/video_core/control/scheduler.cpp
@@ -12,9 +12,24 @@
#include "video_core/control/channel_state.h"
#include "video_core/control/scheduler.h"
#include "video_core/gpu.h"
+#include "video_core/gpu_split.h"
namespace Tegra::Control {
+void Scheduler::BindChannel(GPU& gpu, s32 bind_id) {
+ // With the GPU thread split the rasterizer's channel switches with the methods around it, on
+ // the back thread (deferred outside the lock: the back takes it to bind).
+ if (auto& split = VideoCommon::GpuSplit::Get(); split.Active()) {
+ split.Defer([this, gpu_ptr = &gpu, bind_id] {
+ std::scoped_lock lk{scheduling_guard};
+ gpu_ptr->BindChannel(bind_id);
+ });
+ return;
+ }
+ std::scoped_lock lk{scheduling_guard};
+ gpu.BindChannel(bind_id);
+}
+
void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) {
std::shared_ptr<ChannelState> channel_state;
{
@@ -22,8 +37,8 @@ void Scheduler::Push(GPU& gpu, s32 channel, CommandList&& entries) {
auto it = channels.find(channel);
ASSERT(it != channels.end());
channel_state = it->second;
- gpu.BindChannel(channel_state->bind_id);
}
+ BindChannel(gpu, channel_state->bind_id);
// Process commands outside the lock to reduce contention.
// Multiple channels can prepare their commands in parallel.
channel_state->payload->dma_pusher.Push(std::move(entries));
@@ -47,10 +62,7 @@ void Scheduler::ResumePending(GPU& gpu) {
if (!dma_pusher.HasPendingAcquire() || !dma_pusher.CanResumeAcquire()) {
continue;
}
- {
- std::scoped_lock lk{scheduling_guard};
- gpu.BindChannel(state->bind_id);
- }
+ BindChannel(gpu, state->bind_id);
dma_pusher.DispatchCalls();
progress = true;
}