diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 01:03:37 -0500 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 15:18:01 -0500 |
| commit | 40902b09c7b86de7cf12f26839241f26925d04b5 (patch) | |
| tree | 1a0403bb25a8ab44f37e270564fb4a105ed1e6b9 /src/video_core/nx2.h | |
| parent | 81746294f8114f93d82de1e1984b00d0194f25ba (diff) | |
nx2: leave unwritten sparse blocks without memory when they get zeros
Title 1 clears about 1 GB of its sparse regions at boot through the GPU
memory manager. Every block the clear touched got 4 MiB of guest memory,
which the clear itself then committed on the host, and counted as written,
so the texture cache gave the images over it device memory too. A block
nobody wrote reads zero already: when the part of a write that lands on
it is all zero, leave it as it is.
(cherry picked from commit 0d7ff63fd1a373cecff6ba91022c9027e801e53f)
Diffstat (limited to 'src/video_core/nx2.h')
| -rw-r--r-- | src/video_core/nx2.h | 18 |
1 files changed, 14 insertions, 4 deletions
diff --git a/src/video_core/nx2.h b/src/video_core/nx2.h index 1fb49e15a6..db8c92adfd 100644 --- a/src/video_core/nx2.h +++ b/src/video_core/nx2.h @@ -97,8 +97,9 @@ inline void Nx2ClearSemaphoreRegions() { // Switch 2 sparse regions (nx2:gpu 214, word 7 bit 0x200: title 1's terrain texture arrays and // layer pools, 170 MB-5.8 GB each, of which the game fills a little) get memory per block when the // GPU first writes there, not in full. nx2:gpu installs the handler and the span its regions cover; -// the GPU memory manager calls Nx2BackSparseWrite before each write. -using Nx2SparseBackingHandler = std::function<void(u64 gpu_address, u64 size)>; +// the GPU memory manager calls Nx2BackSparseWrite before each write, with the bytes it writes +// (blocks nobody wrote that only get zeros need no memory: they read zero already). +using Nx2SparseBackingHandler = std::function<void(u64 gpu_address, u64 size, const u8* data)>; inline std::recursive_mutex g_nx2_sparse_backing_mutex; inline Nx2SparseBackingHandler g_nx2_sparse_backing; inline std::atomic<u64> g_nx2_sparse_low{~u64{0}}; @@ -122,14 +123,14 @@ inline void Nx2AddSparseSpan(u64 gpu_address, u64 size) { } } -inline void Nx2BackSparseWrite(u64 gpu_address, u64 size) { +inline void Nx2BackSparseWrite(u64 gpu_address, u64 size, const void* data) { if (gpu_address + size <= g_nx2_sparse_low.load(std::memory_order_relaxed) || gpu_address >= g_nx2_sparse_high.load(std::memory_order_relaxed)) [[likely]] { return; } std::scoped_lock lk{g_nx2_sparse_backing_mutex}; if (g_nx2_sparse_backing) { - g_nx2_sparse_backing(gpu_address, size); + g_nx2_sparse_backing(gpu_address, size, static_cast<const u8*>(data)); } } @@ -174,6 +175,15 @@ inline void Nx2SparseBlockWritten(u64 region_address, u64 block) { } } +/// True unless the block of the region at region_address is known to hold nothing (no write from +/// the CPU or the GPU yet). +inline bool Nx2SparseBlockIsWritten(u64 region_address, u64 block) { + std::scoped_lock lk{g_nx2_sparse_state_mutex}; + const auto it = g_nx2_sparse_state.find(region_address); + return it == g_nx2_sparse_state.end() || block >= it->second.written.size() || + it->second.written[block]; +} + /// The game maps its own memory over [offset, +size) of the region at region_address (mapped), or /// unmaps it there. inline void Nx2SparseTilesMapped(u64 region_address, u64 offset, u64 size, bool mapped) { |
