diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-28 13:22:59 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-10-28 13:22:59 -0400 |
| commit | d0c0cb8906ba99bbe013aaa220887b7b8ab31a7a (patch) | |
| tree | 6e6998122a908ae5739dbcb239e26570a8f82beb /src/video_core/native_qmd.h | |
| parent | d6bbbf9f23a7e5b6a6ab11e2696085e0482b6fc4 (diff) | |
Switch 2: per-channel MME data RAM; one QMD table for all channels
Macro code stays shared by the channels of a process, but each channel
now has its own MME data RAM. Title 1's channels keep their own QMD ring
base in data word 0x27; with one shared RAM every channel used the last
channel's ring, QMDs collided, releases came early or never and the GPU
waited on semaphores (4 s timeouts per frame).
The QMD scheduler state is one table for all channels: a QMD's
dependency counter is state of the QMD, scheduled and decremented from
different channels.
Diagnostics: EDEN_NX2_QMD_TRACE (scheduler loads, counts, launches) and
EDEN_NX2_MME_DUMP (compute macro code, calls, data word 0x27 changes).
Diffstat (limited to 'src/video_core/native_qmd.h')
| -rw-r--r-- | src/video_core/native_qmd.h | 18 |
1 files changed, 16 insertions, 2 deletions
diff --git a/src/video_core/native_qmd.h b/src/video_core/native_qmd.h index 41961c1aea..15c59cb7c6 100644 --- a/src/video_core/native_qmd.h +++ b/src/video_core/native_qmd.h @@ -3,6 +3,7 @@ #pragma once #include <array> #include <cstdint> +#include <functional> #include <string_view> #include <type_traits> #include <unordered_map> @@ -16,6 +17,8 @@ public: using Address = std::uint64_t; using Descriptor = std::array<Word, 64>; struct RejectShaderDispatch { bool operator()(const Descriptor&) const { return false; } }; + // Optional diagnostics: called with "load"/"count"/"defer"/"run" events. + std::function<void(std::string_view, Address, std::int32_t)> trace; template <typename Read, typename Release> std::string_view Signal(Address address, Word argument, Read read, Release release) { return Signal(address, argument, read, release, RejectShaderDispatch{}); @@ -41,6 +44,8 @@ public: return "native shader dispatch required"; } state.dependencies = std::int32_t((qmd[4] >> 14) & 0xffff) + state.deferred; + if (trace) trace("base", request.address, std::int32_t((qmd[4] >> 14) & 0xffff)); + if (trace) trace("load", request.address, state.dependencies); state.deferred = 0; state.loaded = true; state.completed = false; @@ -48,8 +53,13 @@ public: state.scheduled = true; } else if (request.action == 7 || request.action == 12) { const auto delta = request.action == 7 ? -std::int32_t(request.count) : std::int32_t(request.count); - if (!state.loaded || state.completed) { state.deferred += delta; continue; } + if (!state.loaded || state.completed) { + state.deferred += delta; + if (trace) trace("defer", request.address, state.deferred); + continue; + } state.dependencies += delta; + if (trace) trace("count", request.address, state.dependencies); } else if (request.action == 0 || request.action == 1) { continue; } else { @@ -78,6 +88,7 @@ public: // are what other queues and the CPU wait on (the console never fails a launch). const bool dispatched = !(qmd[12] && (qmd[13] & 0xffff) && (qmd[14] & 0xffff)) || dispatch(qmd); if (!dispatched) error = "native shader dispatch required"; + if (trace) trace("run", request.address, dispatched ? 1 : 0); state.completed = true; for (const Word base : ReleaseSlots) { const Word flags = qmd[base + 1]; @@ -114,6 +125,9 @@ private: bool scheduled{}; bool completed{}; }; - std::unordered_map<Address, State> states; + // One table for every channel: a QMD's dependency counter is state of the QMD itself, and + // title 1 schedules a QMD on one channel while another decrements it (a decrement that + // arrived first was kept by the other channel's scheduler and the QMD never launched). + static inline std::unordered_map<Address, State> states; }; } // namespace Tegra |
