renderer_vulkan: Use raw surface handles and improve release logic (#75)

Should fix or at least lessen the crashes when leaving runtime emulation on both Android / PC
Also, may have improved the time required to leave the game

Reviewed-on: #75
Co-authored-by: Briar <205427297+icy-briar@users.noreply.github.com>
Co-committed-by: Briar <205427297+icy-briar@users.noreply.github.com>
This commit is contained in:
icy-briar 2025-05-02 18:42:58 +00:00 committed by CamilleLaVey
parent cb8f449dca
commit 559eb0a488
6 changed files with 57 additions and 26 deletions

View file

@ -120,12 +120,13 @@ RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& emu_window,
// Create surface
surface(CreateSurface(instance, render_window.GetWindowInfo())),
managed_surface(MakeManagedSurface(surface, instance, dld)),
device(CreateDevice(instance, dld, *surface)), memory_allocator(device), state_tracker(),
device(CreateDevice(instance, dld, *surface)),
memory_allocator(device), state_tracker(),
scheduler(device, state_tracker),
swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
render_window.GetFramebufferLayout().height),
present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain,
surface),
*surface),
blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler,
PresentFiltersForDisplay),
blit_capture(device_memory, device, memory_allocator, present_manager, scheduler,
@ -136,11 +137,19 @@ RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& emu_window,
scheduler),
hybrid_memory(std::make_unique<HybridMemory>(device, memory_allocator)),
applet_frame() {
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
turbo_mode.emplace(instance, dld);
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });
}
// Release ownership from the old instance and surface
instance.release();
surface.release();
if (Settings::values.renderer_debug) {
debug_messenger.release();
}
// Initialize HybridMemory system
if (Settings::values.use_gpu_memory_manager.GetValue()) {
#if defined(__linux__) || defined(__ANDROID__) || defined(_WIN32)

View file

@ -10,6 +10,7 @@
#include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_surface.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
@ -94,14 +95,14 @@ bool CanBlitToSwapchain(const vk::PhysicalDevice& physical_device, VkFormat form
} // Anonymous namespace
PresentManager::PresentManager(const vk::Instance& instance_,
Core::Frontend::EmuWindow& render_window_, const Device& device_,
MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
Swapchain& swapchain_, vk::SurfaceKHR& surface_)
PresentManager::PresentManager(const vk::Instance& instance_, Core::Frontend::EmuWindow& render_window_,
const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_,
Swapchain& swapchain_, VkSurfaceKHR_T* surface_handle_)
: instance{instance_}, render_window{render_window_}, device{device_},
memory_allocator{memory_allocator_}, scheduler{scheduler_}, swapchain{swapchain_},
surface{surface_}, blit_supported{CanBlitToSwapchain(device.GetPhysical(),
swapchain.GetImageViewFormat())},
surface_handle{surface_handle_},
blit_supported{CanBlitToSwapchain(device.GetPhysical(),
swapchain.GetImageViewFormat())},
use_present_thread{Settings::values.async_presentation.GetValue()} {
SetImageCount();
@ -288,7 +289,7 @@ void PresentManager::PresentThread(std::stop_token token) {
}
void PresentManager::RecreateSwapchain(Frame* frame) {
swapchain.Create(*surface, frame->width, frame->height);
swapchain.Create(surface_handle, frame->width, frame->height); // Pass raw pointer
SetImageCount();
}
@ -306,7 +307,6 @@ void PresentManager::CopyToSwapchain(Frame* frame) {
try {
// Recreate surface and swapchain if needed.
if (requires_recreation) {
surface = CreateSurface(instance, render_window.GetWindowInfo());
RecreateSwapchain(frame);
}

View file

@ -12,6 +12,8 @@
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
struct VkSurfaceKHR_T;
namespace Core::Frontend {
class EmuWindow;
} // namespace Core::Frontend
@ -37,7 +39,7 @@ class PresentManager {
public:
PresentManager(const vk::Instance& instance, Core::Frontend::EmuWindow& render_window,
const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler,
Swapchain& swapchain, vk::SurfaceKHR& surface);
Swapchain& swapchain, VkSurfaceKHR_T* surface_handle);
~PresentManager();
/// Returns the last used presentation frame
@ -71,7 +73,7 @@ private:
MemoryAllocator& memory_allocator;
Scheduler& scheduler;
Swapchain& swapchain;
vk::SurfaceKHR& surface;
VkSurfaceKHR_T* surface_handle;
vk::CommandPool cmdpool;
std::vector<Frame> frames;
std::queue<Frame*> present_queue;

View file

@ -105,23 +105,23 @@ VkCompositeAlphaFlagBitsKHR ChooseAlphaFlags(const VkSurfaceCapabilitiesKHR& cap
} // Anonymous namespace
Swapchain::Swapchain(VkSurfaceKHR surface_, const Device& device_, Scheduler& scheduler_,
Swapchain::Swapchain(VkSurfaceKHR_T* surface_handle_, const Device& device_, Scheduler& scheduler_,
u32 width_, u32 height_)
: surface{surface_}, device{device_}, scheduler{scheduler_} {
Create(surface_, width_, height_);
: surface_handle{surface_handle_}, device{device_}, scheduler{scheduler_} {
Create(surface_handle, width_, height_);
}
Swapchain::~Swapchain() = default;
void Swapchain::Create(VkSurfaceKHR surface_, u32 width_, u32 height_) {
void Swapchain::Create(VkSurfaceKHR_T* surface_handle_, u32 width_, u32 height_) {
is_outdated = false;
is_suboptimal = false;
width = width_;
height = height_;
surface = surface_;
surface_handle = surface_handle_;
const auto physical_device = device.GetPhysical();
const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface)};
const auto capabilities{physical_device.GetSurfaceCapabilitiesKHR(surface_handle)};
if (capabilities.maxImageExtent.width == 0 || capabilities.maxImageExtent.height == 0) {
return;
}
@ -199,8 +199,8 @@ void Swapchain::Present(VkSemaphore render_semaphore) {
void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
const auto physical_device{device.GetPhysical()};
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface);
const auto formats{physical_device.GetSurfaceFormatsKHR(surface_handle)};
const auto present_modes = physical_device.GetSurfacePresentModesKHR(surface_handle);
has_mailbox = std::find(present_modes.begin(), present_modes.end(),
VK_PRESENT_MODE_MAILBOX_KHR) != present_modes.end();
has_imm = std::find(present_modes.begin(), present_modes.end(),
@ -228,7 +228,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.pNext = nullptr,
.flags = 0,
.surface = surface,
.surface = surface_handle,
.minImageCount = requested_image_count,
.imageFormat = surface_format.format,
.imageColorSpace = surface_format.colorSpace,
@ -269,7 +269,7 @@ void Swapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities) {
swapchain_ci.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
}
// Request the size again to reduce the possibility of a TOCTOU race condition.
const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface);
const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface_handle);
swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height);
// Don't add code within this and the swapchain creation.
swapchain = device.GetLogical().CreateSwapchainKHR(swapchain_ci);

View file

@ -8,6 +8,8 @@
#include "common/common_types.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
struct VkSurfaceKHR_T;
namespace Layout {
struct FramebufferLayout;
}
@ -19,12 +21,12 @@ class Scheduler;
class Swapchain {
public:
explicit Swapchain(VkSurfaceKHR surface, const Device& device, Scheduler& scheduler, u32 width,
explicit Swapchain(VkSurfaceKHR_T* surface_handle, const Device& device, Scheduler& scheduler, u32 width,
u32 height);
~Swapchain();
/// Creates (or recreates) the swapchain with a given size.
void Create(VkSurfaceKHR surface, u32 width, u32 height);
void Create(VkSurfaceKHR_T* surface_handle, u32 width, u32 height);
/// Acquires the next image in the swapchain, waits as needed.
bool AcquireNextImage();
@ -108,7 +110,7 @@ private:
bool NeedsPresentModeUpdate() const;
VkSurfaceKHR surface;
VkSurfaceKHR_T* surface_handle;
const Device& device;
Scheduler& scheduler;

View file

@ -430,6 +430,15 @@ public:
return handle != nullptr;
}
/**
* Releases ownership of the managed handle.
* The caller is responsible for managing the lifetime of the returned handle.
* The Handle object becomes invalid after this call.
*/
Type release() noexcept {
return std::exchange(handle, nullptr);
}
protected:
Type handle = nullptr;
OwnerType owner = nullptr;
@ -501,6 +510,15 @@ public:
return handle != nullptr;
}
/**
* Releases ownership of the managed handle.
* The caller is responsible for managing the lifetime of the returned handle.
* The Handle object becomes invalid after this call.
*/
Type release() noexcept {
return std::exchange(handle, nullptr);
}
protected:
Type handle = nullptr;
const Dispatch* dld = nullptr;