From 19a4653203e9459bb43607a1656194c9601f96d0 Mon Sep 17 00:00:00 2001 From: Robin Kertels Date: Thu, 27 Oct 2022 21:25:32 +0200 Subject: [PATCH] VideoBackends:Vulkan: Replace debug_report with debug_utils The former is deprecated and pretty much all modern drivers support VK_EXT_debug_utils. Android drivers dont support it. On those drivers, we use the implementation provided by the validation layers. --- Source/Core/VideoBackends/Vulkan/VKMain.cpp | 16 +- .../VideoBackends/Vulkan/VulkanContext.cpp | 186 ++++++++++++------ .../Core/VideoBackends/Vulkan/VulkanContext.h | 13 +- .../Vulkan/VulkanEntryPoints.inl | 13 +- 4 files changed, 153 insertions(+), 75 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VKMain.cpp b/Source/Core/VideoBackends/Vulkan/VKMain.cpp index e81c5d066b..46d535ce75 100644 --- a/Source/Core/VideoBackends/Vulkan/VKMain.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKMain.cpp @@ -84,13 +84,13 @@ static bool IsHostGPULoggingEnabled() Common::Log::LogLevel::LERROR); } -// Helper method to determine whether to enable the debug report extension. -static bool ShouldEnableDebugReports(bool enable_validation_layers) +// Helper method to determine whether to enable the debug utils extension. +static bool ShouldEnableDebugUtils(bool enable_validation_layers) { - // Enable debug reports if the Host GPU log option is checked, or validation layers are enabled. + // Enable debug utils if the Host GPU log option is checked, or validation layers are enabled. // The only issue here is that if Host GPU is not checked when the instance is created, the debug // report extension will not be enabled, requiring the game to be restarted before any reports - // will be logged. Otherwise, we'd have to enable debug reports on every instance, when most + // will be logged. Otherwise, we'd have to enable debug utils on every instance, when most // users will never check the Host GPU logging category. return enable_validation_layers || IsHostGPULoggingEnabled(); } @@ -114,10 +114,10 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) // Create Vulkan instance, needed before we can create a surface, or enumerate devices. // We use this instance to fill in backend info, then re-use it for the actual device. bool enable_surface = wsi.type != WindowSystemType::Headless; - bool enable_debug_reports = ShouldEnableDebugReports(enable_validation_layer); + bool enable_debug_utils = ShouldEnableDebugUtils(enable_validation_layer); u32 vk_api_version = 0; VkInstance instance = VulkanContext::CreateVulkanInstance( - wsi.type, enable_debug_reports, enable_validation_layer, &vk_api_version); + wsi.type, enable_debug_utils, enable_validation_layer, &vk_api_version); if (instance == VK_NULL_HANDLE) { PanicAlertFmt("Failed to create Vulkan instance."); @@ -174,8 +174,8 @@ bool VideoBackend::Initialize(const WindowSystemInfo& wsi) // Now we can create the Vulkan device. VulkanContext takes ownership of the instance and surface. g_vulkan_context = - VulkanContext::Create(instance, gpu_list[selected_adapter_index], surface, - enable_debug_reports, enable_validation_layer, vk_api_version); + VulkanContext::Create(instance, gpu_list[selected_adapter_index], surface, enable_debug_utils, + enable_validation_layer, vk_api_version); if (!g_vulkan_context) { PanicAlertFmt("Failed to create Vulkan device"); diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 3275cb9417..6740ae8583 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -18,6 +18,8 @@ namespace Vulkan { +static constexpr const char* VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation"; + std::unique_ptr g_vulkan_context; VulkanContext::VulkanContext(VkInstance instance, VkPhysicalDevice physical_device) @@ -45,8 +47,8 @@ VulkanContext::~VulkanContext() if (m_device != VK_NULL_HANDLE) vkDestroyDevice(m_device, nullptr); - if (m_debug_report_callback != VK_NULL_HANDLE) - DisableDebugReports(); + if (m_debug_utils_messenger != VK_NULL_HANDLE) + DisableDebugUtils(); vkDestroyInstance(m_instance, nullptr); } @@ -77,22 +79,49 @@ bool VulkanContext::CheckValidationLayerAvailablility() res = vkEnumerateInstanceLayerProperties(&layer_count, layer_list.data()); ASSERT(res == VK_SUCCESS); - // Check for both VK_EXT_debug_report and VK_LAYER_LUNARG_standard_validation - return (std::find_if(extension_list.begin(), extension_list.end(), - [](const auto& it) { - return strcmp(it.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0; - }) != extension_list.end() && - std::find_if(layer_list.begin(), layer_list.end(), [](const auto& it) { - return strcmp(it.layerName, "VK_LAYER_KHRONOS_validation") == 0; - }) != layer_list.end()); + bool supports_validation_layers = + std::find_if(layer_list.begin(), layer_list.end(), [](const auto& it) { + return strcmp(it.layerName, VALIDATION_LAYER_NAME) == 0; + }) != layer_list.end(); + + bool supports_debug_utils = + std::find_if(extension_list.begin(), extension_list.end(), [](const auto& it) { + return strcmp(it.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; + }) != extension_list.end(); + + if (!supports_debug_utils && supports_validation_layers) + { + // If the instance doesn't support debug utils but we're using validation layers, + // try to use the implementation of the extension provided by the validation layers. + extension_count = 0; + res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME, &extension_count, nullptr); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, "vkEnumerateInstanceExtensionProperties failed: "); + return false; + } + + extension_list.resize(extension_count); + res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME, &extension_count, + extension_list.data()); + ASSERT(res == VK_SUCCESS); + supports_debug_utils = + std::find_if(extension_list.begin(), extension_list.end(), [](const auto& it) { + return strcmp(it.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME) == 0; + }) != extension_list.end(); + } + + // Check for both VK_EXT_debug_utils and VK_LAYER_KHRONOS_validation + return supports_debug_utils && supports_validation_layers; } -VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_report, +VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_utils, bool enable_validation_layer, u32* out_vk_api_version) { std::vector enabled_extensions; - if (!SelectInstanceExtensions(&enabled_extensions, wstype, enable_debug_report)) + if (!SelectInstanceExtensions(&enabled_extensions, wstype, enable_debug_utils, + enable_validation_layer)) return VK_NULL_HANDLE; VkApplicationInfo app_info = {}; @@ -129,12 +158,11 @@ VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool ena instance_create_info.enabledLayerCount = 0; instance_create_info.ppEnabledLayerNames = nullptr; - // Enable debug layer on debug builds + // Enable validation layer if the user enabled them in the settings if (enable_validation_layer) { - static const char* layer_names[] = {"VK_LAYER_KHRONOS_validation"}; instance_create_info.enabledLayerCount = 1; - instance_create_info.ppEnabledLayerNames = layer_names; + instance_create_info.ppEnabledLayerNames = &VALIDATION_LAYER_NAME; } VkInstance instance; @@ -149,7 +177,8 @@ VkInstance VulkanContext::CreateVulkanInstance(WindowSystemType wstype, bool ena } bool VulkanContext::SelectInstanceExtensions(std::vector* extension_list, - WindowSystemType wstype, bool enable_debug_report) + WindowSystemType wstype, bool enable_debug_utils, + bool validation_layer_enabled) { u32 extension_count = 0; VkResult res = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr); @@ -170,14 +199,50 @@ bool VulkanContext::SelectInstanceExtensions(std::vector* extension available_extension_list.data()); ASSERT(res == VK_SUCCESS); + u32 validation_layer_extension_count = 0; + std::vector validation_layer_extension_list; + if (validation_layer_enabled) + { + res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME, + &validation_layer_extension_count, nullptr); + if (res != VK_SUCCESS) + { + LOG_VULKAN_ERROR(res, + "vkEnumerateInstanceExtensionProperties failed for validation layers: "); + } + else + { + validation_layer_extension_list.resize(validation_layer_extension_count); + res = vkEnumerateInstanceExtensionProperties(VALIDATION_LAYER_NAME, + &validation_layer_extension_count, + validation_layer_extension_list.data()); + ASSERT(res == VK_SUCCESS); + } + } + for (const auto& extension_properties : available_extension_list) INFO_LOG_FMT(VIDEO, "Available extension: {}", extension_properties.extensionName); + for (const auto& extension_properties : validation_layer_extension_list) + { + INFO_LOG_FMT(VIDEO, "Available extension in validation layer: {}", + extension_properties.extensionName); + } + auto AddExtension = [&](const char* name, bool required) { - if (std::find_if(available_extension_list.begin(), available_extension_list.end(), + bool extension_supported = + std::find_if(available_extension_list.begin(), available_extension_list.end(), [&](const VkExtensionProperties& properties) { return !strcmp(name, properties.extensionName); - }) != available_extension_list.end()) + }) != available_extension_list.end(); + extension_supported = + extension_supported || + std::find_if(validation_layer_extension_list.begin(), validation_layer_extension_list.end(), + [&](const VkExtensionProperties& properties) { + return !strcmp(name, properties.extensionName); + }) != validation_layer_extension_list.end(); + + if (extension_supported) { INFO_LOG_FMT(VIDEO, "Enabling extension: {}", name); extension_list->push_back(name); @@ -223,20 +288,21 @@ bool VulkanContext::SelectInstanceExtensions(std::vector* extension } #endif - // VK_EXT_debug_report - if (enable_debug_report && !AddExtension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, false)) - WARN_LOG_FMT(VIDEO, "Vulkan: Debug report requested, but extension is not available."); - AddExtension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false); if (wstype != WindowSystemType::Headless) { AddExtension(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME, false); } + // VK_EXT_debug_utils if (AddExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false)) { g_Config.backend_info.bSupportsSettingObjectNames = true; } + else if (enable_debug_utils) + { + WARN_LOG_FMT(VIDEO, "Vulkan: Debug utils requested, but extension is not available."); + } return true; } @@ -435,9 +501,10 @@ void VulkanContext::PopulateBackendInfoMultisampleModes( config->backend_info.AAModes.emplace_back(64); } -std::unique_ptr -VulkanContext::Create(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR surface, - bool enable_debug_reports, bool enable_validation_layer, u32 vk_api_version) +std::unique_ptr VulkanContext::Create(VkInstance instance, VkPhysicalDevice gpu, + VkSurfaceKHR surface, bool enable_debug_utils, + bool enable_validation_layer, + u32 vk_api_version) { std::unique_ptr context = std::make_unique(instance, gpu); @@ -445,9 +512,9 @@ VulkanContext::Create(VkInstance instance, VkPhysicalDevice gpu, VkSurfaceKHR su context->InitDriverDetails(); context->PopulateShaderSubgroupSupport(); - // Enable debug reports if the "Host GPU" log category is enabled. - if (enable_debug_reports) - context->EnableDebugReports(); + // Enable debug messages if the "Host GPU" log category is enabled. + if (enable_debug_utils) + context->EnableDebugUtils(); // Attempt to create the device. if (!context->CreateDevice(surface, enable_validation_layer) || @@ -679,9 +746,8 @@ bool VulkanContext::CreateDevice(VkSurfaceKHR surface, bool enable_validation_la // Enable debug layer on debug builds if (enable_validation_layer) { - static const char* layer_names[] = {"VK_LAYER_LUNARG_standard_validation"}; device_info.enabledLayerCount = 1; - device_info.ppEnabledLayerNames = layer_names; + device_info.ppEnabledLayerNames = &VALIDATION_LAYER_NAME; } VkResult res = vkCreateDevice(m_physical_device, &device_info, nullptr, &m_device); @@ -732,20 +798,17 @@ bool VulkanContext::CreateAllocator(u32 vk_api_version) return true; } -static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - uint64_t object, size_t location, - int32_t messageCode, - const char* pLayerPrefix, - const char* pMessage, void* pUserData) +static VKAPI_ATTR VkBool32 VKAPI_CALL +DebugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { - const std::string log_message = - fmt::format("Vulkan debug report: ({}) {}", pLayerPrefix ? pLayerPrefix : "", pMessage); - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + const std::string log_message = fmt::format("Vulkan debug message: {}", pCallbackData->pMessage); + if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) ERROR_LOG_FMT(HOST_GPU, "{}", log_message); - else if (flags & (VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT)) + else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) WARN_LOG_FMT(HOST_GPU, "{}", log_message); - else if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) + else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) INFO_LOG_FMT(HOST_GPU, "{}", log_message); else DEBUG_LOG_FMT(HOST_GPU, "{}", log_message); @@ -753,43 +816,50 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(VkDebugReportFlagsEXT return VK_FALSE; } -bool VulkanContext::EnableDebugReports() +bool VulkanContext::EnableDebugUtils() { // Already enabled? - if (m_debug_report_callback != VK_NULL_HANDLE) + if (m_debug_utils_messenger != VK_NULL_HANDLE) return true; // Check for presence of the functions before calling - if (!vkCreateDebugReportCallbackEXT || !vkDestroyDebugReportCallbackEXT || - !vkDebugReportMessageEXT) + if (!vkCreateDebugUtilsMessengerEXT || !vkDestroyDebugUtilsMessengerEXT || + !vkSubmitDebugUtilsMessageEXT) { return false; } - VkDebugReportCallbackCreateInfoEXT callback_info = { - VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, nullptr, - VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | - VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT | - VK_DEBUG_REPORT_DEBUG_BIT_EXT, - DebugReportCallback, nullptr}; + VkDebugUtilsMessengerCreateInfoEXT messenger_info = { + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, + nullptr, + 0, + 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, + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, + DebugUtilsCallback, + nullptr}; - VkResult res = - vkCreateDebugReportCallbackEXT(m_instance, &callback_info, nullptr, &m_debug_report_callback); + VkResult res = vkCreateDebugUtilsMessengerEXT(m_instance, &messenger_info, nullptr, + &m_debug_utils_messenger); if (res != VK_SUCCESS) { - LOG_VULKAN_ERROR(res, "vkCreateDebugReportCallbackEXT failed: "); + LOG_VULKAN_ERROR(res, "vkCreateDebugUtilsMessengerEXT failed: "); return false; } return true; } -void VulkanContext::DisableDebugReports() +void VulkanContext::DisableDebugUtils() { - if (m_debug_report_callback != VK_NULL_HANDLE) + if (m_debug_utils_messenger != VK_NULL_HANDLE) { - vkDestroyDebugReportCallbackEXT(m_instance, m_debug_report_callback, nullptr); - m_debug_report_callback = VK_NULL_HANDLE; + vkDestroyDebugUtilsMessengerEXT(m_instance, m_debug_utils_messenger, nullptr); + m_debug_utils_messenger = VK_NULL_HANDLE; } } diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index 6ba48a34f6..17b1459aa6 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -25,7 +25,7 @@ public: static bool CheckValidationLayerAvailablility(); // Helper method to create a Vulkan instance. - static VkInstance CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_report, + static VkInstance CreateVulkanInstance(WindowSystemType wstype, bool enable_debug_utils, bool enable_validation_layer, u32* out_vk_api_version); // Returns a list of Vulkan-compatible GPUs. @@ -46,12 +46,12 @@ public: // This assumes that PopulateBackendInfo and PopulateBackendInfoAdapters has already // been called for the specified VideoConfig. static std::unique_ptr Create(VkInstance instance, VkPhysicalDevice gpu, - VkSurfaceKHR surface, bool enable_debug_reports, + VkSurfaceKHR surface, bool enable_debug_utils, bool enable_validation_layer, u32 api_version); // Enable/disable debug message runtime. - bool EnableDebugReports(); - void DisableDebugReports(); + bool EnableDebugUtils(); + void DisableDebugUtils(); // Global state accessors VkInstance GetVulkanInstance() const { return m_instance; } @@ -115,7 +115,8 @@ public: private: static bool SelectInstanceExtensions(std::vector* extension_list, - WindowSystemType wstype, bool enable_debug_report); + WindowSystemType wstype, bool enable_debug_utils, + bool validation_layer_enabled); bool SelectDeviceExtensions(bool enable_surface); bool SelectDeviceFeatures(); bool CreateDevice(VkSurfaceKHR surface, bool enable_validation_layer); @@ -134,7 +135,7 @@ private: u32 m_present_queue_family_index = 0; VkQueueFamilyProperties m_graphics_queue_properties = {}; - VkDebugReportCallbackEXT m_debug_report_callback = VK_NULL_HANDLE; + VkDebugUtilsMessengerEXT m_debug_utils_messenger = VK_NULL_HANDLE; VkPhysicalDeviceFeatures m_device_features = {}; VkPhysicalDeviceProperties m_device_properties = {}; diff --git a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl index 3bb21c41e3..d716ce49d2 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl +++ b/Source/Core/VideoBackends/Vulkan/VulkanEntryPoints.inl @@ -57,9 +57,16 @@ VULKAN_INSTANCE_ENTRY_POINT(vkCreateAndroidSurfaceKHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkCreateMetalSurfaceEXT, false) #endif -VULKAN_INSTANCE_ENTRY_POINT(vkCreateDebugReportCallbackEXT, false) -VULKAN_INSTANCE_ENTRY_POINT(vkDestroyDebugReportCallbackEXT, false) -VULKAN_INSTANCE_ENTRY_POINT(vkDebugReportMessageEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkCmdBeginDebugUtilsLabelEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkCmdEndDebugUtilsLabelEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkCmdInsertDebugUtilsLabelEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkDestroyDebugUtilsMessengerEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkCreateDebugUtilsMessengerEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkQueueBeginDebugUtilsLabelEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkQueueEndDebugUtilsLabelEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkQueueInsertDebugUtilsLabelEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectTagEXT, false) +VULKAN_INSTANCE_ENTRY_POINT(vkSubmitDebugUtilsMessageEXT, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceProperties2, false) VULKAN_INSTANCE_ENTRY_POINT(vkGetPhysicalDeviceSurfaceCapabilities2KHR, false) VULKAN_INSTANCE_ENTRY_POINT(vkSetDebugUtilsObjectNameEXT, false)