Some changes to GC

This commit is contained in:
CamilleLaVey
2026-09-18 01:56:42 -04:00
parent 17adc28b2c
commit 08741c919a
6 changed files with 113 additions and 53 deletions
+1
View File
@@ -22,6 +22,7 @@ add_library(video_core STATIC
buffer_cache/usage_tracker.h
buffer_cache/virtual_range_cache.h
buffer_cache/word_manager.h
cache_reclaim.h
cache_types.h
capture.h
cdma_pusher.cpp
+45 -25
View File
@@ -30,34 +30,52 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
#ifdef YUZU_LEGACY
immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive);
#endif
if (!runtime.CanReportMemoryUsage()) {
minimum_memory = DEFAULT_EXPECTED_MEMORY;
critical_memory = DEFAULT_CRITICAL_MEMORY;
return;
}
const s64 device_local_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
const s64 min_spacing_expected = device_local_memory - 1_GiB;
const s64 min_spacing_critical = device_local_memory - 512_MiB;
const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
minimum_memory = static_cast<u64>(
(std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected),
DEFAULT_EXPECTED_MEMORY));
critical_memory = static_cast<u64>(
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY));
device_local_memory = runtime.GetDeviceLocalMemory();
const auto thresholds = VideoCommon::MakeReclaimThresholds(
device_local_memory, static_cast<u64>(TARGET_THRESHOLD),
static_cast<u64>(DEFAULT_EXPECTED_MEMORY), static_cast<u64>(DEFAULT_CRITICAL_MEMORY));
minimum_memory = thresholds.minimum;
expected_memory = thresholds.expected;
critical_memory = thresholds.critical;
}
template <class P>
BufferCache<P>::~BufferCache() = default;
template <class P>
void BufferCache<P>::ReclaimInline() {
if (total_used_memory < minimum_memory) {
return;
}
int num_iterations = 8;
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
if (num_iterations == 0) {
return true;
}
Buffer& buffer = slot_buffers[buffer_id];
if (memory_tracker.IsRegionGpuModified(buffer.CpuAddr(), buffer.SizeBytes())) {
return false;
}
--num_iterations;
DeleteBuffer(buffer_id);
return false;
};
lru_cache.ForEachItemBelow(frame_tick - INLINE_TICKS_TO_DESTROY, clean_up);
}
template <class P>
void BufferCache<P>::RunGarbageCollector() {
const bool aggressive_gc = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32;
const bool aggressive_gc = heap_pressure || total_used_memory >= critical_memory;
const bool priority_gc = aggressive_gc || total_used_memory >= expected_memory;
u64 ticks_to_destroy = 120;
int num_iterations = 32;
if (aggressive_gc) {
ticks_to_destroy = 30;
num_iterations = 64;
} else if (priority_gc) {
ticks_to_destroy = 60;
num_iterations = 48;
}
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
if (num_iterations == 0) {
return true;
@@ -96,11 +114,12 @@ void BufferCache<P>::TickFrame() {
const bool skip_preferred = hits * 256 < shots * 251;
channel_state->uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
// If we can obtain the memory info, use it instead of the estimate.
if (runtime.CanReportMemoryUsage()) {
total_used_memory = runtime.GetDeviceMemoryUsage();
heap_pressure = false;
if (device_local_memory != 0 && runtime.CanReportMemoryUsage()) {
heap_pressure = runtime.GetDeviceMemoryUsage() + HEAP_PRESSURE_HEADROOM >=
device_local_memory;
}
if (total_used_memory >= minimum_memory) {
if (total_used_memory >= minimum_memory || heap_pressure) {
RunGarbageCollector();
}
++frame_tick;
@@ -1674,6 +1693,7 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
template <class P>
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size,
bool sparse_compatible) {
ReclaimInline();
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
wanted_size = static_cast<u32>(device_addr_end - device_addr);
@@ -31,6 +31,7 @@
#include "video_core/buffer_cache/buffer_base.h"
#include "video_core/buffer_cache/virtual_range_cache.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/cache_reclaim.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
@@ -199,6 +200,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
static constexpr u64 HEAP_PRESSURE_HEADROOM = 512_MiB;
static constexpr u64 INLINE_TICKS_TO_DESTROY = 240;
// Debug Flags.
@@ -381,6 +384,8 @@ private:
void RunGarbageCollector();
void ReclaimInline();
void BindHostIndexBuffer();
void BindHostVertexBuffers();
@@ -540,8 +545,11 @@ private:
std::vector<MultiRangeSegment> compute_segments;
u64 frame_tick = 0;
u64 total_used_memory = 0;
u64 device_local_memory = 0;
u64 minimum_memory = 0;
u64 expected_memory = 0;
u64 critical_memory = 0;
bool heap_pressure = false;
BufferId inline_buffer_id;
#ifdef YUZU_LEGACY
bool immediately_free = false;
+35
View File
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <algorithm>
#include "common/common_types.h"
namespace VideoCommon {
struct ReclaimThresholds {
u64 minimum{};
u64 expected{};
u64 critical{};
};
[[nodiscard]] constexpr ReclaimThresholds MakeReclaimThresholds(u64 device_local_memory,
u64 target_threshold,
u64 default_expected,
u64 default_critical) {
u64 critical = default_critical;
if (device_local_memory != 0) {
const u64 budget = (std::min)(device_local_memory, target_threshold);
critical = (std::min)(critical, budget / 2);
}
const u64 expected = (std::min)(default_expected, (critical * 3) / 4);
return ReclaimThresholds{
.minimum = (expected * 3) / 4,
.expected = expected,
.critical = critical,
};
}
} // namespace VideoCommon
+17 -25
View File
@@ -58,24 +58,14 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
void(slot_samplers.insert(runtime, sampler_descriptor));
if constexpr (HAS_DEVICE_MEMORY_INFO) {
const s64 device_local_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
const s64 min_spacing_expected = device_local_memory - 1_GiB;
const s64 min_spacing_critical = device_local_memory - 512_MiB;
const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD);
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
expected_memory = static_cast<u64>(
(std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected),
DEFAULT_EXPECTED_MEMORY));
critical_memory = static_cast<u64>(
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
DEFAULT_CRITICAL_MEMORY));
minimum_memory = static_cast<u64>((device_local_memory - mem_threshold) / 2);
} else {
expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
minimum_memory = 0;
device_local_memory = runtime.GetDeviceLocalMemory();
}
const auto thresholds = VideoCommon::MakeReclaimThresholds(
device_local_memory, static_cast<u64>(TARGET_THRESHOLD),
static_cast<u64>(DEFAULT_EXPECTED_MEMORY), static_cast<u64>(DEFAULT_CRITICAL_MEMORY));
minimum_memory = thresholds.minimum;
expected_memory = thresholds.expected;
critical_memory = thresholds.critical;
}
template <class P>
@@ -85,8 +75,9 @@ void TextureCache<P>::RunGarbageCollector() {
u64 ticks_to_destroy = 0;
size_t num_iterations = 0;
const auto Configure = [&](bool allow_aggressive) {
high_priority_mode = total_used_memory >= expected_memory;
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
high_priority_mode = heap_pressure || total_used_memory >= expected_memory;
aggressive_mode =
allow_aggressive && (heap_pressure || total_used_memory >= critical_memory);
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
};
@@ -100,7 +91,7 @@ void TextureCache<P>::RunGarbageCollector() {
return false;
}
const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap);
if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) {
if (!aggressive_mode && (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) {
return false;
}
if (must_download) {
@@ -126,7 +117,7 @@ void TextureCache<P>::RunGarbageCollector() {
};
Configure(false);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
if (total_used_memory >= critical_memory) {
if (heap_pressure || total_used_memory >= critical_memory) {
Configure(true);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
}
@@ -134,11 +125,12 @@ void TextureCache<P>::RunGarbageCollector() {
template <class P>
void TextureCache<P>::TickFrame() {
// If we can obtain the memory info, use it instead of the estimate.
if (runtime.CanReportMemoryUsage()) {
total_used_memory = runtime.GetDeviceMemoryUsage();
heap_pressure = false;
if (device_local_memory != 0 && runtime.CanReportMemoryUsage()) {
heap_pressure = runtime.GetDeviceMemoryUsage() + HEAP_PRESSURE_HEADROOM >=
device_local_memory;
}
if (total_used_memory > minimum_memory) {
if (total_used_memory > minimum_memory || heap_pressure) {
RunGarbageCollector();
}
sentenced_images.Tick();
@@ -31,6 +31,7 @@
#include "common/thread_worker.h"
#include "video_core/compatible_formats.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/cache_reclaim.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/surface.h"
@@ -121,6 +122,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
static constexpr u64 HEAP_PRESSURE_HEADROOM = 512_MiB;
static constexpr size_t GC_EMERGENCY_COUNTS = 2;
using Runtime = typename P::Runtime;
@@ -440,9 +442,11 @@ private:
bool has_deleted_images = false;
bool is_rescaling = false;
u64 total_used_memory = 0;
u64 minimum_memory;
u64 expected_memory;
u64 critical_memory;
u64 device_local_memory = 0;
u64 minimum_memory = 0;
u64 expected_memory = 0;
u64 critical_memory = 0;
bool heap_pressure = false;
struct BufferDownload {
GPUVAddr address;