aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/nx2.h
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 15:14:37 -0500
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-11-01 15:14:37 -0500
commit91b7400a27a89f1ff9bef7f148913aa76f0a39ad (patch)
tree1d2ff177cb93ed6af25ad011bf3b06c0bb9a89c2 /src/video_core/nx2.h
parent76812d0bd86c3dfbef473ad6236a68045ad3d4b5 (diff)
parentbef001c1515a13a9b8e361a5a7d372fc2e01094c (diff)
Merge remote-tracking branch 'origin/fix/nx2-gpu-written-command-lists' into test/linux-all-prs-plus
# Conflicts: # src/core/hle/service/nvdrv/nvdrv.cpp
Diffstat (limited to 'src/video_core/nx2.h')
-rw-r--r--src/video_core/nx2.h38
1 files changed, 38 insertions, 0 deletions
diff --git a/src/video_core/nx2.h b/src/video_core/nx2.h
index 318607384b..1fb49e15a6 100644
--- a/src/video_core/nx2.h
+++ b/src/video_core/nx2.h
@@ -20,6 +20,44 @@ namespace Tegra {
// Ampere classes and the Turing+ macro ISA, which Eden's Maxwell engines don't implement.
inline std::atomic<bool> g_nx2_ampere{false};
+// Device pages the GPU has written through the buffer cache (shader stores, copies), ever. Title 1
+// runs command lists the GPU writes each frame (20-byte entries its culling fills in); read
+// without a flush they are stale and the draws behind them (trees) go missing. The bits never
+// clear: the dispatch that rewrites a page may still be queued on the split's back when its front
+// reads the command list. One bit per 4 KiB of the 36-bit device address space (2 MiB).
+inline constexpr u64 Nx2WrittenPageBits = 12;
+inline std::array<std::atomic<u64>, (u64{1} << (36 - Nx2WrittenPageBits)) / 64> g_nx2_gpu_written_pages{};
+
+inline void Nx2MarkGpuWritten(u64 device_address, u64 size) {
+ if (size == 0) {
+ return;
+ }
+ const u64 last = std::min<u64>((device_address + size - 1) >> Nx2WrittenPageBits,
+ g_nx2_gpu_written_pages.size() * 64 - 1);
+ for (u64 page = device_address >> Nx2WrittenPageBits; page <= last; ++page) {
+ auto& word = g_nx2_gpu_written_pages[page / 64];
+ const u64 bit = u64{1} << (page % 64);
+ if ((word.load(std::memory_order_relaxed) & bit) == 0) {
+ word.fetch_or(bit, std::memory_order_relaxed);
+ }
+ }
+}
+
+/// A new emulation session starts: no page has been written by its GPU yet.
+inline void Nx2ClearGpuWrittenPages() {
+ for (auto& word : g_nx2_gpu_written_pages) {
+ word.store(0, std::memory_order_relaxed);
+ }
+}
+
+[[nodiscard]] inline bool Nx2IsGpuWritten(u64 device_address) {
+ const u64 page = device_address >> Nx2WrittenPageBits;
+ if (page >= g_nx2_gpu_written_pages.size() * 64) {
+ return false;
+ }
+ return (g_nx2_gpu_written_pages[page / 64].load(std::memory_order_relaxed) >> (page % 64)) & 1;
+}
+
// NVN2 queues release their progress values into the second page of a two-page GPU-accessible
// region and GPU acquires read the same offsets in the first page (the CPU polls the second).
// Something on the console copies one into the other; until that is known, the puller mirrors