aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/native_qmd.h
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 13:22:59 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-28 13:22:59 -0400
commitd0c0cb8906ba99bbe013aaa220887b7b8ab31a7a (patch)
tree6e6998122a908ae5739dbcb239e26570a8f82beb /src/video_core/native_qmd.h
parentd6bbbf9f23a7e5b6a6ab11e2696085e0482b6fc4 (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.h18
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