aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/host_memory.cpp2
-rw-r--r--src/common/host_memory.h3
-rw-r--r--src/core/device_memory_manager.h7
-rw-r--r--src/core/device_memory_manager.inc44
-rw-r--r--src/video_core/texture_cache/texture_cache.h14
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h2
6 files changed, 70 insertions, 2 deletions
diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp
index c0e7160dd4..2c45dfd3a5 100644
--- a/src/common/host_memory.cpp
+++ b/src/common/host_memory.cpp
@@ -791,7 +791,7 @@ void HostMemory::Protect(size_t virtual_offset, size_t length, MemoryPermission
}
void HostMemory::HintCold(size_t physical_offset, size_t length,
- std::optional<size_t> virtual_offset) {
+ std::optional<size_t> virtual_offset) const {
if (length == 0 || backing_base == nullptr) {
return;
}
diff --git a/src/common/host_memory.h b/src/common/host_memory.h
index fe0938103c..4d8d15cf58 100644
--- a/src/common/host_memory.h
+++ b/src/common/host_memory.h
@@ -57,7 +57,8 @@ public:
/// Memory written once and read again rarely: its pages leave the working set (backing and,
/// when given, its view at virtual_offset), so the OS compresses or pages them out first when
/// it needs RAM. The contents stay; using the pages brings them back.
- void HintCold(size_t physical_offset, size_t length, std::optional<size_t> virtual_offset);
+ void HintCold(size_t physical_offset, size_t length,
+ std::optional<size_t> virtual_offset) const;
[[nodiscard]] u8* BackingBasePointer() noexcept {
return backing_base;
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_;
}
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 63abffed0f..13637aa265 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -315,6 +315,12 @@ void TextureCache<P>::TickFrame() {
sentenced_image_view.Tick();
TickAsyncDecode();
TickAsyncUnswizzle();
+ if (frame_tick % 600 == 0 && !uploaded_ranges.empty()) {
+ for (const auto& [address, size] : uploaded_ranges) {
+ device_memory.HintCold(address, size);
+ }
+ uploaded_ranges.clear();
+ }
runtime.TickFrame();
++frame_tick;
@@ -1352,6 +1358,14 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image));
UploadImageContents(image, staging);
runtime.InsertUploadMemoryBarrier();
+ // The host GPU has its own copy now and the guest rarely reads this memory again: TickFrame
+ // marks it cold, so the OS pages it out before anything else when it needs RAM (title 1
+ // streams several GB of textures through it).
+ constexpr u64 ColdHintMinBytes = 64_KiB;
+ if (image.guest_size_bytes >= ColdHintMinBytes &&
+ False(image.flags & (ImageFlagBits::GpuModified | ImageFlagBits::Sparse))) {
+ uploaded_ranges.emplace_back(image.cpu_addr, image.guest_size_bytes);
+ }
}
template <class P>
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 11e95f98b3..26d989f2da 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -558,6 +558,8 @@ private:
Common::ThreadWorker texture_decode_worker{1, "TextureDecoder", {},
Common::ThreadPlacement::Efficiency};
std::vector<std::unique_ptr<AsyncDecodeContext>> async_decodes;
+ /// Guest memory images were uploaded from since the last cold hint (device address, size).
+ std::vector<std::pair<DAddr, u64>> uploaded_ranges;
std::deque<PendingUnswizzle> unswizzle_queue;
u8 current_unswizzle_frame;