feat(video_core): implement comprehensive VRAM management system

Add automatic VRAM leak prevention with configurable garbage collection
to prevent device loss crashes during extended play sessions.

New Settings:
- vram_limit_mb: Configurable VRAM limit (0 = auto-detect 80%)
- gc_aggressiveness: Off/Light/Moderate/Heavy/Extreme levels
- texture_eviction_frames: Frames before texture eviction (default 2)
- buffer_eviction_frames: Frames before buffer eviction (default 5)
- sparse_texture_priority_eviction: Prioritize large sparse textures
- log_vram_usage: Enable VRAM statistics logging

Core Changes:
- Enhanced texture cache with LRU eviction and sparse texture priority
- Enhanced buffer cache with configurable eviction thresholds
- Added VRAM pressure monitoring using VK_EXT_memory_budget
- Emergency GC triggers at 90%/95% VRAM usage thresholds

Platform Support:
- Desktop: Settings in Graphics > Advanced tab
- Android: Settings in Zep Zone category

Fixes VRAM climbing steadily during gameplay until device loss.
Target: Stable VRAM usage below configured limit for 2+ hours.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron
2026-01-25 15:21:02 +10:00
parent 44f9cb6347
commit 3e2137a470
16 changed files with 938 additions and 48 deletions

View File

@@ -159,6 +159,25 @@ void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebu
render_window.OnFrameDisplayed();
};
// FIXED: VRAM leak prevention - Check VRAM pressure before rendering
if (device.CanReportMemoryUsage()) {
const u64 current_usage = device.GetDeviceMemoryUsage();
const u64 total_vram = device.GetDeviceLocalMemory();
const u32 configured_limit = Settings::values.vram_limit_mb.GetValue();
const u64 vram_limit = configured_limit > 0
? static_cast<u64>(configured_limit) * 1024ULL * 1024ULL
: static_cast<u64>(total_vram * 0.80);
// If VRAM usage is above 90% of limit, trigger emergency GC on texture/buffer caches
if (current_usage >= static_cast<u64>(vram_limit * 0.90)) {
LOG_WARNING(Render_Vulkan,
"VRAM pressure critical: {}MB/{}MB ({:.1f}%), triggering emergency GC",
current_usage / (1024ULL * 1024ULL), vram_limit / (1024ULL * 1024ULL),
(static_cast<f32>(current_usage) / vram_limit) * 100.0f);
rasterizer.TriggerMemoryGC();
}
}
RenderAppletCaptureLayer(framebuffers);
if (!render_window.IsShown()) {
@@ -201,6 +220,30 @@ void RendererVulkan::Report() const {
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
LOG_INFO(Render_Vulkan, "Available VRAM: {:.2f} GiB", available_vram);
// FIXED: VRAM leak prevention - Report VRAM management settings
const u32 vram_limit_mb = Settings::values.vram_limit_mb.GetValue();
const auto gc_level = Settings::values.gc_aggressiveness.GetValue();
const u32 texture_eviction = Settings::values.texture_eviction_frames.GetValue();
const u32 buffer_eviction = Settings::values.buffer_eviction_frames.GetValue();
if (vram_limit_mb > 0) {
LOG_INFO(Render_Vulkan, "VRAM Limit: {} MB (configured)", vram_limit_mb);
} else {
LOG_INFO(Render_Vulkan, "VRAM Limit: Auto ({:.0f} MB, 80% of available)",
available_vram * 0.8 * 1024.0);
}
LOG_INFO(Render_Vulkan, "GC Aggressiveness: {}, Texture eviction: {} frames, Buffer eviction: {} frames",
static_cast<u32>(gc_level), texture_eviction, buffer_eviction);
// FIXED: VRAM leak prevention - Report VK_EXT_memory_budget support
if (device.CanReportMemoryUsage()) {
const auto current_usage = device.GetDeviceMemoryUsage();
LOG_INFO(Render_Vulkan, "VK_EXT_memory_budget: Supported, Current usage: {:.2f} GiB",
static_cast<f64>(current_usage) / f64{1_GiB});
} else {
LOG_INFO(Render_Vulkan, "VK_EXT_memory_budget: Not supported (using estimates)");
}
static constexpr auto field = Common::Telemetry::FieldType::UserSystem;
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
telemetry_session.AddField(field, "GPU_Model", model_name);

View File

@@ -861,6 +861,17 @@ u64 RasterizerVulkan::GetStagingMemoryUsage() const {
}
}
// FIXED: VRAM leak prevention - Trigger garbage collection on texture/buffer caches
void RasterizerVulkan::TriggerMemoryGC() {
std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex};
// Trigger GC on both caches
texture_cache.TriggerGarbageCollection();
buffer_cache.TriggerGarbageCollection();
LOG_DEBUG(Render_Vulkan, "Manual memory GC triggered");
}
bool RasterizerVulkan::AccelerateConditionalRendering() {
gpu_memory->FlushCaching();
return query_cache.AccelerateHostConditionalRendering();

View File

@@ -125,6 +125,10 @@ public:
u64 GetBufferMemoryUsage() const;
u64 GetTextureMemoryUsage() const;
u64 GetStagingMemoryUsage() const;
// FIXED: VRAM leak prevention - Trigger garbage collection on texture/buffer caches
void TriggerMemoryGC();
bool AccelerateConditionalRendering() override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,