diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 9ec71ecede..ab5cbe67b9 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -59,6 +59,7 @@ void LogSettings() { log_setting("Renderer_UseVsync", values.use_vsync.GetValue()); log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue()); log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue()); + log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue()); log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue()); log_setting("Audio_OutputEngine", values.sink_id); log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); @@ -141,6 +142,7 @@ void RestoreGlobalState(bool is_powered_on) { values.use_assembly_shaders.SetGlobal(true); values.use_asynchronous_shaders.SetGlobal(true); values.use_fast_gpu_time.SetGlobal(true); + values.use_caches_gc.SetGlobal(true); values.bg_red.SetGlobal(true); values.bg_green.SetGlobal(true); values.bg_blue.SetGlobal(true); diff --git a/src/common/settings.h b/src/common/settings.h index 6198f2d9fd..a1c0bf3ad2 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -152,6 +152,7 @@ struct Values { Setting use_assembly_shaders; Setting use_asynchronous_shaders; Setting use_fast_gpu_time; + Setting use_caches_gc; Setting bg_red; Setting bg_green; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index b4fa85c5b6..a8fb21d92c 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -350,9 +350,10 @@ BufferCache

::BufferCache(VideoCore::RasterizerInterface& rasterizer_, template void BufferCache

::TickFrame() { + const bool enabled_gc = Settings::values.use_caches_gc.GetValue(); SCOPE_EXIT({ - ++frame_tick; - delayed_destruction_ring.Tick(); + ++frame_tick; + delayed_destruction_ring.Tick(); }); // Calculate hits and shots and move hit bits to the right const u32 hits = std::reduce(uniform_cache_hits.begin(), uniform_cache_hits.end()); @@ -367,7 +368,7 @@ void BufferCache

::TickFrame() { const bool skip_preferred = hits * 256 < shots * 251; uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0; - const bool activate_gc = total_used_memory >= expected_memory; + const bool activate_gc = enabled_gc && total_used_memory >= expected_memory; if (!activate_gc) { return; } diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h index 40c047ea19..e326cab71c 100644 --- a/src/video_core/texture_cache/image_base.h +++ b/src/video_core/texture_cache/image_base.h @@ -27,10 +27,10 @@ enum class ImageFlagBits : u32 { Picked = 1 << 7, ///< Temporary flag to mark the image as picked // Garbage Collection Flags - BadOverlap = 1 << 8, ///< This image overlaps other but doesn't fit, has higher - ///< garbage collection priority - Alias = 1 << 9, ///< This image has aliases and has priority on garbage - ///< collection + BadOverlap = 1 << 8, ///< This image overlaps other but doesn't fit, has higher + ///< garbage collection priority + Alias = 1 << 9, ///< This image has aliases and has priority on garbage + ///< collection }; DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits) diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index cf48f7b022..8685f44182 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -22,6 +22,7 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "video_core/compatible_formats.h" #include "video_core/delayed_destruction_ring.h" #include "video_core/dirty_flags.h" @@ -384,6 +385,15 @@ TextureCache

::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface& template void TextureCache

::TickFrame() { + const bool enabled_gc = Settings::values.use_caches_gc.GetValue(); + if (!enabled_gc) { + // @Note(Blinkhawk): compile error with SCOPE_EXIT on msvc. + sentenced_images.Tick(); + sentenced_framebuffers.Tick(); + sentenced_image_view.Tick(); + ++frame_tick; + return; + } const bool high_priority_mode = total_used_memory >= expected_memory; const bool aggressive_mode = total_used_memory >= critical_memory; const u64 ticks_to_destroy = high_priority_mode ? 60 : 100; @@ -397,22 +407,20 @@ void TextureCache

::TickFrame() { } const auto [image_id, image] = *deletion_iterator; const bool is_alias = True(image->flags & ImageFlagBits::Alias); - if (is_alias && image->aliased_images.size() <= 1) { - ++deletion_iterator; - continue; - } const bool is_bad_overlap = True(image->flags & ImageFlagBits::BadOverlap); const bool must_download = image->IsSafeDownload(); - const u64 ticks_needed = is_bad_overlap ? ticks_to_destroy >> 4 : ticks_to_destroy; - const bool should_care = - aggressive_mode || is_bad_overlap || is_alias || (high_priority_mode && !must_download); + bool should_care = is_bad_overlap || is_alias || (high_priority_mode && !must_download); + const u64 ticks_needed = + is_bad_overlap + ? ticks_to_destroy >> 4 + : ((should_care && aggressive_mode) ? ticks_to_destroy >> 1 : ticks_to_destroy); + should_care |= aggressive_mode; if (should_care && image->frame_tick + ticks_needed < frame_tick) { if (is_bad_overlap) { const bool overlap_check = std::ranges::all_of(image->overlapping_images, [&](const ImageId& overlap_id) { auto& overlap = slot_images[overlap_id]; - return (overlap.frame_tick >= image->frame_tick) && - (overlap.modification_tick > image->modification_tick); + return overlap.frame_tick >= image->frame_tick; }); if (!overlap_check) { ++deletion_iterator; @@ -420,23 +428,20 @@ void TextureCache

::TickFrame() { } } if (!is_bad_overlap && must_download) { - if (is_alias) { - const bool alias_check = - std::ranges::all_of(image->aliased_images, [&](const AliasedImage& alias) { - auto& alias_image = slot_images[alias.id]; - return (alias_image.frame_tick >= image->frame_tick) && - (alias_image.modification_tick > image->modification_tick); - }); - if (!alias_check) { - ++deletion_iterator; - continue; - } + const bool alias_check = + std::ranges::none_of(image->aliased_images, [&](const AliasedImage& alias) { + auto& alias_image = slot_images[alias.id]; + return (alias_image.frame_tick < image->frame_tick) || + (alias_image.modification_tick < image->modification_tick); + }); + + if (alias_check) { + auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes); + const auto copies = FullDownloadCopies(image->info); + image->DownloadMemory(map, copies); + runtime.Finish(); + SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span); } - auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes); - const auto copies = FullDownloadCopies(image->info); - image->DownloadMemory(map, copies); - runtime.Finish(); - SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span); } if (True(image->flags & ImageFlagBits::Tracked)) { UntrackImage(*image); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 916a227241..57843ac5a8 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -817,6 +817,7 @@ void Config::ReadRendererValues() { QStringLiteral("use_asynchronous_shaders"), false); ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), true); + ReadSettingGlobal(Settings::values.use_caches_gc, QStringLiteral("use_caches_gc"), false); ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0); @@ -1401,6 +1402,7 @@ void Config::SaveRendererValues() { Settings::values.use_asynchronous_shaders, false); WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true); + WriteSettingGlobal(QStringLiteral("use_caches_gc"), Settings::values.use_caches_gc, false); // Cast to double because Qt's written float values are not human-readable WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index 35bf9c6be5..a9e611125d 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -30,6 +30,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() { ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue()); + ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue()); ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); if (Settings::IsConfiguringGlobal()) { @@ -62,6 +63,8 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() { ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders, ui->use_asynchronous_shaders, use_asynchronous_shaders); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_caches_gc, ui->use_caches_gc, + use_caches_gc); ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, ui->use_fast_gpu_time, use_fast_gpu_time); @@ -101,6 +104,7 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { ui->use_asynchronous_shaders->setEnabled( Settings::values.use_asynchronous_shaders.UsingGlobal()); ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); + ui->use_caches_gc->setEnabled(Settings::values.use_caches_gc.UsingGlobal()); ui->anisotropic_filtering_combobox->setEnabled( Settings::values.max_anisotropy.UsingGlobal()); @@ -115,6 +119,8 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() { use_asynchronous_shaders); ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time, Settings::values.use_fast_gpu_time, use_fast_gpu_time); + ConfigurationShared::SetColoredTristate(ui->use_caches_gc, Settings::values.use_caches_gc, + use_caches_gc); ConfigurationShared::SetColoredComboBox( ui->gpu_accuracy, ui->label_gpu_accuracy, static_cast(Settings::values.gpu_accuracy.GetValue(true))); diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index e61b571c7f..9148aacf24 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -38,4 +38,5 @@ private: ConfigurationShared::CheckState use_assembly_shaders; ConfigurationShared::CheckState use_asynchronous_shaders; ConfigurationShared::CheckState use_fast_gpu_time; + ConfigurationShared::CheckState use_caches_gc; }; diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui index 846a30586d..3566e9bfaf 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.ui +++ b/src/yuzu/configuration/configure_graphics_advanced.ui @@ -103,6 +103,16 @@ + + + + Enables garbage collection for the GPU caches, this will try to keep VRAM within 3-4Gb and flush least used textures/buffers. This option may be unsafe on a few games + + + Enable GPU caches garbage collection (unsafe) + + + diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index efa1b1d18d..8399190626 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -227,6 +227,10 @@ use_asynchronous_gpu_emulation = # 0: Off, 1 (default): On use_vsync = +# Whether to use garbage collection or not. +# 0 (default): Off, 1: On +use_caches_gc = + # The clear color for the renderer. What shows up on the sides of the bottom screen. # Must be in range of 0.0-1.0. Defaults to 1.0 for all. bg_red =