mirror of
https://github.com/ARMSX2/ARMSX3.git
synced 2026-08-24 16:58:52 -07:00
FrameGen: release its Vulkan objects before the device that owns them
Frame generation's state is file-scope global and had no teardown path. shutdown() only finalised the old dlopen'd library, and release_shared_images() was never called at all, so at emulation stop the fences, command pool, command buffers, allocator, queue and the whole pass chain stayed live as children of a destroyed VkDevice. The reachable consequence is worse than the leak. On the NEXT boot in the same process g_native.valid() was still true, so build_native_stack() was skipped and generate() waited on a fence from the dead device, reset freed command buffers and submitted to its queue. g_shared_w/h still matched too, so the output images were not recreated either. That is boot -> stop -> boot, and it is deterministic rather than intermittent. It is the likeliest explanation for frame generation working in one game and then failing immediately in the next one launched without restarting the app, which is exactly how it was reported. release_device_resources() destroys the stack and the images and resets the state that gates rebuilding, including g_disabled -- a new device may not fail where the last one did. Called from ~VKGSRender after vkDeviceWaitIdle, while the device is still alive. Found by a Vulkan lifetime audit. Not verified on device; the hardware was unavailable.
This commit is contained in:
@@ -831,6 +831,7 @@ namespace vk::frame_gen
|
||||
}
|
||||
|
||||
void release_shared_images() {}
|
||||
void release_device_resources() {}
|
||||
void commit_capture() {}
|
||||
#endif
|
||||
|
||||
@@ -916,6 +917,46 @@ namespace vk::frame_gen
|
||||
|
||||
}
|
||||
|
||||
void release_device_resources()
|
||||
{
|
||||
// Everything here is a child of the VkDevice and MUST die before it does.
|
||||
//
|
||||
// Nothing called this. shutdown() only finalised the old dlopen'd library, so at
|
||||
// emulation stop the fences, command pool, command buffers, allocator, queue and the
|
||||
// whole pass chain stayed live against a destroyed device -- and worse, on the NEXT boot
|
||||
// in the same process g_native.valid() was still true, so the stack was not rebuilt:
|
||||
// generate() then waited on a fence from the dead device and submitted to its queue.
|
||||
//
|
||||
// That is the boot -> stop -> boot path, and it is deterministic. It is the most likely
|
||||
// explanation for frame generation working in one game and failing immediately in the
|
||||
// next one launched without restarting the app.
|
||||
destroy_native_stack();
|
||||
|
||||
for (auto& out : g_shared_out)
|
||||
{
|
||||
out.destroy();
|
||||
}
|
||||
|
||||
|
||||
g_context = -1;
|
||||
g_context_outputs = 0;
|
||||
g_shared_w = 0;
|
||||
g_shared_h = 0;
|
||||
g_captures = 0;
|
||||
g_planned_generations = 0;
|
||||
g_source_image = VK_NULL_HANDLE;
|
||||
g_source_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
g_plan_ready.store(false);
|
||||
g_recorded_capture.store(false);
|
||||
g_fresh_capture.store(false);
|
||||
g_display_fps.store(0.f);
|
||||
|
||||
// A new device may not support what the last one did.
|
||||
g_disabled = false;
|
||||
|
||||
framegen_log.notice("Frame generation device resources released");
|
||||
}
|
||||
|
||||
bool capture_presented_frame(const vk::command_buffer& cmd, const vk::render_device& dev,
|
||||
VkImage src, VkImageLayout src_layout, u32 width, u32 height,
|
||||
u32 guest_width, u32 guest_height)
|
||||
|
||||
@@ -160,6 +160,11 @@ namespace vk::frame_gen
|
||||
// Release the shared images. Safe to call when none exist.
|
||||
void release_shared_images();
|
||||
|
||||
/// Destroy every Vulkan object frame generation owns. MUST be called while the device is
|
||||
/// still alive -- these are all its children, and they also gate whether the next boot
|
||||
/// rebuilds rather than reusing handles from a destroyed device.
|
||||
void release_device_resources();
|
||||
|
||||
// How many frames the current setting asks us to generate between each real pair.
|
||||
// Zero when frame generation is off, unsupported, or has no shaders.
|
||||
u32 generated_frame_count();
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "VKCommonPipelineLayout.h"
|
||||
#include "VKCompute.h"
|
||||
#include "VKGSRender.h"
|
||||
#include "VKFrameGen.h"
|
||||
#include "Emu/RSX/rsx_profiler.h"
|
||||
#include "vkutils/gpu_timer.h"
|
||||
#include "VKHelpers.h"
|
||||
@@ -858,6 +859,11 @@ VKGSRender::~VKGSRender()
|
||||
//Wait for device to finish up with resources
|
||||
vkDeviceWaitIdle(*m_device);
|
||||
|
||||
// Frame generation owns fences, a command pool, an allocator and images, all children of this
|
||||
// device. Released here, while it is still alive: without this they outlived it, and the next
|
||||
// boot in the same process reused the stale handles because the stack still looked valid.
|
||||
vk::frame_gen::release_device_resources();
|
||||
|
||||
// Globals. TODO: Refactor lifetime management
|
||||
if (auto async_scheduler = g_fxo->try_get<vk::AsyncTaskScheduler>())
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user