vk: Run GC on the driver manager thread

This commit is contained in:
kd-11
2026-08-13 11:39:34 +03:00
committed by kd-11
parent 5bd7fd7817
commit c27b38f300
3 changed files with 79 additions and 34 deletions
+2 -6
View File
@@ -412,6 +412,7 @@ VKGSRender::VKGSRender(utils::serial* ar) noexcept : GSRender(ar)
{
// Initialize dependencies
g_fxo->need<rsx::dma_manager>();
g_fxo->need<vk::driver_manager_thread>();
if (!m_instance.create("RPCS3"))
{
@@ -1028,6 +1029,7 @@ bool VKGSRender::on_vram_exhausted(rsx::problem_severity severity)
// Hard sync before trying to evict anything. This guarantees no UAF crashes in the driver.
// As a bonus, we also get a free gc pass
flush_command_queue(true, true);
g_fxo->get<vk::driver_manager_thread>().drain();
if (m_texture_cache.is_overallocated())
{
@@ -2689,12 +2691,6 @@ void VKGSRender::renderctl(u32 request_code, void* args)
free(packet);
break;
}
case vk::rctrl_run_gc:
{
auto eid = reinterpret_cast<u64>(args);
vk::on_event_completed(eid, true);
break;
}
default:
rsx::thread::renderctl(request_code, args);
}
+38 -14
View File
@@ -10,6 +10,7 @@ namespace vk
std::unordered_map<uptr, vmm_allocation_t> allocations;
std::unordered_map<uptr, atomic_t<u64>> memory_usage;
std::unordered_map<vmm_allocation_pool, atomic_t<u64>> pool_usage;
shared_mutex mutex;
void clear()
{
@@ -87,19 +88,13 @@ namespace vk
return g_last_completed_event.load();
}
void on_event_completed(u64 event_id, bool flush)
void on_event_completed(u64 event_id)
{
if (!flush && g_cfg.video.multithreaded_rsx)
g_fxo->get<vk::driver_manager_thread>().notify_completed(event_id);
g_last_completed_event.atomic_op([event_id](u64& value)
{
auto& offloader_thread = g_fxo->get<rsx::dma_manager>();
ensure(!offloader_thread.is_current_thread());
offloader_thread.backend_ctrl(rctrl_run_gc, reinterpret_cast<void*>(event_id));
return;
}
g_resource_manager.eid_completed(event_id);
g_last_completed_event = std::max(event_id, g_last_completed_event.load());
value = std::max(event_id, value);
});
}
void print_debug_markers()
@@ -120,6 +115,8 @@ namespace vk
auto key = reinterpret_cast<uptr>(handle);
const vmm_allocation_t info = { memory_size, memory_type, pool };
std::lock_guard lock(g_vmm_stats.mutex);
if (const auto ins = g_vmm_stats.allocations.insert_or_assign(key, info);
!ins.second)
{
@@ -140,6 +137,8 @@ namespace vk
void vmm_notify_memory_freed(void* handle)
{
std::lock_guard lock(g_vmm_stats.mutex);
auto key = reinterpret_cast<uptr>(handle);
if (auto found = g_vmm_stats.allocations.find(key);
found != g_vmm_stats.allocations.end())
@@ -153,12 +152,14 @@ namespace vk
void vmm_reset()
{
std::lock_guard lock(g_vmm_stats.mutex);
g_vmm_stats.clear();
g_event_ctr = 0;
g_last_completed_event = 0;
}
u64 vmm_get_application_memory_usage(const memory_type_info& memory_type)
u64 vmm_get_application_memory_usage_impl(const memory_type_info& memory_type)
{
u64 result = 0;
for (const auto& memory_type_index : memory_type)
@@ -175,13 +176,32 @@ namespace vk
return result;
}
u64 vmm_get_application_memory_usage(const memory_type_info& memory_type)
{
reader_lock lock(g_vmm_stats.mutex);
return vmm_get_application_memory_usage_impl(memory_type);
}
u64 vmm_get_application_pool_usage_impl(vmm_allocation_pool pool)
{
if (auto found = g_vmm_stats.pool_usage.find(pool);
found != g_vmm_stats.pool_usage.end())
{
return found->second;
}
return 0ull;
}
u64 vmm_get_application_pool_usage(vmm_allocation_pool pool)
{
return g_vmm_stats.pool_usage[pool];
reader_lock lock(g_vmm_stats.mutex);
return vmm_get_application_pool_usage_impl(pool);
}
rsx::problem_severity vmm_determine_memory_load_severity()
{
reader_lock lock(g_vmm_stats.mutex);
const auto vmm_load = get_current_mem_allocator()->get_memory_usage();
rsx::problem_severity load_severity = rsx::problem_severity::low;
@@ -211,7 +231,7 @@ namespace vk
// 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);
const auto local_memory_usage = vmm_get_application_memory_usage_impl(mem_info.device_local);
constexpr u64 _1M = 0x100000;
const auto res_scale = rsx::get_current_renderer()->resolution_scaling_config.scale_factor();
@@ -264,12 +284,16 @@ namespace vk
void vmm_notify_object_allocated(vmm_allocation_pool pool)
{
std::lock_guard lock(g_vmm_stats.mutex);
ensure(pool >= VMM_ALLOCATION_POOL_SAMPLER);
g_vmm_stats.pool_usage[pool]++;
}
void vmm_notify_object_freed(vmm_allocation_pool pool)
{
std::lock_guard lock(g_vmm_stats.mutex);
ensure(pool >= VMM_ALLOCATION_POOL_SAMPLER);
g_vmm_stats.pool_usage[pool]--;
}
+39 -14
View File
@@ -6,7 +6,7 @@
#include "Utilities/mutex.h"
#include <deque>
#include <list>
#include <memory>
namespace vk
@@ -14,7 +14,7 @@ namespace vk
u64 get_event_id();
u64 current_event_id();
u64 last_completed_event_id();
void on_event_completed(u64 event_id, bool flush = false);
void on_event_completed(u64 event_id);
struct eid_scope_t
{
@@ -52,8 +52,8 @@ namespace vk
private:
sampler_pool_t m_sampler_pool;
std::deque<eid_scope_t> m_eid_map;
shared_mutex m_eid_map_lock;
std::list<eid_scope_t> m_eid_map;
mutable shared_mutex m_eid_map_lock;
std::vector<std::function<void()>> m_exit_handlers;
@@ -61,7 +61,6 @@ namespace vk
{
const auto eid = current_event_id();
{
std::lock_guard lock(m_eid_map_lock);
if (m_eid_map.empty() || m_eid_map.back().eid != eid)
{
m_eid_map.emplace_back(eid);
@@ -88,7 +87,12 @@ namespace vk
void flush()
{
m_eid_map.clear();
std::list<eid_scope_t> dispose_queue;
{
std::lock_guard lock(m_eid_map_lock);
dispose_queue.splice(dispose_queue.begin(), m_eid_map);
}
m_sampler_pool.clear();
}
@@ -135,11 +139,13 @@ namespace vk
void dispose(vk::disposable_t& disposable) override
{
std::lock_guard lock(m_eid_map_lock);
get_current_eid_scope().m_disposables.emplace_back(std::move(disposable));
}
inline void dispose(std::unique_ptr<vk::gpu_debug_marker>& object)
{
std::lock_guard lock(m_eid_map_lock);
// Special case as we may need to read these out.
// FIXME: We can manage these markers better and remove this exception.
get_current_eid_scope().m_debug_markers.emplace_back(std::move(object));
@@ -154,32 +160,51 @@ namespace vk
void push_down_current_scope()
{
std::lock_guard lock(m_eid_map_lock);
get_current_eid_scope().eid++;
}
void eid_completed(u64 eid)
{
while (!m_eid_map.empty())
// We move scope objects into this so that they're destroyed outside the lock.
// These objects should outlive the lock, the idea is to release the main thread while the caller handles the callback spam.
std::list<eid_scope_t> discarded_scopes;
{
const auto& scope = m_eid_map.front();
if (scope.eid > eid)
reader_lock lock(m_eid_map_lock);
// First, scan to find the newest item that is going out of scope
auto newest_it = m_eid_map.begin();
while (newest_it != m_eid_map.end() && newest_it->eid <= eid)
{
break;
newest_it++;
}
eid_scope_t tmp(0);
// Any hits?
if (newest_it == m_eid_map.begin())
{
std::lock_guard lock(m_eid_map_lock);
m_eid_map.front().swap(tmp);
m_eid_map.pop_front();
return;
}
// Take all of the entries in one sweep
lock.upgrade();
// Post-upgrade iterators should be safe
discarded_scopes.splice(
discarded_scopes.end(),
m_eid_map,
m_eid_map.begin(),
newest_it);
}
// Cleanup runs here on the discard pile
}
void trim();
std::vector<const gpu_debug_marker*> gather_debug_markers() const
{
reader_lock lock(m_eid_map_lock);
std::vector<const gpu_debug_marker*> result;
for (const auto& scope : m_eid_map)
{