aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/buffer_cache
diff options
context:
space:
mode:
authorTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 08:38:09 -0400
committerTakoroka <137927095+Takoroka@users.noreply.github.com>2026-10-29 20:49:47 -0400
commit35e55ed818a705847bc2cb676fc35d315b683e55 (patch)
treedd38c1d3e60343009bc94bb6d1893f3aa27fb183 /src/video_core/buffer_cache
parent25bd673899ccdfc58477a286fcc39632feaf9def (diff)
buffer_cache: collect on the buffers' own size for Switch 2 titles
The buffer collector compared the whole device's memory use with thresholds from the device size, so with title 1's textures filling an 8 GB GPU (~6.8 GB used, buffers ~200 MiB of it) it ran aggressively every frame: up to 64 buffers evicted and recreated later, the GPU-written ones downloaded first with a wait for the GPU (~5% of the GPU thread). For Switch 2 titles it now runs when the buffers themselves pass 1 GiB (aggressive past 2 GiB); below the aggressive mode it also keeps buffers holding GPU-written data, which it would have to download. Other titles keep the device-wide thresholds. Buffer downloads by the collector: 5.4% -> 0%; the cache settles around 540 MiB.
Diffstat (limited to 'src/video_core/buffer_cache')
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h26
-rw-r--r--src/video_core/buffer_cache/buffer_cache_base.h3
2 files changed, 24 insertions, 5 deletions
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 39a528ddeb..42d7ba066d 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -58,8 +58,7 @@ template <class P>
BufferCache<P>::~BufferCache() = default;
template <class P>
-void BufferCache<P>::RunGarbageCollector() {
- const bool aggressive_gc = total_used_memory >= critical_memory;
+void BufferCache<P>::RunGarbageCollector(bool aggressive_gc) {
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32;
// Their GPU-written data is downloaded first, all with one wait for the GPU: one wait per
@@ -71,6 +70,14 @@ void BufferCache<P>::RunGarbageCollector() {
return true;
}
--num_iterations;
+ // Below the critical threshold, buffers holding GPU-written data stay: evicting one
+ // means downloading it first. The thresholds compare the whole device's use (mostly
+ // textures) with fixed headroom, so on an 8 GB GPU the collector ran every frame once a
+ // title 1 course was loaded, and the downloads were ~2.7% of the GPU thread.
+ const Buffer& buffer = slot_buffers[buffer_id];
+ if (!aggressive_gc && gpu_modified_ranges.Intersects(buffer.CpuAddr(), buffer.SizeBytes())) {
+ return false;
+ }
evicted.push_back(buffer_id);
return false;
});
@@ -129,8 +136,17 @@ void BufferCache<P>::TickFrame() {
if (runtime.CanReportMemoryUsage()) {
total_used_memory = runtime.GetDeviceMemoryUsage();
}
- if (total_used_memory >= minimum_memory) {
- RunGarbageCollector();
+ if (gpu_memory != nullptr && gpu_memory->UsesNativeTiling()) {
+ // Switch 2 titles fill the device with textures (their collector watches the budget);
+ // title 1's buffers were ~200 MiB of an 8 GB GPU's ~6.8 GB in use, yet this collector
+ // ran aggressively every frame on the device-wide figure, evicting up to 64 buffers (and
+ // downloading the GPU-written ones, ~5% of the GPU thread) that were recreated later.
+ // Collect when the buffers themselves are large.
+ if (buffer_bytes >= 1_GiB) {
+ RunGarbageCollector(buffer_bytes >= 2_GiB);
+ }
+ } else if (total_used_memory >= minimum_memory) {
+ RunGarbageCollector(total_used_memory >= critical_memory);
}
++frame_tick;
delayed_destruction_ring.Tick();
@@ -1770,9 +1786,11 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
Buffer& buffer = slot_buffers[buffer_id];
const auto size = buffer.SizeBytes();
if (insert) {
+ buffer_bytes += Common::AlignUp(size, 1024);
total_used_memory += Common::AlignUp(size, 1024);
buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
} else {
+ buffer_bytes -= Common::AlignUp(size, 1024);
total_used_memory -= Common::AlignUp(size, 1024);
lru_cache.Free(buffer.getLRUID());
}
diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h
index 25d35e03ec..45a1836341 100644
--- a/src/video_core/buffer_cache/buffer_cache_base.h
+++ b/src/video_core/buffer_cache/buffer_cache_base.h
@@ -378,7 +378,7 @@ private:
((device_addr + size) & ~Core::DEVICE_PAGEMASK);
}
- void RunGarbageCollector();
+ void RunGarbageCollector(bool aggressive_gc);
void BindHostIndexBuffer();
@@ -560,6 +560,7 @@ private:
std::vector<MultiRangeSegment> compute_segments;
u64 frame_tick = 0;
u64 total_used_memory = 0;
+ u64 buffer_bytes = 0; ///< The buffers' own size (total_used_memory may be the device's)
u64 minimum_memory = 0;
u64 critical_memory = 0;
BufferId inline_buffer_id;