diff --git a/rpcs3/Emu/RSX/VK/VKResourceManager.cpp b/rpcs3/Emu/RSX/VK/VKResourceManager.cpp index 5ff1634c71..d115e49ba2 100644 --- a/rpcs3/Emu/RSX/VK/VKResourceManager.cpp +++ b/rpcs3/Emu/RSX/VK/VKResourceManager.cpp @@ -5,8 +5,20 @@ namespace vk { - std::unordered_map g_vmm_allocations; - std::unordered_map> g_vmm_memory_usage; + struct vmm_memory_stats + { + std::unordered_map allocations; + std::unordered_map> memory_usage; + std::unordered_map> pool_usage; + + void clear() + { + allocations.clear(); + memory_usage.clear(); + pool_usage.clear(); + } + } + g_vmm_stats; resource_manager g_resource_manager; atomic_t g_event_ctr; @@ -57,20 +69,22 @@ namespace vk void vmm_notify_memory_allocated(void* handle, u32 memory_type, u64 memory_size, vmm_allocation_pool pool) { auto key = reinterpret_cast(handle); - const vmm_allocation_t info = { memory_size, memory_type }; + const vmm_allocation_t info = { memory_size, memory_type, pool }; - if (const auto ins = g_vmm_allocations.insert_or_assign(key, info); + if (const auto ins = g_vmm_stats.allocations.insert_or_assign(key, info); !ins.second) { rsx_log.error("Duplicate vmm entry with memory handle 0x%llx", key); } - auto& vmm_size = g_vmm_memory_usage[memory_type]; + g_vmm_stats.pool_usage[pool] += memory_size; + + auto& vmm_size = g_vmm_stats.memory_usage[memory_type]; vmm_size += memory_size; if (vmm_size > s_vmm_warn_threshold_size && (vmm_size - memory_size) <= s_vmm_warn_threshold_size) { - rsx_log.warning("Memory type 0x%x has allocated more than %.2fG. Currently allocated %.2fG", + rsx_log.warning("Memory type 0x%x has allocated more than %04.2fG. Currently allocated %04.2fG", memory_type, size_in_GiB(s_vmm_warn_threshold_size), size_in_GiB(vmm_size)); } } @@ -78,19 +92,90 @@ namespace vk void vmm_notify_memory_freed(void* handle) { auto key = reinterpret_cast(handle); - if (auto found = g_vmm_allocations.find(key); - found != g_vmm_allocations.end()) + if (auto found = g_vmm_stats.allocations.find(key); + found != g_vmm_stats.allocations.end()) { const auto& info = found->second; - g_vmm_memory_usage[info.type_index] -= info.size; - g_vmm_allocations.erase(found); + g_vmm_stats.memory_usage[info.type_index] -= info.size; + g_vmm_stats.pool_usage[info.pool] -= info.size; + g_vmm_stats.allocations.erase(found); } } void vmm_reset() { - g_vmm_memory_usage.clear(); - g_vmm_allocations.clear(); + g_vmm_stats.clear(); + g_event_ctr = 0; + g_last_completed_event = 0; + } + + u64 vmm_get_application_memory_usage(u32 memory_type) + { + return g_vmm_stats.memory_usage[memory_type]; + } + + u64 vmm_get_application_pool_usage(vmm_allocation_pool pool) + { + return g_vmm_stats.pool_usage[pool]; + } + + rsx::problem_severity vmm_determine_memory_load_severity() + { + const auto vmm_load = get_current_mem_allocator()->get_memory_usage(); + rsx::problem_severity load_severity = rsx::problem_severity::low; + + // Fragmentation tuning + if (vmm_load < 50.f) + { + get_current_mem_allocator()->set_fastest_allocation_flags(); + } + else if (vmm_load > 75.f) + { + // Avoid fragmentation if we can + get_current_mem_allocator()->set_safest_allocation_flags(); + + if (vmm_load > 95.f) + { + // Drivers will often crash long before returning OUT_OF_DEVICE_MEMORY errors. + load_severity = rsx::problem_severity::fatal; + } + else if (vmm_load > 90.f) + { + load_severity = rsx::problem_severity::severe; + } + else + { + load_severity = rsx::problem_severity::moderate; + } + + // Query actual usage for comparison. Maybe we just have really fragmented memory... + const auto mem_info = get_current_renderer()->get_memory_mapping(); + const auto local_memory_usage = vmm_get_application_memory_usage(mem_info.device_local); + + constexpr u64 _1GB = (1024 * 0x100000); + if (local_memory_usage < (mem_info.device_local_total_bytes / 2) || // Less than 50% VRAM usage OR + (mem_info.device_local_total_bytes - local_memory_usage) > _1GB) // More than 1GiB of unallocated memory + { + // Lower severity to avoid slowing performance too much + load_severity = rsx::problem_severity::low; + } + else if ((mem_info.device_local_total_bytes - local_memory_usage) > 512 * 0x100000) + { + // At least 512MB left, do not overreact + load_severity = rsx::problem_severity::moderate; + } + + if (load_severity >= rsx::problem_severity::moderate) + { + // NOTE: For some reason fmt::format with a sized float followed by percentage sign causes random crashing. + // This is a bug unrelated to this, but explains why we're going with integral percentages here. + const auto application_memory_load = (local_memory_usage * 100) / mem_info.device_local_total_bytes; + rsx_log.warning("Actual device memory used by internal allocations is %lluM (%llu%%)", local_memory_usage / 0x100000, application_memory_load); + rsx_log.warning("Video memory usage is at %d%%. Will attempt to reclaim some resources.", static_cast(vmm_load)); + } + } + + return load_severity; } bool vmm_handle_memory_pressure(rsx::problem_severity severity) @@ -105,21 +190,8 @@ namespace vk void vmm_check_memory_usage() { - const auto vmm_load = get_current_mem_allocator()->get_memory_usage(); - rsx::problem_severity load_severity = rsx::problem_severity::low; - - if (vmm_load > 90.f) - { - rsx_log.warning("Video memory usage exceeding 90%. Will attempt to reclaim resources."); - load_severity = rsx::problem_severity::severe; - } - else if (vmm_load > 75.f) - { - rsx_log.notice("Video memory usage exceeding 75%. Will attempt to reclaim resources."); - load_severity = rsx::problem_severity::moderate; - } - - if (load_severity >= rsx::problem_severity::moderate) + if (const auto load_severity = vmm_determine_memory_load_severity(); + load_severity >= rsx::problem_severity::moderate) { vmm_handle_memory_pressure(load_severity); } diff --git a/rpcs3/Emu/RSX/VK/VKResourceManager.h b/rpcs3/Emu/RSX/VK/VKResourceManager.h index 6db8b3ece7..9c194ce4d8 100644 --- a/rpcs3/Emu/RSX/VK/VKResourceManager.h +++ b/rpcs3/Emu/RSX/VK/VKResourceManager.h @@ -183,6 +183,7 @@ namespace vk { u64 size; u32 type_index; + vmm_allocation_pool pool; }; resource_manager* get_resource_manager();