From edcde4ffd16f8163263a7ffd07d95e9c5b2c845b Mon Sep 17 00:00:00 2001 From: Serkyo Date: Thu, 30 Apr 2026 12:19:21 +0200 Subject: [PATCH] feat(renderer): wire vulkan debug messenger to tracing Implemented the Vulkan debug callback to intercept internal driver warnings and validation layer errors, converting them into tracing logs. Resolved unused assignment and unsafe pointer 2024 edition warnings. Added doc comments for the Renderer and its initialization logic. --- Cargo.lock | 1 + crates/renderer/Cargo.toml | 1 + crates/renderer/src/lib.rs | 75 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 3 deletions(-) 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