// SPDX-License-Identifier: AGPL-3.0-only use crate::error::RendererError; use ash::{Entry, Instance, ext, vk}; use std::ffi::c_char; // `CStr` and the tracing macros are used only by the debug-build validation callback. #[cfg(debug_assertions)] use std::ffi::CStr; #[cfg(debug_assertions)] use tracing::{debug, error, info, warn}; /// Creates a Vulkan instance and optionally a debug messenger. /// /// # Errors /// /// Returns [`RendererError::VulkanError`] if instance creation fails, or if the debug messenger cannot be created in debug builds. pub fn create_instance( entry: &Entry, required_extensions: &[*const c_char], ) -> Result< ( Instance, Option, vk::DebugUtilsMessengerEXT, ), RendererError, > { // The validation extension and layer are pushed only in debug builds, so in release builds these vectors are never mutated after initialization. #[cfg_attr( not(debug_assertions), expect( unused_mut, reason = "the debug-only block below mutates these vectors" ) )] let mut extensions = required_extensions.to_vec(); #[cfg_attr( not(debug_assertions), expect( unused_mut, reason = "the debug-only block below mutates these vectors" ) )] let mut layers = Vec::new(); #[cfg(debug_assertions)] { extensions.push(ext::debug_utils::NAME.as_ptr()); layers.push(c"VK_LAYER_KHRONOS_validation".as_ptr()); } let app_info = vk::ApplicationInfo::default().api_version(vk::API_VERSION_1_3); let create_info = vk::InstanceCreateInfo::default() .application_info(&app_info) .enabled_extension_names(&extensions) .enabled_layer_names(&layers); let instance = unsafe { entry.create_instance(&create_info, None)? }; #[cfg(debug_assertions)] let (debug_utils, debug_messenger) = { let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::default() .message_severity( vk::DebugUtilsMessageSeverityFlagsEXT::WARNING | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR, ) .message_type( vk::DebugUtilsMessageTypeFlagsEXT::GENERAL | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE, ) .pfn_user_callback(Some(vulkan_debug_callback)); let utils = ext::debug_utils::Instance::new(entry, &instance); let messenger = unsafe { utils.create_debug_utils_messenger(&debug_info, None)? }; (Some(utils), messenger) }; #[cfg(not(debug_assertions))] let (debug_utils, debug_messenger) = (None, vk::DebugUtilsMessengerEXT::null()); Ok((instance, debug_utils, debug_messenger)) } /// The callback function invoked by Vulkan's validation layers. /// /// # Safety /// /// Invoked by the Vulkan loader, which must pass a valid `p_callback_data` pointer whose `p_message` is either null or a valid NUL-terminated C string. Not to be called directly. #[cfg(debug_assertions)] unsafe extern "system" fn vulkan_debug_callback( message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, _message_type: vk::DebugUtilsMessageTypeFlagsEXT, p_callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>, _user_data: *mut std::ffi::c_void, ) -> vk::Bool32 { let callback_data = unsafe { *p_callback_data }; let message = if callback_data.p_message.is_null() { "".into() } else { unsafe { CStr::from_ptr(callback_data.p_message).to_string_lossy() } }; match message_severity { vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => debug!("{message}"), vk::DebugUtilsMessageSeverityFlagsEXT::INFO => info!("{message}"), vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => warn!("{message}"), vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => error!("{message}"), _ => info!("{message}"), } vk::FALSE }