use core::slice;
use std::{borrow::Cow, ffi::{CStr, CString, c_void}, thread};
use ash::vk;
pub struct DebugCallback {
callback: vk::DebugUtilsMessengerEXT,
loader: ash::ext::debug_utils::Instance,
}
impl DebugCallback {
pub fn destroy(&self) {
unsafe {
self.loader
.destroy_debug_utils_messenger(self.callback, None);
};
}
pub fn new(entry: &ash::Entry, instance: &ash::Instance) -> Self {
let debug_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
.message_severity(
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE
| vk::DebugUtilsMessageSeverityFlagsEXT::INFO
| vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
| vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
)
.message_type(
vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
| vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
| vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
)
.pfn_user_callback(Some(debug_utils_messenger_callback));
let loader = ash::ext::debug_utils::Instance::new(entry, instance);
let callback = unsafe {
loader
.create_debug_utils_messenger(&debug_info, None)
.unwrap()
};
DebugCallback { callback, loader }
}
}
#[derive(Debug)]
struct ValidationLayerProperties {
layer_description: CString,
layer_spec_version: u32,
}
#[derive(Debug)]
pub struct DebugUtilsMessengerUserData {
validation_layer_properties: Option<ValidationLayerProperties>,
has_obs_layer: bool,
}
unsafe extern "system" fn debug_utils_messenger_callback(
message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
message_type: vk::DebugUtilsMessageTypeFlagsEXT,
callback_data_ptr: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
user_data: *mut c_void,
) -> vk::Bool32 {
if thread::panicking() {
return vk::FALSE;
}
let cd = unsafe { &*callback_data_ptr };
let user_data = unsafe { &*user_data.cast::<DebugUtilsMessengerUserData>() };
const VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912: i32 = 0x56146426;
if cd.message_id_number == VUID_VKCMDENDDEBUGUTILSLABELEXT_COMMANDBUFFER_01912 {
if let Some(layer_properties) = user_data.validation_layer_properties.as_ref() {
if layer_properties.layer_description.as_ref() == c"Khronos Validation Layer"
&& layer_properties.layer_spec_version >= vk::make_api_version(0, 1, 3, 240)
&& layer_properties.layer_spec_version <= vk::make_api_version(0, 1, 3, 250)
{
return vk::FALSE;
}
}
}
const VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781: i32 = 0x4c8929c1;
if cd.message_id_number == VUID_VKSWAPCHAINCREATEINFOKHR_PNEXT_07781 {
return vk::FALSE;
}
const VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627: i32 = 0x45125641;
if cd.message_id_number == VUID_VKRENDERPASSBEGININFO_FRAMEBUFFER_04627
&& user_data.has_obs_layer
{
return vk::FALSE;
}
const VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184: i32 = 0x45ef177c;
if cd.message_id_number == VUID_VKCMDCOPYIMAGETOBUFFER_PREGIONS_00184 {
return vk::FALSE;
}
const VUID_STANDALONESPIRV_NONE_10684: i32 = 0xb210f7c2_u32 as i32;
if cd.message_id_number == VUID_STANDALONESPIRV_NONE_10684 {
return vk::FALSE;
}
let level = match message_severity {
vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Trace,
vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Debug,
vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn,
vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error,
_ => log::Level::Warn,
};
let message_id_name =
unsafe { cd.message_id_name_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
let message = unsafe { cd.message_as_c_str() }.map_or(Cow::Borrowed(""), CStr::to_string_lossy);
let _ = std::panic::catch_unwind(|| {
log::log!(
level,
"{:?} [{} (0x{:x})]\n\t{}",
message_type,
message_id_name,
cd.message_id_number,
message,
);
});
if cd.queue_label_count != 0 {
let labels =
unsafe { slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize) };
let names = labels
.iter()
.flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
.collect::<Vec<_>>();
let _ = std::panic::catch_unwind(|| {
log::log!(level, "\tqueues: {}", names.join(", "));
});
}
if cd.cmd_buf_label_count != 0 {
let labels =
unsafe { slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize) };
let names = labels
.iter()
.flat_map(|dul_obj| unsafe { dul_obj.label_name_as_c_str() }.map(CStr::to_string_lossy))
.collect::<Vec<_>>();
let _ = std::panic::catch_unwind(|| {
log::log!(level, "\tcommand buffers: {}", names.join(", "));
});
}
if cd.object_count != 0 {
let labels = unsafe { slice::from_raw_parts(cd.p_objects, cd.object_count as usize) };
let names = labels
.iter()
.map(|obj_info| {
let name = unsafe { obj_info.object_name_as_c_str() }
.map_or(Cow::Borrowed("?"), CStr::to_string_lossy);
format!(
"(type: {:?}, hndl: 0x{:x}, name: {})",
obj_info.object_type, obj_info.object_handle, name
)
})
.collect::<Vec<_>>();
let _ = std::panic::catch_unwind(|| {
log::log!(level, "\tobjects: {}", names.join(", "));
});
}
#[cfg(feature = "validation_canary")]
if cfg!(debug_assertions) && level == log::Level::Error {
use alloc::string::ToString as _;
crate::VALIDATION_CANARY.add(message.to_string());
}
vk::FALSE
}