[vk] Unify RAII in Vulkan (#2679)

This PR consolidates Vulkan RAII on video_core/vulkan_common/vulkan_wrapper.h’s vk::Handle and remove the unused duplicate src/video_core/vulkan_common/vulkan_raii.h, reducing confusion and maintenance. Swapchain now uses RAII‑managed per‑image semaphores and clears them in Destroy(), providing correct present synchronization and automatic cleanup. Expected result: simpler lifetimes, fewer leak risks, and more stable presentation with negligible overhead.

Co-authored-by: Ribbit <ribbit@placeholder.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/2679
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Co-authored-by: Ribbit <ribbit@eden-emu.dev>
Co-committed-by: Ribbit <ribbit@eden-emu.dev>
This commit is contained in:
Ribbit 2025-10-08 04:01:24 +02:00 committed by crueter
parent acd7d792a3
commit db65f10768
Signed by: crueter
GPG key ID: 425ACD2D4830EBC6
35 changed files with 42 additions and 353 deletions

View file

@ -164,15 +164,6 @@ try
PresentFiltersForAppletCapture)
, rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, scheduler) {
// Initialize RAII wrappers after creating the main objects
if (Settings::values.enable_raii.GetValue()) {
managed_instance = MakeManagedInstance(instance, dld);
if (Settings::values.renderer_debug) {
managed_debug_messenger = MakeManagedDebugUtilsMessenger(debug_messenger, instance, dld);
}
managed_surface = MakeManagedSurface(surface, instance, dld);
}
if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) {
turbo_mode.emplace(instance, dld);
scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); });

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -20,7 +23,6 @@
#include "video_core/vulkan_common/vulkan_device.h"
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
#include "video_core/vulkan_common/vulkan_raii.h"
namespace Core::Memory {
class Memory;
@ -78,16 +80,10 @@ private:
// Keep original handles for compatibility with existing code
vk::Instance instance;
// RAII wrapper for instance
ManagedInstance managed_instance;
vk::DebugUtilsMessenger debug_messenger;
// RAII wrapper for debug messenger
ManagedDebugUtilsMessenger managed_debug_messenger;
vk::SurfaceKHR surface;
// RAII wrapper for surface
ManagedSurface managed_surface;
Device device;
MemoryAllocator memory_allocator;

View file

@ -351,6 +351,7 @@ void Swapchain::CreateSemaphores() {
void Swapchain::Destroy() {
frame_index = 0;
present_semaphores.clear();
render_semaphores.clear();
swapchain.reset();
}

View file

@ -1,231 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <utility>
#include <functional>
#include <string>
#include "common/logging/log.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
/**
* RAII wrapper for Vulkan resources.
* Automatically manages the lifetime of Vulkan objects using RAII principles.
*/
template <typename T, typename Owner = void*, typename Dispatch = vk::InstanceDispatch>
class VulkanRaii {
public:
using DeleterFunc = std::function<void(T, const Dispatch&)>;
// Default constructor - creates a null handle
VulkanRaii() : handle{}, deleter{}, dispatch{} {}
// Constructor with handle and deleter
VulkanRaii(T handle_, DeleterFunc deleter_, const Dispatch& dispatch_, const char* resource_name = "Vulkan resource")
: handle{handle_}, deleter{std::move(deleter_)}, dispatch{dispatch_} {
LOG_DEBUG(Render_Vulkan, "RAII wrapper created for {}", resource_name);
}
// Move constructor
VulkanRaii(VulkanRaii&& other) noexcept
: handle{std::exchange(other.handle, VK_NULL_HANDLE)},
deleter{std::move(other.deleter)},
dispatch{other.dispatch} {
}
// Move assignment
VulkanRaii& operator=(VulkanRaii&& other) noexcept {
if (this != &other) {
cleanup();
handle = std::exchange(other.handle, VK_NULL_HANDLE);
deleter = std::move(other.deleter);
dispatch = other.dispatch;
}
return *this;
}
// Destructor - automatically cleans up the resource
~VulkanRaii() {
cleanup();
}
// Disallow copying
VulkanRaii(const VulkanRaii&) = delete;
VulkanRaii& operator=(const VulkanRaii&) = delete;
// Get the underlying handle
T get() const noexcept {
return handle;
}
// Check if the handle is valid
bool valid() const noexcept {
return handle != VK_NULL_HANDLE;
}
// Release ownership of the handle without destroying it
T release() noexcept {
return std::exchange(handle, VK_NULL_HANDLE);
}
// Reset the handle (destroying the current one if it exists)
void reset(T new_handle = VK_NULL_HANDLE, DeleterFunc new_deleter = {}) {
cleanup();
handle = new_handle;
deleter = std::move(new_deleter);
}
// Implicit conversion to handle type
operator T() const noexcept {
return handle;
}
// Dereference operator for pointer-like access
T operator->() const noexcept {
return handle;
}
private:
// Optimized cleanup function
void cleanup() noexcept {
if (handle != VK_NULL_HANDLE && deleter) {
deleter(handle, dispatch);
handle = VK_NULL_HANDLE;
}
}
T handle;
DeleterFunc deleter;
Dispatch dispatch;
};
// Common type aliases for Vulkan RAII wrappers with clearer names
using ManagedInstance = VulkanRaii<VkInstance, void*, vk::InstanceDispatch>;
using ManagedDevice = VulkanRaii<VkDevice, void*, vk::DeviceDispatch>;
using ManagedSurface = VulkanRaii<VkSurfaceKHR, VkInstance, vk::InstanceDispatch>;
using ManagedSwapchain = VulkanRaii<VkSwapchainKHR, VkDevice, vk::DeviceDispatch>;
using ManagedCommandPool = VulkanRaii<VkCommandPool, VkDevice, vk::DeviceDispatch>;
using ManagedBuffer = VulkanRaii<VkBuffer, VkDevice, vk::DeviceDispatch>;
using ManagedImage = VulkanRaii<VkImage, VkDevice, vk::DeviceDispatch>;
using ManagedImageView = VulkanRaii<VkImageView, VkDevice, vk::DeviceDispatch>;
using ManagedSampler = VulkanRaii<VkSampler, VkDevice, vk::DeviceDispatch>;
using ManagedShaderModule = VulkanRaii<VkShaderModule, VkDevice, vk::DeviceDispatch>;
using ManagedPipeline = VulkanRaii<VkPipeline, VkDevice, vk::DeviceDispatch>;
using ManagedPipelineLayout = VulkanRaii<VkPipelineLayout, VkDevice, vk::DeviceDispatch>;
using ManagedDescriptorSetLayout = VulkanRaii<VkDescriptorSetLayout, VkDevice, vk::DeviceDispatch>;
using ManagedDescriptorPool = VulkanRaii<VkDescriptorPool, VkDevice, vk::DeviceDispatch>;
using ManagedSemaphore = VulkanRaii<VkSemaphore, VkDevice, vk::DeviceDispatch>;
using ManagedFence = VulkanRaii<VkFence, VkDevice, vk::DeviceDispatch>;
using ManagedDebugUtilsMessenger = VulkanRaii<VkDebugUtilsMessengerEXT, VkInstance, vk::InstanceDispatch>;
// Helper functions to create RAII wrappers
/**
* Creates an RAII wrapper for a Vulkan instance
*/
inline ManagedInstance MakeManagedInstance(const vk::Instance& instance, const vk::InstanceDispatch& dispatch) {
auto deleter = [](VkInstance handle, const vk::InstanceDispatch& dld) {
dld.vkDestroyInstance(handle, nullptr);
};
return ManagedInstance(*instance, deleter, dispatch, "VkInstance");
}
/**
* Creates an RAII wrapper for a Vulkan device
*/
inline ManagedDevice MakeManagedDevice(const vk::Device& device, const vk::DeviceDispatch& dispatch) {
auto deleter = [](VkDevice handle, const vk::DeviceDispatch& dld) {
dld.vkDestroyDevice(handle, nullptr);
};
return ManagedDevice(*device, deleter, dispatch, "VkDevice");
}
/**
* Creates an RAII wrapper for a Vulkan surface
*/
inline ManagedSurface MakeManagedSurface(const vk::SurfaceKHR& surface, const vk::Instance& instance, const vk::InstanceDispatch& dispatch) {
auto deleter = [instance_ptr = *instance](VkSurfaceKHR handle, const vk::InstanceDispatch& dld) {
dld.vkDestroySurfaceKHR(instance_ptr, handle, nullptr);
};
return ManagedSurface(*surface, deleter, dispatch, "VkSurfaceKHR");
}
/**
* Creates an RAII wrapper for a Vulkan debug messenger
*/
inline ManagedDebugUtilsMessenger MakeManagedDebugUtilsMessenger(const vk::DebugUtilsMessenger& messenger,
const vk::Instance& instance,
const vk::InstanceDispatch& dispatch) {
auto deleter = [instance_ptr = *instance](VkDebugUtilsMessengerEXT handle, const vk::InstanceDispatch& dld) {
dld.vkDestroyDebugUtilsMessengerEXT(instance_ptr, handle, nullptr);
};
return ManagedDebugUtilsMessenger(*messenger, deleter, dispatch, "VkDebugUtilsMessengerEXT");
}
/**
* Creates an RAII wrapper for a Vulkan swapchain
*/
inline ManagedSwapchain MakeManagedSwapchain(VkSwapchainKHR swapchain_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
auto deleter = [device_handle](VkSwapchainKHR handle, const vk::DeviceDispatch& dld) {
dld.vkDestroySwapchainKHR(device_handle, handle, nullptr);
};
return ManagedSwapchain(swapchain_handle, deleter, dispatch, "VkSwapchainKHR");
}
/**
* Creates an RAII wrapper for a Vulkan buffer
*/
inline ManagedBuffer MakeManagedBuffer(VkBuffer buffer_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
auto deleter = [device_handle](VkBuffer handle, const vk::DeviceDispatch& dld) {
dld.vkDestroyBuffer(device_handle, handle, nullptr);
};
return ManagedBuffer(buffer_handle, deleter, dispatch, "VkBuffer");
}
/**
* Creates an RAII wrapper for a Vulkan image
*/
inline ManagedImage MakeManagedImage(VkImage image_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
auto deleter = [device_handle](VkImage handle, const vk::DeviceDispatch& dld) {
dld.vkDestroyImage(device_handle, handle, nullptr);
};
return ManagedImage(image_handle, deleter, dispatch, "VkImage");
}
/**
* Creates an RAII wrapper for a Vulkan image view
*/
inline ManagedImageView MakeManagedImageView(VkImageView view_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
auto deleter = [device_handle](VkImageView handle, const vk::DeviceDispatch& dld) {
dld.vkDestroyImageView(device_handle, handle, nullptr);
};
return ManagedImageView(view_handle, deleter, dispatch, "VkImageView");
}
/**
* Creates an RAII wrapper for a Vulkan semaphore
*/
inline ManagedSemaphore MakeManagedSemaphore(VkSemaphore semaphore_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
auto deleter = [device_handle](VkSemaphore handle, const vk::DeviceDispatch& dld) {
dld.vkDestroySemaphore(device_handle, handle, nullptr);
};
return ManagedSemaphore(semaphore_handle, deleter, dispatch, "VkSemaphore");
}
/**
* Creates an RAII wrapper for a Vulkan fence
*/
inline ManagedFence MakeManagedFence(VkFence fence_handle, VkDevice device_handle, const vk::DeviceDispatch& dispatch) {
auto deleter = [device_handle](VkFence handle, const vk::DeviceDispatch& dld) {
dld.vkDestroyFence(device_handle, handle, nullptr);
};
return ManagedFence(fence_handle, deleter, dispatch, "VkFence");
}
} // namespace Vulkan

View file

@ -12,7 +12,6 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "video_core/vulkan_common/vk_enum_string_helper.h"
#include "video_core/vulkan_common/vma.h"
#include "video_core/vulkan_common/vulkan_wrapper.h"
@ -311,10 +310,7 @@ const char* Exception::what() const noexcept {
}
void Destroy(VkInstance instance, const InstanceDispatch& dld) noexcept {
// FIXME: A double free occurs here if RAII is enabled.
if (!Settings::values.enable_raii.GetValue()) {
dld.vkDestroyInstance(instance, nullptr);
}
dld.vkDestroyInstance(instance, nullptr);
}
void Destroy(VkDevice device, const InstanceDispatch& dld) noexcept {
@ -417,10 +413,7 @@ void Destroy(VkInstance instance, VkDebugReportCallbackEXT handle,
}
void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept {
// FIXME: A double free occurs here if RAII is enabled.
if (!Settings::values.enable_raii.GetValue()) {
dld.vkDestroySurfaceKHR(instance, handle, nullptr);
}
dld.vkDestroySurfaceKHR(instance, handle, nullptr);
}
VkResult Free(VkDevice device, VkDescriptorPool handle, Span<VkDescriptorSet> sets,

View file

@ -516,7 +516,7 @@ public:
}
/// Returns true when there's a held object.
operator bool() const noexcept {
explicit operator bool() const noexcept {
return handle != nullptr;
}
@ -627,7 +627,7 @@ class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> {
public:
/// Creates a Vulkan instance.
/// @throw Exception on initialization error.
static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
[[nodiscard]] static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dispatch);
/// Enumerates physical devices.
@ -637,12 +637,12 @@ public:
/// Creates a debug callback messenger.
/// @throw Exception on creation failure.
DebugUtilsMessenger CreateDebugUtilsMessenger(
[[nodiscard]] DebugUtilsMessenger CreateDebugUtilsMessenger(
const VkDebugUtilsMessengerCreateInfoEXT& create_info) const;
/// Creates a debug report callback.
/// @throw Exception on creation failure.
DebugReportCallback CreateDebugReportCallback(
[[nodiscard]] DebugReportCallback CreateDebugReportCallback(
const VkDebugReportCallbackCreateInfoEXT& create_info) const;
/// Returns dispatch table.
@ -986,58 +986,60 @@ class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
public:
static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
Span<const char*> enabled_extensions, const void* next,
DeviceDispatch& dispatch);
[[nodiscard]] static Device Create(VkPhysicalDevice physical_device,
Span<VkDeviceQueueCreateInfo> queues_ci,
Span<const char*> enabled_extensions, const void* next,
DeviceDispatch& dispatch);
Queue GetQueue(u32 family_index) const noexcept;
[[nodiscard]] Queue GetQueue(u32 family_index) const noexcept;
BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
[[nodiscard]] BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;
[[nodiscard]] ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;
Semaphore CreateSemaphore() const;
[[nodiscard]] Semaphore CreateSemaphore() const;
Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
[[nodiscard]] Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
Fence CreateFence(const VkFenceCreateInfo& ci) const;
[[nodiscard]] Fence CreateFence(const VkFenceCreateInfo& ci) const;
DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
[[nodiscard]] DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
[[nodiscard]] RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const;
[[nodiscard]] DescriptorSetLayout CreateDescriptorSetLayout(
const VkDescriptorSetLayoutCreateInfo& ci) const;
PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const;
[[nodiscard]] PipelineCache CreatePipelineCache(const VkPipelineCacheCreateInfo& ci) const;
PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
[[nodiscard]] PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
VkPipelineCache cache = nullptr) const;
[[nodiscard]] Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci,
VkPipelineCache cache = nullptr) const;
Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
VkPipelineCache cache = nullptr) const;
[[nodiscard]] Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci,
VkPipelineCache cache = nullptr) const;
Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
[[nodiscard]] Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const;
[[nodiscard]] Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const;
CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const;
[[nodiscard]] CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const;
DescriptorUpdateTemplate CreateDescriptorUpdateTemplate(
[[nodiscard]] DescriptorUpdateTemplate CreateDescriptorUpdateTemplate(
const VkDescriptorUpdateTemplateCreateInfo& ci) const;
QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const;
[[nodiscard]] QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const;
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
[[nodiscard]] ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
Event CreateEvent() const;
[[nodiscard]] Event CreateEvent() const;
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
[[nodiscard]] SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept;
[[nodiscard]] DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept;
DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const;
[[nodiscard]] DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const;
VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer,
void* pnext = nullptr) const noexcept;