video_core: Implement thread-safe Vulkan renderer teardown

This commit is contained in:
collecting
2026-05-07 03:53:11 -04:00
parent ab583abedf
commit 660becc759
4 changed files with 60 additions and 2 deletions
+5 -1
View File
@@ -41,7 +41,11 @@ struct GPU::Impl {
shader_notify{std::make_unique<VideoCore::ShaderNotify>()}, is_async{is_async_},
gpu_thread{system_, is_async_}, scheduler{std::make_unique<Control::Scheduler>(gpu)} {}
~Impl() = default;
~Impl() {
if (rasterizer) {
rasterizer->Shutdown();
}
}
std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) {
auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id);
+3
View File
@@ -38,6 +38,9 @@ class RasterizerInterface {
public:
virtual ~RasterizerInterface() = default;
/// Prepare the rasterizer for shutdown
virtual void Shutdown() {}
/// Dispatches a draw invocation
virtual void Draw(bool is_indexed, u32 instance_count) = 0;
@@ -205,7 +205,12 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra
});
}
RasterizerVulkan::~RasterizerVulkan() {
void RasterizerVulkan::Shutdown() {
if (is_shutting_down.exchange(true)) {
return;
}
std::unique_lock exclusive_guard{shutdown_mutex};
// 1. Tell the GPU to finish current work
scheduler.Finish();
@@ -213,6 +218,10 @@ RasterizerVulkan::~RasterizerVulkan() {
// This ensures VkBuffer/VkImage handles are gone before the memory they sit on is freed
buffer_cache_runtime.Finish();
texture_cache_runtime.Finish();
}
RasterizerVulkan::~RasterizerVulkan() {
Shutdown();
// 3. Clear the Staging Pool slabs
staging_pool.TriggerCacheRelease(MemoryUsage::Upload);
@@ -225,6 +234,11 @@ RasterizerVulkan::~RasterizerVulkan() {
template <typename Func>
void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) {
return;
}
SCOPE_EXIT {
gpu.TickWork();
};
@@ -500,6 +514,9 @@ void RasterizerVulkan::Clear(u32 layer_count) {
}
void RasterizerVulkan::DispatchCompute() {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) return;
// Skip first 2 dispatches for Marvel Cosmic Invasion to fix boot issues
if (program_id == UICommon::TitleID::MarvelCosmicInvasion) {
static u32 dispatch_count = 0;
@@ -564,6 +581,9 @@ void Vulkan::RasterizerVulkan::DisableGraphicsUniformBuffer(size_t stage, u32 in
void RasterizerVulkan::FlushAll() {}
void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType which) {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) return;
if (addr == 0 || size == 0) {
return;
}
@@ -581,6 +601,9 @@ void RasterizerVulkan::FlushRegion(DAddr addr, u64 size, VideoCommon::CacheType
}
bool RasterizerVulkan::MustFlushRegion(DAddr addr, u64 size, VideoCommon::CacheType which) {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) return false;
if ((True(which & VideoCommon::CacheType::BufferCache))) {
std::scoped_lock lock{buffer_cache.mutex};
if (buffer_cache.IsRegionGpuModified(addr, size)) {
@@ -599,6 +622,15 @@ bool RasterizerVulkan::MustFlushRegion(DAddr addr, u64 size, VideoCommon::CacheT
}
VideoCore::RasterizerDownloadArea RasterizerVulkan::GetFlushArea(DAddr addr, u64 size) {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) {
return {
.start_address = Common::AlignDown(addr, Core::DEVICE_PAGESIZE),
.end_address = Common::AlignUp(addr + size, Core::DEVICE_PAGESIZE),
.preemtive = true,
};
}
{
std::scoped_lock lock{texture_cache.mutex};
auto area = texture_cache.GetFlushArea(addr, size);
@@ -615,6 +647,9 @@ VideoCore::RasterizerDownloadArea RasterizerVulkan::GetFlushArea(DAddr addr, u64
}
void RasterizerVulkan::InvalidateRegion(DAddr addr, u64 size, VideoCommon::CacheType which) {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) return;
if (addr == 0 || size == 0) {
return;
}
@@ -849,6 +884,9 @@ u64 RasterizerVulkan::GetStagingMemoryUsage() const {
}
void RasterizerVulkan::TriggerMemoryGC() {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) return;
std::scoped_lock lock{texture_cache.mutex, buffer_cache.mutex};
texture_cache.TriggerGarbageCollection();
buffer_cache.TriggerGarbageCollection();
@@ -862,6 +900,9 @@ bool RasterizerVulkan::AccelerateConditionalRendering() {
bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src,
const Tegra::Engines::Fermi2D::Surface& dst,
const Tegra::Engines::Fermi2D::Config& copy_config) {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) return false;
std::scoped_lock lock{texture_cache.mutex};
return texture_cache.BlitImage(dst, src, copy_config);
}
@@ -872,6 +913,9 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerVulkan::AccessAccelerateDMA()
void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
std::span<const u8> memory) {
std::shared_lock shared_guard{shutdown_mutex};
if (is_shutting_down) return;
auto cpu_addr = gpu_memory->GpuToCpuAddress(address);
if (!cpu_addr) [[unlikely]] {
gpu_memory->WriteBlock(address, memory.data(), copy_size);
@@ -4,6 +4,8 @@
#pragma once
#include <array>
#include <atomic>
#include <shared_mutex>
#include <boost/container/static_vector.hpp>
@@ -82,6 +84,8 @@ public:
StateTracker& state_tracker_, Scheduler& scheduler_);
~RasterizerVulkan() override;
void Shutdown() override;
void Draw(bool is_indexed, u32 instance_count) override;
void DrawIndirect() override;
void DrawTexture() override;
@@ -223,6 +227,9 @@ private:
boost::container::static_vector<VkSampler, MAX_TEXTURES> sampler_handles;
u32 draw_counter = 0;
std::shared_mutex shutdown_mutex;
std::atomic_bool is_shutting_down{false};
};
} // namespace Vulkan