#pragma once #include #include #include namespace lt::renderer::vk { class Messenger { public: // NOLINTNEXTLINE(performance-enum-size) enum Severity : decltype(std::to_underlying( VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT )) { verbose = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT, info = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT, warning = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT, error = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, all_severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT, }; // NOLINTNEXTLINE(performance-enum-size) enum Type : decltype(std::to_underlying(VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT)) { general = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT, validation = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, performance = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, // address_binding = VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT, /** @note: does not include address binding yet. */ all_type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, }; using CallbackData_T = const VkDebugUtilsMessengerCallbackDataEXT *; using Callback_T = std::function; struct CreateInfo { Severity severity; Type type; Callback_T callback; void *user_data; }; explicit Messenger(CreateInfo info) : m_callback(std::move(info.callback)) , m_user_data(info.user_data) { ensure(m_callback, "Failed to initialize vk::Messenger: null callback"); ensure(info.severity != Severity {}, "Failed to initialize vk::Messenger: null severity"); ensure(info.type != Type {}, "Failed to initialize vk::Messenger: null type"); // Instance may not be initialized yet // Making it get initialized inside a call to vk_create_debug_messenger // Which would invoke a nullptr... if (!vk_create_debug_messenger) { Instance::get(); } const auto vulkan_info = VkDebugUtilsMessengerCreateInfoEXT { .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT, .messageSeverity = info.severity, .messageType = info.type, .pfnUserCallback = &validation_layers_callback, .pUserData = this, }; ensure( !vk_create_debug_messenger(Instance::get(), &vulkan_info, nullptr, &m_debug_messenger), "Failed to create vulkan debug utils messenger" ); } ~Messenger() { vk_destroy_debug_messenger(Instance::get(), m_debug_messenger, nullptr); } Messenger(Messenger &&) = default; Messenger(const Messenger &) = delete; auto operator=(Messenger &&) -> Messenger & = default; auto operator=(const Messenger &) const -> Messenger & = delete; private: static auto validation_layers_callback( VkDebugUtilsMessageSeverityFlagBitsEXT const message_severity, VkDebugUtilsMessageTypeFlagsEXT const message_type, VkDebugUtilsMessengerCallbackDataEXT const *const callback_data, void *const vulkan_user_data ) -> VkBool32 { auto *messenger = (Messenger *)vulkan_user_data; // NOLINT ensure(messenger, "Null vulkan_user_data received in messenger callback"); messenger->validation_layers_callback_impl(message_severity, message_type, callback_data); return VK_FALSE; } void validation_layers_callback_impl( VkDebugUtilsMessageSeverityFlagBitsEXT message_severity, VkDebugUtilsMessageTypeFlagsEXT message_type, const VkDebugUtilsMessengerCallbackDataEXT *callback_data ) { m_callback( static_cast(message_severity), static_cast(message_type), callback_data, m_user_data ); } memory::NullOnMove m_debug_messenger = VK_NULL_HANDLE; Callback_T m_callback; void *m_user_data; }; } // namespace lt::renderer::vk