aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-31 01:32:04 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-31 01:32:04 -0400
commita03e816886f4241309a6e77ba7df1ad922da1fc0 (patch)
tree034f04751a0ee44b0ec6840a111adb7553f05174 /src/core
parentb40ad217ab829226841cb71f6e4c5cb05977a225 (diff)
nx2: let the host page out texture pool backing first
Switch 2 sparse blocks and GPU-only regions get guest memory when a game writes them (Mario Kart World copies 2-4 GB of compressed textures into its sparse texture pools) and the texture cache reads it again only when it builds an image there. Every 30 s, mark that backing cold: its pages leave the working set (VirtualUnlock on Windows, MADV_PAGEOUT on Linux) so the OS compresses or pages them out before anything else when RAM is short. The contents stay. Mario Kart World: 8.5 GB of RAM in use at five minutes instead of 9.0 GB on a machine with RAM to spare, frame times unchanged.
Diffstat (limited to 'src/core')
-rw-r--r--src/core/hle/service/nx2_gsv.h40
1 files changed, 40 insertions, 0 deletions
diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h
index 9d00b81368..2551a98211 100644
--- a/src/core/hle/service/nx2_gsv.h
+++ b/src/core/hle/service/nx2_gsv.h
@@ -786,6 +786,46 @@ private:
std::array<Kernel::KEvent*, NumIds> m_events{};
std::map<u32, GpuMemory> m_memory;
u32 m_next_memory_handle{1};
+
+ // Every 30 s: the backing of sparse blocks and GPU-only regions is marked cold. The game
+ // writes it once (texture uploads by 2D blits: title 1 fills 2-4 GB of texture pools) and it
+ // is read again only when the texture cache builds an image over it, so the OS may compress or
+ // page it out before anything else. Last member: stops before the regions are destroyed.
+ void HintColdBacking() {
+ struct Range {
+ u64 physical_offset;
+ u64 virtual_address;
+ u64 size;
+ };
+ std::vector<Range> ranges;
+ {
+ std::scoped_lock lk{m_region_mutex};
+ for (const auto& [gpu_address, region] : m_gpu_only_regions) {
+ u64 virtual_address = region.cpu_address;
+ for (const auto& block : *region.pages) {
+ ranges.push_back({GetInteger(block.GetAddress()) - Core::DramMemoryMap::Base,
+ virtual_address, block.GetSize()});
+ virtual_address += block.GetSize();
+ }
+ }
+ }
+ auto& buffer = system.DeviceMemory().buffer;
+ for (const Range& range : ranges) {
+ buffer.HintCold(range.physical_offset, range.size, range.virtual_address);
+ }
+ }
+ std::jthread m_cold_hint_thread{[this](std::stop_token stop) {
+ Common::SetCurrentThreadName("nx2:ColdHint");
+ Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
+ while (!stop.stop_requested()) {
+ for (int i = 0; i < 30 && !stop.stop_requested(); ++i) {
+ std::this_thread::sleep_for(std::chrono::seconds{1});
+ }
+ if (!stop.stop_requested()) {
+ HintColdBacking();
+ }
+ }
+ }};
};
// Native memory and images handed out by the Switch 2 graphics server (gsv 111/114/423/429/315),