use super::Instance;
use crate::{
macros::{vulkan_bitflags, vulkan_enum},
RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
};
use std::{
error::Error,
ffi::{c_void, CStr},
fmt::{Debug, Display, Error as FmtError, Formatter},
mem::MaybeUninit,
panic::{catch_unwind, AssertUnwindSafe, RefUnwindSafe},
ptr,
sync::Arc,
};
pub(super) type UserCallback = Arc<dyn Fn(&Message<'_>) + RefUnwindSafe + Send + Sync>;
#[must_use = "The DebugUtilsMessenger object must be kept alive for as long as you want your callback to be called"]
pub struct DebugUtilsMessenger {
handle: ash::vk::DebugUtilsMessengerEXT,
instance: Arc<Instance>,
_user_callback: Box<UserCallback>,
}
impl DebugUtilsMessenger {
pub unsafe fn new(
instance: Arc<Instance>,
mut create_info: DebugUtilsMessengerCreateInfo,
) -> Result<Self, DebugUtilsMessengerCreationError> {
Self::validate_create(&instance, &mut create_info)?;
let (handle, user_callback) = Self::record_create(&instance, create_info)?;
Ok(DebugUtilsMessenger {
handle,
instance,
_user_callback: user_callback,
})
}
fn validate_create(
instance: &Instance,
create_info: &mut DebugUtilsMessengerCreateInfo,
) -> Result<(), DebugUtilsMessengerCreationError> {
let &mut DebugUtilsMessengerCreateInfo {
message_type,
message_severity,
user_callback: _,
_ne: _,
} = create_info;
if !instance.enabled_extensions().ext_debug_utils {
return Err(DebugUtilsMessengerCreationError::RequirementNotMet {
required_for: "`DebugUtilsMessenger`",
requires_one_of: RequiresOneOf {
instance_extensions: &["ext_debug_utils"],
..Default::default()
},
});
}
message_severity.validate_instance(instance)?;
assert!(!message_severity.is_empty());
message_type.validate_instance(instance)?;
assert!(!message_type.is_empty());
Ok(())
}
unsafe fn record_create(
instance: &Instance,
create_info: DebugUtilsMessengerCreateInfo,
) -> Result<
(ash::vk::DebugUtilsMessengerEXT, Box<UserCallback>),
DebugUtilsMessengerCreationError,
> {
let DebugUtilsMessengerCreateInfo {
message_severity,
message_type,
user_callback,
_ne: _,
} = create_info;
let user_callback = Box::new(user_callback);
let create_info = ash::vk::DebugUtilsMessengerCreateInfoEXT {
flags: ash::vk::DebugUtilsMessengerCreateFlagsEXT::empty(),
message_severity: message_severity.into(),
message_type: message_type.into(),
pfn_user_callback: Some(trampoline),
p_user_data: &*user_callback as &Arc<_> as *const Arc<_> as *const c_void as *mut _,
..Default::default()
};
let fns = instance.fns();
let handle = {
let mut output = MaybeUninit::uninit();
(fns.ext_debug_utils.create_debug_utils_messenger_ext)(
instance.handle(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok((handle, user_callback))
}
}
impl Drop for DebugUtilsMessenger {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.instance.fns();
(fns.ext_debug_utils.destroy_debug_utils_messenger_ext)(
self.instance.handle(),
self.handle,
ptr::null(),
);
}
}
}
impl Debug for DebugUtilsMessenger {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
let Self {
handle,
instance,
_user_callback: _,
} = self;
f.debug_struct("DebugUtilsMessenger")
.field("handle", handle)
.field("instance", instance)
.finish_non_exhaustive()
}
}
pub(super) unsafe extern "system" fn trampoline(
message_severity: ash::vk::DebugUtilsMessageSeverityFlagsEXT,
message_types: ash::vk::DebugUtilsMessageTypeFlagsEXT,
callback_data: *const ash::vk::DebugUtilsMessengerCallbackDataEXT,
user_data: *mut c_void,
) -> ash::vk::Bool32 {
let _ = catch_unwind(AssertUnwindSafe(move || {
let user_callback = user_data as *mut UserCallback as *const _;
let user_callback: &UserCallback = &*user_callback;
let layer_prefix = (*callback_data)
.p_message_id_name
.as_ref()
.map(|msg_id_name| {
CStr::from_ptr(msg_id_name)
.to_str()
.expect("debug callback message not utf-8")
});
let description = CStr::from_ptr((*callback_data).p_message)
.to_str()
.expect("debug callback message not utf-8");
let message = Message {
severity: message_severity.into(),
ty: message_types.into(),
layer_prefix,
description,
};
user_callback(&message);
}));
ash::vk::FALSE
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DebugUtilsMessengerCreationError {
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
}
impl Error for DebugUtilsMessengerCreationError {}
impl Display for DebugUtilsMessengerCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
}
}
}
impl From<VulkanError> for DebugUtilsMessengerCreationError {
fn from(err: VulkanError) -> DebugUtilsMessengerCreationError {
panic!("unexpected error: {:?}", err)
}
}
impl From<RequirementNotMet> for DebugUtilsMessengerCreationError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
#[derive(Clone)]
pub struct DebugUtilsMessengerCreateInfo {
pub message_severity: DebugUtilsMessageSeverity,
pub message_type: DebugUtilsMessageType,
pub user_callback: UserCallback,
pub _ne: crate::NonExhaustive,
}
impl DebugUtilsMessengerCreateInfo {
#[inline]
pub fn user_callback(user_callback: UserCallback) -> Self {
Self {
message_severity: DebugUtilsMessageSeverity {
error: true,
warning: true,
..DebugUtilsMessageSeverity::empty()
},
message_type: DebugUtilsMessageType {
general: true,
..DebugUtilsMessageType::empty()
},
user_callback,
_ne: crate::NonExhaustive(()),
}
}
}
impl Debug for DebugUtilsMessengerCreateInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
let Self {
message_severity,
message_type,
user_callback: _,
_ne: _,
} = self;
f.debug_struct("DebugUtilsMessengerCreateInfo")
.field("message_severity", message_severity)
.field("message_type", message_type)
.finish_non_exhaustive()
}
}
pub struct Message<'a> {
pub severity: DebugUtilsMessageSeverity,
pub ty: DebugUtilsMessageType,
pub layer_prefix: Option<&'a str>,
pub description: &'a str,
}
vulkan_bitflags! {
#[non_exhaustive]
DebugUtilsMessageSeverity = DebugUtilsMessageSeverityFlagsEXT(u32);
error = ERROR,
warning = WARNING,
information = INFO,
verbose = VERBOSE,
}
vulkan_bitflags! {
#[non_exhaustive]
DebugUtilsMessageType = DebugUtilsMessageTypeFlagsEXT(u32);
general = GENERAL,
validation = VALIDATION,
performance = PERFORMANCE,
}
#[derive(Clone, Debug)]
pub struct DebugUtilsLabel {
pub label_name: String,
pub color: [f32; 4],
pub _ne: crate::NonExhaustive,
}
impl Default for DebugUtilsLabel {
#[inline]
fn default() -> Self {
Self {
label_name: String::new(),
color: [0.0; 4],
_ne: crate::NonExhaustive(()),
}
}
}
vulkan_enum! {
ValidationFeatureEnable = ValidationFeatureEnableEXT(i32);
GpuAssisted = GPU_ASSISTED,
GpuAssistedReserveBindingSlot = GPU_ASSISTED_RESERVE_BINDING_SLOT,
BestPractices = BEST_PRACTICES,
DebugPrintf = DEBUG_PRINTF,
SynchronizationValidation = SYNCHRONIZATION_VALIDATION,
}
vulkan_enum! {
ValidationFeatureDisable = ValidationFeatureDisableEXT(i32);
All = ALL,
Shaders = SHADERS,
ThreadSafety = THREAD_SAFETY,
ApiParameters = API_PARAMETERS,
ObjectLifetimes = OBJECT_LIFETIMES,
CoreChecks = CORE_CHECKS,
UniqueHandles = UNIQUE_HANDLES,
ShaderValidationCache = SHADER_VALIDATION_CACHE,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
instance::{InstanceCreateInfo, InstanceExtensions},
VulkanLibrary,
};
use std::thread;
#[test]
fn ensure_sendable() {
let instance = {
let library = match VulkanLibrary::new() {
Ok(x) => x,
Err(_) => return,
};
match Instance::new(
library,
InstanceCreateInfo {
enabled_extensions: InstanceExtensions {
ext_debug_utils: true,
..InstanceExtensions::empty()
},
..Default::default()
},
) {
Ok(x) => x,
Err(_) => return,
}
};
let callback = unsafe {
DebugUtilsMessenger::new(
instance,
DebugUtilsMessengerCreateInfo {
message_severity: DebugUtilsMessageSeverity {
error: true,
..DebugUtilsMessageSeverity::empty()
},
message_type: DebugUtilsMessageType {
general: true,
validation: true,
performance: true,
..DebugUtilsMessageType::empty()
},
..DebugUtilsMessengerCreateInfo::user_callback(Arc::new(|_| {}))
},
)
}
.unwrap();
thread::spawn(move || {
drop(callback);
});
}
}