diff --git a/Cargo.lock b/Cargo.lock index 7cd4bf9..62f9a0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1055,6 +1055,7 @@ version = "0.1.0" dependencies = [ "ash", "thiserror 2.0.18", + "tracing", ] [[package]] diff --git a/crates/renderer/Cargo.toml b/crates/renderer/Cargo.toml index 5c0e449..dcf69a5 100644 --- a/crates/renderer/Cargo.toml +++ b/crates/renderer/Cargo.toml @@ -6,3 +6,4 @@ edition = "2024" [dependencies] ash = "0.38.0" thiserror = "2.0.18" +tracing = "0.1.44" diff --git a/crates/renderer/src/lib.rs b/crates/renderer/src/lib.rs index a0bacfb..a9c5ac2 100644 --- a/crates/renderer/src/lib.rs +++ b/crates/renderer/src/lib.rs @@ -1,13 +1,21 @@ pub mod error; -use std::ffi::c_char; -use ash::{Entry, Instance, vk}; +use std::ffi::{CStr, c_char}; +use ash::{Entry, Instance, ext, vk}; +use tracing::{debug, error, info, warn}; pub use error::RendererError; +/// The core renderer structure holding the Vulkan instance and debug resources. pub struct Renderer { + /// The entry point to the Vulkan library. _entry: Entry, + /// The Vulkan instance handle. instance: Instance, + /// The debug utils instance, if enabled (debug builds only). + debug_utils: Option, + /// The debug messenger handle, if enabled (debug builds only). + debug_messenger: vk::DebugUtilsMessengerEXT, } impl Renderer { @@ -16,12 +24,14 @@ impl Renderer { pub fn new(required_extensions: &[*const c_char]) -> Result { let entry = unsafe { Entry::load() }?; + // Prepare extensions and layers let mut extensions: Vec<*const c_char> = required_extensions.to_vec(); let mut layers: Vec<*const c_char> = Vec::new(); + // Enable debug extensions and validation layers in debug builds #[cfg(debug_assertions)] { - extensions.push(ash::ext::debug_utils::NAME.as_ptr()); + extensions.push(ext::debug_utils::NAME.as_ptr()); layers.push(c"VK_LAYER_KHRONOS_validation".as_ptr()); } @@ -33,11 +43,70 @@ impl Renderer { .enabled_extension_names(&extensions) .enabled_layer_names(&layers); + // Create the Vulkan instance let instance = unsafe { entry.create_instance(&create_info, None)? }; + // Setup debug messenger for validation layer feedback + #[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(Self { _entry: entry, instance, + debug_utils, + debug_messenger, }) } +} + +/// The callback function invoked by Vulkan's validation layers. +/// +/// This function translates Vulkan debug messages into `tracing` events. +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 { + // Safety: p_callback_data is guaranteed to be valid by the Vulkan spec + let callback_data = unsafe { *p_callback_data }; + + // Convert raw C string to Rust string for logging + let message = if callback_data.p_message.is_null() { + "".into() + } else { + unsafe { CStr::from_ptr(callback_data.p_message).to_string_lossy() } + }; + + // Route severity to matching tracing macro + 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}"), + } + + // TRUE aborts the Vulkan call that triggered the warning + vk::FALSE } \ No newline at end of file