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.
This commit is contained in:
Serkyo 2026-04-30 12:19:21 +02:00
parent e20126fea5
commit edcde4ffd1
3 changed files with 74 additions and 3 deletions

1
Cargo.lock generated
View file

@ -1055,6 +1055,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"ash", "ash",
"thiserror 2.0.18", "thiserror 2.0.18",
"tracing",
] ]
[[package]] [[package]]

View file

@ -6,3 +6,4 @@ edition = "2024"
[dependencies] [dependencies]
ash = "0.38.0" ash = "0.38.0"
thiserror = "2.0.18" thiserror = "2.0.18"
tracing = "0.1.44"

View file

@ -1,13 +1,21 @@
pub mod error; pub mod error;
use std::ffi::c_char; use std::ffi::{CStr, c_char};
use ash::{Entry, Instance, vk}; use ash::{Entry, Instance, ext, vk};
use tracing::{debug, error, info, warn};
pub use error::RendererError; pub use error::RendererError;
/// The core renderer structure holding the Vulkan instance and debug resources.
pub struct Renderer { pub struct Renderer {
/// The entry point to the Vulkan library.
_entry: Entry, _entry: Entry,
/// The Vulkan instance handle.
instance: Instance, instance: Instance,
/// The debug utils instance, if enabled (debug builds only).
debug_utils: Option<ext::debug_utils::Instance>,
/// The debug messenger handle, if enabled (debug builds only).
debug_messenger: vk::DebugUtilsMessengerEXT,
} }
impl Renderer { impl Renderer {
@ -16,12 +24,14 @@ impl Renderer {
pub fn new(required_extensions: &[*const c_char]) -> Result<Self, RendererError> { pub fn new(required_extensions: &[*const c_char]) -> Result<Self, RendererError> {
let entry = unsafe { Entry::load() }?; let entry = unsafe { Entry::load() }?;
// Prepare extensions and layers
let mut extensions: Vec<*const c_char> = required_extensions.to_vec(); let mut extensions: Vec<*const c_char> = required_extensions.to_vec();
let mut layers: Vec<*const c_char> = Vec::new(); let mut layers: Vec<*const c_char> = Vec::new();
// Enable debug extensions and validation layers in debug builds
#[cfg(debug_assertions)] #[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()); layers.push(c"VK_LAYER_KHRONOS_validation".as_ptr());
} }
@ -33,11 +43,70 @@ impl Renderer {
.enabled_extension_names(&extensions) .enabled_extension_names(&extensions)
.enabled_layer_names(&layers); .enabled_layer_names(&layers);
// Create the Vulkan instance
let instance = unsafe { entry.create_instance(&create_info, None)? }; 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 { Ok(Self {
_entry: entry, _entry: entry,
instance, 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
} }