From 12f213ffad2be23d363387276af078ea00384c50 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 1 Jun 2023 02:47:08 +0300 Subject: [PATCH] rsx: Trim the number of in-flight invalidated resources (temp cache) - This drastically improves memory allocation behavior. Holding too many invalidated resources can lead to a cascading overallocation error as old resources hold refs to even older resources and nothing gets deleted. --- rpcs3/Emu/RSX/Common/surface_store.h | 65 ++++++++++++++++++++++++++-- rpcs3/Emu/RSX/GL/GLPresent.cpp | 2 +- rpcs3/Emu/RSX/GL/GLRenderTargets.h | 8 +--- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 2 +- rpcs3/Emu/RSX/VK/VKPresent.cpp | 2 +- rpcs3/Emu/RSX/VK/VKRenderTargets.cpp | 20 +++------ rpcs3/Emu/RSX/VK/VKRenderTargets.h | 2 +- 7 files changed, 74 insertions(+), 27 deletions(-) diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index 94a0864a87..63f2e185f9 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -74,6 +74,7 @@ namespace rsx std::vector superseded_surfaces; std::list invalidated_resources; + const u64 max_invalidated_resources_count = 256ull; u64 cache_tag = 1ull; // Use 1 as the start since 0 is default tag on new surfaces u64 write_tag = 1ull; @@ -1361,7 +1362,25 @@ namespace rsx return true; } - virtual bool handle_memory_pressure(command_list_type cmd, problem_severity severity) + void trim_invalidated_resources(command_list_type cmd, problem_severity severity) + { + // It is possible to have stale invalidated resources holding references to other invalidated resources. + // This can bloat the VRAM usage significantly especially if the references are never collapsed. + for (auto& surface : invalidated_resources) + { + if (!surface->has_refs() || surface->old_contents.empty()) + { + continue; + } + + if (can_collapse_surface(surface, severity)) + { + surface->memory_barrier(cmd, rsx::surface_access::transfer_read); + } + } + } + + void collapse_dirty_surfaces(command_list_type cmd, problem_severity severity) { auto process_list_function = [&](surface_ranged_map& data, const utils::address_range& range) { @@ -1390,14 +1409,54 @@ namespace rsx } }; + process_list_function(m_render_targets_storage, m_render_targets_memory_range); + process_list_function(m_depth_stencil_storage, m_depth_stencil_memory_range); + } + + virtual bool handle_memory_pressure(command_list_type cmd, problem_severity severity) + { ensure(severity >= rsx::problem_severity::moderate); const auto old_usage = m_active_memory_used; // Try and find old surfaces to remove - process_list_function(m_render_targets_storage, m_render_targets_memory_range); - process_list_function(m_depth_stencil_storage, m_depth_stencil_memory_range); + collapse_dirty_surfaces(cmd, severity); + + // Check invalidated resources as they can have long dependency chains + if (invalidated_resources.size() > max_invalidated_resources_count || + severity >= rsx::problem_severity::severe) + { + trim_invalidated_resources(cmd, severity); + } return (m_active_memory_used < old_usage); } + + void run_cleanup_internal( + command_list_type cmd, + rsx::problem_severity memory_pressure, + u32 max_surface_store_memory_mb, + std::function pre_task_callback) + { + if (check_memory_usage(max_surface_store_memory_mb * 0x100000)) + { + pre_task_callback(cmd); + + const auto severity = std::max(memory_pressure, rsx::problem_severity::moderate); + handle_memory_pressure(cmd, severity); + } + else if (invalidated_resources.size() > max_invalidated_resources_count) + { + pre_task_callback(cmd); + + // Check invalidated resources as they can have long dependency chains + trim_invalidated_resources(cmd, memory_pressure); + + if ((invalidated_resources.size() + 16u) > max_invalidated_resources_count) + { + // We didn't release enough resources, scan the active RTTs as well + collapse_dirty_surfaces(cmd, memory_pressure); + } + } + } }; } diff --git a/rpcs3/Emu/RSX/GL/GLPresent.cpp b/rpcs3/Emu/RSX/GL/GLPresent.cpp index 85d373b929..e9544a054e 100644 --- a/rpcs3/Emu/RSX/GL/GLPresent.cpp +++ b/rpcs3/Emu/RSX/GL/GLPresent.cpp @@ -401,7 +401,7 @@ void GLGSRender::flip(const rsx::display_flip_info_t& info) m_gl_texture_cache.on_frame_end(); m_vertex_cache->purge(); - auto removed_textures = m_rtts.free_invalidated(cmd); + auto removed_textures = m_rtts.trim(cmd); m_framebuffer_cache.remove_if([&](auto& fbo) { if (fbo.unused_check_count() >= 2) return true; // Remove if stale diff --git a/rpcs3/Emu/RSX/GL/GLRenderTargets.h b/rpcs3/Emu/RSX/GL/GLRenderTargets.h index 60dd4b6104..0a4d1f8afd 100644 --- a/rpcs3/Emu/RSX/GL/GLRenderTargets.h +++ b/rpcs3/Emu/RSX/GL/GLRenderTargets.h @@ -390,13 +390,9 @@ struct gl_render_targets : public rsx::surface_store invalidated_resources.clear(); } - std::vector free_invalidated(gl::command_context& cmd) + std::vector trim(gl::command_context& cmd) { - // Do not allow more than 256M of RSX memory to be used by RTTs - if (check_memory_usage(256 * 0x100000)) - { - handle_memory_pressure(cmd, rsx::problem_severity::moderate); - } + run_cleanup_internal(cmd, rsx::problem_severity::moderate, 256, [](gl::command_context&) {}); std::vector removed; invalidated_resources.remove_if([&](auto &rtt) diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index abc4ff3c5f..a008806947 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -1151,7 +1151,7 @@ bool VKGSRender::on_vram_exhausted(rsx::problem_severity severity) if (m_rtts.handle_memory_pressure(*m_current_command_buffer, severity)) { surface_cache_relieved = true; - m_rtts.free_invalidated(*m_current_command_buffer, severity); + m_rtts.trim(*m_current_command_buffer, severity); } const bool any_cache_relieved = (texture_cache_relieved || surface_cache_relieved); diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index f2289b9927..202499cc6d 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -125,7 +125,7 @@ void VKGSRender::advance_queued_frames() vk::vmm_check_memory_usage(); // m_rtts storage is double buffered and should be safe to tag on frame boundary - m_rtts.free_invalidated(*m_current_command_buffer, vk::vmm_determine_memory_load_severity()); + m_rtts.trim(*m_current_command_buffer, vk::vmm_determine_memory_load_severity()); // Texture cache is also double buffered to prevent use-after-free m_texture_cache.on_frame_end(); diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp b/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp index f87fcea19c..6df1cde1bd 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp @@ -168,20 +168,15 @@ namespace vk return any_released; } - void surface_cache::free_invalidated(vk::command_buffer& cmd, rsx::problem_severity memory_pressure) + void surface_cache::trim(vk::command_buffer& cmd, rsx::problem_severity memory_pressure) { - // Do not allow more than 300M of RSX memory to be used by RTTs. - // The actual boundary is 256M but we need to give some overallocation for performance reasons. - if (check_memory_usage(300 * 0x100000)) + run_cleanup_internal(cmd, rsx::problem_severity::moderate, 300, [](vk::command_buffer& cmd) { if (!cmd.is_recording()) { cmd.begin(); } - - const auto severity = std::max(memory_pressure, rsx::problem_severity::moderate); - handle_memory_pressure(cmd, severity); - } + }); const u64 last_finished_frame = vk::get_last_completed_frame_id(); invalidated_resources.remove_if([&](std::unique_ptr& rtt) @@ -195,13 +190,10 @@ namespace vk return false; } - if (memory_pressure >= rsx::problem_severity::severe) + if (rtt->resolve_surface && memory_pressure >= rsx::problem_severity::moderate) { - if (rtt->resolve_surface) - { - // We do not need to keep resolve targets around if things are bad. - vk::get_resource_manager()->dispose(rtt->resolve_surface); - } + // We do not need to keep resolve targets around. + vk::get_resource_manager()->dispose(rtt->resolve_surface); } if (rtt->frame_tag >= last_finished_frame) diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index 81aea5858f..e1dfe0142d 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -642,7 +642,7 @@ namespace vk bool is_overallocated(); bool can_collapse_surface(const std::unique_ptr& surface, rsx::problem_severity severity) override; bool handle_memory_pressure(vk::command_buffer& cmd, rsx::problem_severity severity) override; - void free_invalidated(vk::command_buffer& cmd, rsx::problem_severity memory_pressure); + void trim(vk::command_buffer& cmd, rsx::problem_severity memory_pressure); }; } //h