diff --git a/rpcs3/Emu/RSX/Common/surface_store.h b/rpcs3/Emu/RSX/Common/surface_store.h index 54d0b82848..ffc4ad21e2 100644 --- a/rpcs3/Emu/RSX/Common/surface_store.h +++ b/rpcs3/Emu/RSX/Common/surface_store.h @@ -1075,14 +1075,24 @@ namespace rsx { return false; } - else + else if (m_active_memory_used > (max_safe_memory * 3) / 2) { rsx_log.warning("Surface cache is using too much memory! (%dM)", m_active_memory_used / 0x100000); - return true; } + else + { + rsx_log.trace("Surface cache is using too much memory! (%dM)", m_active_memory_used / 0x100000); + } + + return true; } - bool handle_memory_pressure(command_list_type cmd, problem_severity /*severity*/) + virtual bool can_collapse_surface(const surface_storage_type&) + { + return true; + } + + virtual bool handle_memory_pressure(command_list_type cmd, problem_severity severity) { auto process_list_function = [&](std::unordered_map& data) { @@ -1092,18 +1102,22 @@ namespace rsx if (surface->dirty()) { // Force memory barrier to release some resources - surface->memory_barrier(cmd, rsx::surface_access::shader_read); + if (can_collapse_surface(It->second)) + { + // NOTE: Do not call memory_barrier under fatal conditions as it can create allocations! + // It would be safer to leave the resources hanging around and spill them instead + surface->memory_barrier(cmd, rsx::surface_access::memory_read); + } } else if (!surface->test()) { // Remove this invalidate(It->second); It = data.erase(It); + continue; } - else - { - ++It; - } + + ++It; } }; diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp b/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp index 99953b18f6..1921a69688 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.cpp @@ -1,7 +1,128 @@ #include "VKRenderTargets.h" +#include "VKResourceManager.h" namespace vk { + void surface_cache::destroy() + { + invalidate_all(); + invalidated_resources.clear(); + } + + u64 surface_cache::get_surface_cache_memory_quota(u64 total_device_memory) + { + total_device_memory /= 0x100000; + u64 quota = 0; + + if (total_device_memory >= 2048) + { + quota = std::min(6144ull, (total_device_memory * 40) / 100); + } + else if (total_device_memory >= 1024) + { + quota = 768; + } + else if (total_device_memory >= 768) + { + quota = 256; + } + else + { + // Remove upto 128MB but at least aim for half of available VRAM + quota = std::min(128ull, total_device_memory / 2); + } + + return quota * 0x100000; + } + + bool surface_cache::can_collapse_surface(const std::unique_ptr& surface) + { + if (surface->samples() == 1) + { + return true; + } + + // MSAA surface, check if we have the memory for this... + return vk::vmm_determine_memory_load_severity() < rsx::problem_severity::fatal; + } + + bool surface_cache::handle_memory_pressure(vk::command_buffer& cmd, rsx::problem_severity severity) + { + bool any_released = rsx::surface_store::handle_memory_pressure(cmd, severity); + + if (severity >= rsx::problem_severity::fatal) + { + // TODO + } + + return any_released; + } + + void surface_cache::free_invalidated(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)) + { + 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) + { + ensure(rtt->frame_tag != 0); + + if (rtt->has_refs()) + { + // Actively in use, likely for a reading pass. + // Call handle_memory_pressure before calling this method. + return false; + } + + if (memory_pressure >= rsx::problem_severity::severe) + { + 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); + } + } + + if (rtt->frame_tag >= last_finished_frame) + { + // RTT itself still in use by the frame. + return false; + } + + switch (memory_pressure) + { + case rsx::problem_severity::low: + return (rtt->unused_check_count() >= 2); + case rsx::problem_severity::moderate: + return (rtt->unused_check_count() >= 1); + case rsx::problem_severity::severe: + case rsx::problem_severity::fatal: + // We're almost dead anyway. Remove forcefully. + return true; + default: + fmt::throw_exception("Unreachable"); + } + }); + } + + bool surface_cache::is_overallocated() + { + const auto surface_cache_vram_load = vmm_get_application_pool_usage(VMM_ALLOCATION_POOL_SURFACE_CACHE); + const auto surface_cache_allocation_quota = get_surface_cache_memory_quota(get_current_renderer()->get_memory_mapping().device_local_total_bytes); + return (surface_cache_vram_load > surface_cache_allocation_quota); + } + // Get the linear resolve target bound to this surface. Initialize if none exists vk::viewable_image* render_target::get_resolve_target_safe(vk::command_buffer& cmd) { diff --git a/rpcs3/Emu/RSX/VK/VKRenderTargets.h b/rpcs3/Emu/RSX/VK/VKRenderTargets.h index adcd972a60..99238d9590 100644 --- a/rpcs3/Emu/RSX/VK/VKRenderTargets.h +++ b/rpcs3/Emu/RSX/VK/VKRenderTargets.h @@ -6,10 +6,10 @@ #include "VKFormats.h" #include "VKHelpers.h" #include "vkutils/barriers.h" +#include "vkutils/buffer_object.h" #include "vkutils/data_heap.h" #include "vkutils/device.h" #include "vkutils/image.h" -#include "vkutils/memory.h" #include "vkutils/scratch.h" #include @@ -42,6 +42,7 @@ namespace vk public: u64 frame_tag = 0; // frame id when invalidated, 0 if not invalid + using viewable_image::viewable_image; vk::viewable_image* get_surface(rsx::surface_access access_type) override; @@ -78,7 +79,7 @@ namespace vk rsx::surface_color_format format, usz width, usz height, usz pitch, rsx::surface_antialiasing antialias, - vk::render_device &device, vk::command_buffer& cmd) + vk::render_device& device, vk::command_buffer& cmd) { const auto fmt = vk::get_compatible_surface_format(format); VkFormat requested_format = fmt.first; @@ -118,7 +119,7 @@ namespace vk VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, usage_flags, - 0, vk::VMM_ALLOCATION_POOL_SURFACE_CACHE, RSX_FORMAT_CLASS_COLOR); + 0, VMM_ALLOCATION_POOL_SURFACE_CACHE, RSX_FORMAT_CLASS_COLOR); rtt->set_debug_name(fmt::format("RTV @0x%x", address)); rtt->change_layout(cmd, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); @@ -144,7 +145,7 @@ namespace vk rsx::surface_depth_format2 format, usz width, usz height, usz pitch, rsx::surface_antialiasing antialias, - vk::render_device &device, vk::command_buffer& cmd) + vk::render_device& device, vk::command_buffer& cmd) { const VkFormat requested_format = vk::get_compatible_depth_surface_format(device.get_formats_support(), format); VkImageUsageFlags usage_flags = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; @@ -179,7 +180,7 @@ namespace vk VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_TILING_OPTIMAL, usage_flags, - 0, vk::VMM_ALLOCATION_POOL_SURFACE_CACHE, rsx::classify_format(format)); + 0, VMM_ALLOCATION_POOL_SURFACE_CACHE, rsx::classify_format(format)); ds->set_debug_name(fmt::format("DSV @0x%x", address)); ds->change_layout(cmd, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); @@ -187,7 +188,7 @@ namespace vk ds->set_format(format); ds->set_aa_mode(antialias); ds->sample_layout = sample_layout; - ds->memory_usage_flags= rsx::surface_usage_flags::attachment; + ds->memory_usage_flags = rsx::surface_usage_flags::attachment; ds->state_flags = rsx::surface_state_flags::erase_bkgnd; ds->native_component_map = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_R }; ds->native_pitch = static_cast(width) * get_format_block_size_in_bytes(format) * ds->samples_x; @@ -221,7 +222,7 @@ namespace vk VK_IMAGE_TILING_OPTIMAL, ref->info.usage, ref->info.flags, - vk::VMM_ALLOCATION_POOL_SURFACE_CACHE, + VMM_ALLOCATION_POOL_SURFACE_CACHE, ref->format_class()); sink->add_ref(); @@ -274,7 +275,7 @@ namespace vk surface->get_surface_height() >= height); } - static void prepare_surface_for_drawing(vk::command_buffer& cmd, vk::render_target *surface) + static void prepare_surface_for_drawing(vk::command_buffer& cmd, vk::render_target* surface) { if (surface->aspect() == VK_IMAGE_ASPECT_COLOR_BIT) { @@ -292,12 +293,12 @@ namespace vk static void prepare_surface_for_sampling(vk::command_buffer& /*cmd*/, vk::render_target* /*surface*/) {} - static bool surface_is_pitch_compatible(const std::unique_ptr &surface, usz pitch) + static bool surface_is_pitch_compatible(const std::unique_ptr& surface, usz pitch) { return surface->rsx_pitch == pitch; } - static void invalidate_surface_contents(vk::command_buffer& /*cmd*/, vk::render_target *surface, u32 address, usz pitch) + static void invalidate_surface_contents(vk::command_buffer& /*cmd*/, vk::render_target* surface, u32 address, usz pitch) { surface->rsx_pitch = static_cast(pitch); surface->queue_tag(address); @@ -307,7 +308,7 @@ namespace vk surface->raster_type = rsx::surface_raster_type::linear; } - static void notify_surface_invalidated(const std::unique_ptr &surface) + static void notify_surface_invalidated(const std::unique_ptr& surface) { surface->frame_tag = vk::get_current_frame_id(); if (!surface->frame_tag) surface->frame_tag = 1; @@ -324,14 +325,14 @@ namespace vk static void notify_surface_persist(const std::unique_ptr& /*surface*/) {} - static void notify_surface_reused(const std::unique_ptr &surface) + static void notify_surface_reused(const std::unique_ptr& surface) { surface->state_flags |= rsx::surface_state_flags::erase_bkgnd; surface->add_ref(); } static bool int_surface_matches_properties( - const std::unique_ptr &surface, + const std::unique_ptr& surface, VkFormat format, usz width, usz height, rsx::surface_antialiasing antialias, @@ -349,7 +350,7 @@ namespace vk } static bool surface_matches_properties( - const std::unique_ptr &surface, + const std::unique_ptr& surface, rsx::surface_color_format format, usz width, usz height, rsx::surface_antialiasing antialias, @@ -360,7 +361,7 @@ namespace vk } static bool surface_matches_properties( - const std::unique_ptr &surface, + const std::unique_ptr& surface, rsx::surface_depth_format2 format, usz width, usz height, rsx::surface_antialiasing antialias, @@ -371,43 +372,22 @@ namespace vk return int_surface_matches_properties(surface, vk_format, width, height, antialias, check_refs); } - static vk::render_target *get(const std::unique_ptr &tex) + static vk::render_target* get(const std::unique_ptr& tex) { return tex.get(); } }; - struct surface_cache : public rsx::surface_store + class surface_cache : public rsx::surface_store { - void destroy() - { - invalidate_all(); - invalidated_resources.clear(); - } + private: + u64 get_surface_cache_memory_quota(u64 total_device_memory); - void free_invalidated(vk::command_buffer& cmd) - { - // Do not allow more than 256M of RSX memory to be used by RTTs - if (check_memory_usage(256 * 0x100000)) - { - if (!cmd.is_recording()) - { - cmd.begin(); - } - - handle_memory_pressure(cmd, rsx::problem_severity::moderate); - } - - const u64 last_finished_frame = vk::get_last_completed_frame_id(); - invalidated_resources.remove_if([&](std::unique_ptr &rtt) - { - ensure(rtt->frame_tag != 0); - - if (rtt->unused_check_count() >= 2 && rtt->frame_tag < last_finished_frame) - return true; - - return false; - }); - } + public: + void destroy(); + bool is_overallocated(); + bool can_collapse_surface(const std::unique_ptr& surface) 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); }; }