diff options
| author | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 00:30:52 -0400 |
|---|---|---|
| committer | Takoroka <137927095+Takoroka@users.noreply.github.com> | 2026-11-01 08:48:31 -0500 |
| commit | e2c512916c6f5042d030fcf1708043233f8df08e (patch) | |
| tree | f9750541db337ba3415a05c4a0d73ebde979c458 /src/core | |
| parent | a03e816886f4241309a6e77ba7df1ad922da1fc0 (diff) | |
texture_cache: mark guest memory textures were uploaded from cold
Once a texture is uploaded the host GPU has its own copy, and the guest
rarely reads that memory again, but it stayed in the emulator's working
set. Every 600 frames the guest ranges of images uploaded since (64 KiB
or more, not GPU-written or sparse) are marked cold through
HostMemory::HintCold, backing and CPU view, from a low-priority worker,
so the OS pages them out before anything else when it needs RAM. Their
contents stay; using them brings them back. Mario Kart World's title
screen at ~10 minutes: game memory in RAM 4.8-5.5 -> 3.6 GB, the
emulator's real RAM 10.6-11.9 -> 9.4 GB, frames over 100 ms not worse.
(cherry picked from commit ea36b2c5ca352f8c8cdcc829a2d3bde9bdc06ece)
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/device_memory_manager.h | 7 | ||||
| -rw-r--r-- | src/core/device_memory_manager.inc | 44 |
2 files changed, 51 insertions, 0 deletions
diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 9c034f01a1..05a8136169 100644 --- a/src/core/device_memory_manager.h +++ b/src/core/device_memory_manager.h @@ -18,6 +18,7 @@ #include "common/common_types.h" #include "common/range_mutex.h" #include "common/scratch_buffer.h" +#include "common/thread_worker.h" #include "common/sparse_large_vector.h" namespace Core { @@ -53,6 +54,10 @@ public: void BindInterface(DeviceInterface* device_inter); + /// Marks the memory behind [address, address + size) cold (Common::HostMemory::HintCold), + /// from a low-priority thread: data the host GPU has its own copy of, e.g. uploaded textures. + void HintCold(DAddr address, size_t size); + DAddr Allocate(size_t size); void AllocateFixed(DAddr start, size_t size); void Free(DAddr start, size_t size); @@ -194,7 +199,9 @@ private: std::unique_ptr<DeviceMemoryManagerAllocator<Traits>> impl; const uintptr_t physical_base; + const DeviceMemory* device_memory_ptr; DeviceInterface* device_inter; + std::unique_ptr<Common::ThreadWorker> cold_hint_worker; struct TrackedEntry { VAddr cpu_backing_address; diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc index c45bc71656..77ab95e07e 100644 --- a/src/core/device_memory_manager.inc +++ b/src/core/device_memory_manager.inc @@ -171,6 +171,7 @@ struct DeviceMemoryManagerAllocator { template <typename Traits> DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memory_) : physical_base{uintptr_t(device_memory_.buffer.BackingBasePointer())} + , device_memory_ptr{&device_memory_} , device_inter{nullptr} , compressed_device_addr(1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS)) , tracked_entries(device_as_size >> Memory::YUZU_PAGEBITS) @@ -183,6 +184,49 @@ template <typename Traits> DeviceMemoryManager<Traits>::~DeviceMemoryManager() = default; template <typename Traits> +void DeviceMemoryManager<Traits>::HintCold(DAddr address, size_t size) { + struct Run { + u64 physical_offset; + VAddr virtual_address; ///< 0: no CPU mapping known + u64 size; + }; + std::vector<Run> runs; + const size_t page_end = Common::DivCeil(address + size, Memory::YUZU_PAGESIZE); + for (size_t page = address >> Memory::YUZU_PAGEBITS; page < page_end; ++page) { + const u32 compressed = tracked_entries[page].compressed_physical_ptr; + if (compressed == 0) { + continue; + } + const u64 physical = u64{compressed - 1} << Memory::YUZU_PAGEBITS; + const VAddr vaddr = ExtractCPUBacking(page).second; + if (!runs.empty()) { + Run& last = runs.back(); + const bool contiguous_view = + last.virtual_address == 0 ? vaddr == 0 : last.virtual_address + last.size == vaddr; + if (last.physical_offset + last.size == physical && contiguous_view) { + last.size += Memory::YUZU_PAGESIZE; + continue; + } + } + runs.push_back({physical, vaddr, Memory::YUZU_PAGESIZE}); + } + if (runs.empty()) { + return; + } + if (!cold_hint_worker) { + cold_hint_worker = std::make_unique<Common::ThreadWorker>(1, "DeviceMemory:ColdHint"); + } + cold_hint_worker->QueueWork([this, runs = std::move(runs)] { + for (const Run& run : runs) { + device_memory_ptr->buffer.HintCold( + run.physical_offset, run.size, + run.virtual_address != 0 ? std::optional<size_t>{run.virtual_address} + : std::nullopt); + } + }); +} + +template <typename Traits> void DeviceMemoryManager<Traits>::BindInterface(DeviceInterface* device_inter_) { device_inter = device_inter_; } |
