diff options
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 | ||||
| -rw-r--r-- | src/core/hle/service/nx2_gsv.h | 40 |
3 files changed, 91 insertions, 0 deletions
diff --git a/src/core/device_memory_manager.h b/src/core/device_memory_manager.h index 27ff342213..d275bdbf2d 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); @@ -197,7 +202,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 83faa1b3f3..3523e3cf1c 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_; } diff --git a/src/core/hle/service/nx2_gsv.h b/src/core/hle/service/nx2_gsv.h index 628bdeda5b..90959057f1 100644 --- a/src/core/hle/service/nx2_gsv.h +++ b/src/core/hle/service/nx2_gsv.h @@ -1246,6 +1246,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), |
