renderer_vulkan: Create debug callback on separate file and throw

Initialize debug callbacks (messenger) from a separate file. This allows
sharing code with different backends.

Change our Vulkan error handling to use exceptions instead of error
codes, simplifying the initialization process.
merge-requests/60/head
ReinUsesLisp 2020-12-25 02:01:13 +07:00
parent 25f88d99ce
commit 47843b4f09
8 changed files with 88 additions and 79 deletions

@ -258,6 +258,8 @@ add_library(video_core STATIC
textures/texture.h textures/texture.h
video_core.cpp video_core.cpp
video_core.h video_core.h
vulkan_common/vulkan_debug_callback.cpp
vulkan_common/vulkan_debug_callback.h
vulkan_common/vulkan_instance.cpp vulkan_common/vulkan_instance.cpp
vulkan_common/vulkan_instance.h vulkan_common/vulkan_instance.h
vulkan_common/vulkan_library.cpp vulkan_common/vulkan_library.cpp

@ -29,6 +29,7 @@
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h" #include "video_core/renderer_vulkan/vk_state_tracker.h"
#include "video_core/renderer_vulkan/vk_swapchain.h" #include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/vulkan_common/vulkan_debug_callback.h"
#include "video_core/vulkan_common/vulkan_instance.h" #include "video_core/vulkan_common/vulkan_instance.h"
#include "video_core/vulkan_common/vulkan_library.h" #include "video_core/vulkan_common/vulkan_library.h"
#include "video_core/vulkan_common/vulkan_wrapper.h" #include "video_core/vulkan_common/vulkan_wrapper.h"
@ -48,24 +49,6 @@
namespace Vulkan { namespace Vulkan {
namespace { namespace {
VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT* data,
[[maybe_unused]] void* user_data) {
const char* const message{data->pMessage};
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
LOG_CRITICAL(Render_Vulkan, "{}", message);
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
LOG_WARNING(Render_Vulkan, "{}", message);
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
LOG_INFO(Render_Vulkan, "{}", message);
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
LOG_DEBUG(Render_Vulkan, "{}", message);
}
return VK_FALSE;
}
std::string GetReadableVersion(u32 version) { std::string GetReadableVersion(u32 version) {
return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
VK_VERSION_PATCH(version)); VK_VERSION_PATCH(version));
@ -158,7 +141,11 @@ bool RendererVulkan::Init() {
library = OpenLibrary(); library = OpenLibrary();
std::tie(instance, instance_version) = CreateInstance( std::tie(instance, instance_version) = CreateInstance(
library, dld, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug); library, dld, render_window.GetWindowInfo().type, true, Settings::values.renderer_debug);
if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) { if (Settings::values.renderer_debug) {
debug_callback = CreateDebugCallback(instance);
}
if (!CreateSurface() || !PickDevices()) {
return false; return false;
} }
@ -201,18 +188,6 @@ void RendererVulkan::ShutDown() {
device.reset(); device.reset();
} }
bool RendererVulkan::CreateDebugCallback() {
if (!Settings::values.renderer_debug) {
return true;
}
debug_callback = instance.TryCreateDebugCallback(DebugCallback);
if (!debug_callback) {
LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
return false;
}
return true;
}
bool RendererVulkan::CreateSurface() { bool RendererVulkan::CreateSurface() {
[[maybe_unused]] const auto& window_info = render_window.GetWindowInfo(); [[maybe_unused]] const auto& window_info = render_window.GetWindowInfo();
VkSurfaceKHR unsafe_surface = nullptr; VkSurfaceKHR unsafe_surface = nullptr;

@ -56,8 +56,6 @@ public:
static std::vector<std::string> EnumerateDevices(); static std::vector<std::string> EnumerateDevices();
private: private:
bool CreateDebugCallback();
bool CreateSurface(); bool CreateSurface();
bool PickDevices(); bool PickDevices();
@ -78,7 +76,7 @@ private:
VKScreenInfo screen_info; VKScreenInfo screen_info;
vk::DebugCallback debug_callback; vk::DebugUtilsMessenger debug_callback;
std::unique_ptr<VKDevice> device; std::unique_ptr<VKDevice> device;
std::unique_ptr<VKMemoryManager> memory_manager; std::unique_ptr<VKMemoryManager> memory_manager;
std::unique_ptr<StateTracker> state_tracker; std::unique_ptr<StateTracker> state_tracker;

@ -0,0 +1,45 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string_view>
#include "common/logging/log.h"
#include "video_core/vulkan_common/vulkan_debug_callback.h"
namespace Vulkan {
namespace {
VkBool32 Callback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT* data,
[[maybe_unused]] void* user_data) {
const std::string_view message{data->pMessage};
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
LOG_CRITICAL(Render_Vulkan, "{}", message);
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
LOG_WARNING(Render_Vulkan, "{}", message);
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
LOG_INFO(Render_Vulkan, "{}", message);
} else if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
LOG_DEBUG(Render_Vulkan, "{}", message);
}
return VK_FALSE;
}
} // Anonymous namespace
vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance) {
return instance.CreateDebugUtilsMessenger(VkDebugUtilsMessengerCreateInfoEXT{
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
.pfnUserCallback = Callback,
});
}
} // namespace Vulkan

@ -0,0 +1,11 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/vulkan_common/vulkan_wrapper.h"
namespace Vulkan {
vk::DebugUtilsMessenger CreateDebugCallback(const vk::Instance& instance);
} // namespace Vulkan

@ -117,21 +117,20 @@ std::pair<vk::Instance, u32> CreateInstance(Common::DynamicLibrary& library,
bool enable_debug_utils, bool enable_layers) { bool enable_debug_utils, bool enable_layers) {
if (!library.IsOpen()) { if (!library.IsOpen()) {
LOG_ERROR(Render_Vulkan, "Vulkan library not available"); LOG_ERROR(Render_Vulkan, "Vulkan library not available");
return {}; throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
if (!library.GetSymbol("vkGetInstanceProcAddr", &dld.vkGetInstanceProcAddr)) { if (!library.GetSymbol("vkGetInstanceProcAddr", &dld.vkGetInstanceProcAddr)) {
LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan"); LOG_ERROR(Render_Vulkan, "vkGetInstanceProcAddr not present in Vulkan");
return {}; throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
if (!vk::Load(dld)) { if (!vk::Load(dld)) {
LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers"); LOG_ERROR(Render_Vulkan, "Failed to load Vulkan function pointers");
return {}; throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils); const std::vector<const char*> extensions = RequiredExtensions(window_type, enable_debug_utils);
if (!AreExtensionsSupported(dld, extensions)) { if (!AreExtensionsSupported(dld, extensions)) {
return {}; throw vk::Exception(VK_ERROR_EXTENSION_NOT_PRESENT);
} }
std::vector<const char*> layers = Layers(enable_layers); std::vector<const char*> layers = Layers(enable_layers);
RemoveUnavailableLayers(dld, layers); RemoveUnavailableLayers(dld, layers);
@ -139,12 +138,9 @@ std::pair<vk::Instance, u32> CreateInstance(Common::DynamicLibrary& library,
const u32 version = std::min(vk::AvailableVersion(dld), VK_API_VERSION_1_1); const u32 version = std::min(vk::AvailableVersion(dld), VK_API_VERSION_1_1);
vk::Instance instance = vk::Instance::Create(version, layers, extensions, dld); vk::Instance instance = vk::Instance::Create(version, layers, extensions, dld);
if (!instance) {
LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance");
return {};
}
if (!vk::Load(*instance, dld)) { if (!vk::Load(*instance, dld)) {
LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers"); LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
return std::make_pair(std::move(instance), version); return std::make_pair(std::move(instance), version);
} }

@ -435,7 +435,7 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffe
} }
Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions, Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dispatch) noexcept { InstanceDispatch& dispatch) {
const VkApplicationInfo application_info{ const VkApplicationInfo application_info{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = nullptr, .pNext = nullptr,
@ -455,22 +455,17 @@ Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char
.enabledExtensionCount = extensions.size(), .enabledExtensionCount = extensions.size(),
.ppEnabledExtensionNames = extensions.data(), .ppEnabledExtensionNames = extensions.data(),
}; };
VkInstance instance; VkInstance instance;
if (dispatch.vkCreateInstance(&ci, nullptr, &instance) != VK_SUCCESS) { Check(dispatch.vkCreateInstance(&ci, nullptr, &instance));
// Failed to create the instance.
return {};
}
if (!Proc(dispatch.vkDestroyInstance, dispatch, "vkDestroyInstance", instance)) { if (!Proc(dispatch.vkDestroyInstance, dispatch, "vkDestroyInstance", instance)) {
// We successfully created an instance but the destroy function couldn't be loaded. // We successfully created an instance but the destroy function couldn't be loaded.
// This is a good moment to panic. // This is a good moment to panic.
return {}; throw vk::Exception(VK_ERROR_INITIALIZATION_FAILED);
} }
return Instance(instance, dispatch); return Instance(instance, dispatch);
} }
std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices() { std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices() const {
u32 num; u32 num;
if (dld->vkEnumeratePhysicalDevices(handle, &num, nullptr) != VK_SUCCESS) { if (dld->vkEnumeratePhysicalDevices(handle, &num, nullptr) != VK_SUCCESS) {
return std::nullopt; return std::nullopt;
@ -483,27 +478,11 @@ std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices(
return std::make_optional(std::move(physical_devices)); return std::make_optional(std::move(physical_devices));
} }
DebugCallback Instance::TryCreateDebugCallback( DebugUtilsMessenger Instance::CreateDebugUtilsMessenger(
PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept { const VkDebugUtilsMessengerCreateInfoEXT& create_info) const {
const VkDebugUtilsMessengerCreateInfoEXT ci{ VkDebugUtilsMessengerEXT object;
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, Check(dld->vkCreateDebugUtilsMessengerEXT(handle, &create_info, nullptr, &object));
.pNext = nullptr, return DebugUtilsMessenger(object, handle, *dld);
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
.pfnUserCallback = callback,
.pUserData = nullptr,
};
VkDebugUtilsMessengerEXT messenger;
if (dld->vkCreateDebugUtilsMessengerEXT(handle, &ci, nullptr, &messenger) != VK_SUCCESS) {
return {};
}
return DebugCallback(messenger, handle, *dld);
} }
void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {

@ -555,7 +555,7 @@ private:
const DeviceDispatch* dld = nullptr; const DeviceDispatch* dld = nullptr;
}; };
using DebugCallback = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; using DebugUtilsMessenger = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>;
using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>;
using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>;
using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>;
@ -573,16 +573,19 @@ class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> {
using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle; using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle;
public: public:
/// Creates a Vulkan instance. Use "operator bool" for error handling. /// Creates a Vulkan instance.
/// @throw Exception on initialization error.
static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions, static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dispatch) noexcept; InstanceDispatch& dispatch);
/// Enumerates physical devices. /// Enumerates physical devices.
/// @return Physical devices and an empty handle on failure. /// @return Physical devices and an empty handle on failure.
std::optional<std::vector<VkPhysicalDevice>> EnumeratePhysicalDevices(); std::optional<std::vector<VkPhysicalDevice>> EnumeratePhysicalDevices() const;
/// Tries to create a debug callback messenger. Returns an empty handle on failure. /// Creates a debug callback messenger.
DebugCallback TryCreateDebugCallback(PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept; /// @throw Exception on creation failure.
DebugUtilsMessenger CreateDebugUtilsMessenger(
const VkDebugUtilsMessengerCreateInfoEXT& create_info) const;
}; };
class Queue { class Queue {