use super::{Instance, InstanceExtensions};
use crate::{
macros::{vulkan_bitflags, vulkan_enum},
DebugWrapper, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version,
VulkanError, VulkanObject,
};
use std::{
ffi::{c_void, CStr},
fmt::{Debug, Error as FmtError, Formatter},
mem::MaybeUninit,
panic::{catch_unwind, AssertUnwindSafe, RefUnwindSafe},
ptr, slice,
sync::Arc,
};
#[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: DebugWrapper<Arc<Instance>>,
_user_callback: Arc<DebugUtilsMessengerCallback>,
}
impl DebugUtilsMessenger {
#[inline]
pub fn new(
instance: Arc<Instance>,
create_info: DebugUtilsMessengerCreateInfo,
) -> Result<Self, Validated<VulkanError>> {
Self::validate_new(&instance, &create_info)?;
unsafe { Ok(Self::new_unchecked(instance, create_info)?) }
}
fn validate_new(
instance: &Instance,
create_info: &DebugUtilsMessengerCreateInfo,
) -> Result<(), Box<ValidationError>> {
if !instance.enabled_extensions().ext_debug_utils {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::InstanceExtension(
"ext_debug_utils",
)])]),
..Default::default()
}));
}
create_info
.validate(instance)
.map_err(|err| err.add_context("create_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
instance: Arc<Instance>,
create_info: DebugUtilsMessengerCreateInfo,
) -> Result<Self, VulkanError> {
let DebugUtilsMessengerCreateInfo {
message_severity,
message_type,
user_callback,
_ne: _,
} = create_info;
let create_info_vk = 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_ptr() as *const c_void as *mut _,
..Default::default()
};
let handle = {
let fns = instance.fns();
let mut output = MaybeUninit::uninit();
(fns.ext_debug_utils.create_debug_utils_messenger_ext)(
instance.handle(),
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok(DebugUtilsMessenger {
handle,
instance: DebugWrapper(instance),
_user_callback: 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()
}
}
#[derive(Clone)]
pub struct DebugUtilsMessengerCreateInfo {
pub message_severity: DebugUtilsMessageSeverity,
pub message_type: DebugUtilsMessageType,
pub user_callback: Arc<DebugUtilsMessengerCallback>,
pub _ne: crate::NonExhaustive,
}
impl DebugUtilsMessengerCreateInfo {
#[inline]
pub fn user_callback(user_callback: Arc<DebugUtilsMessengerCallback>) -> Self {
Self {
message_severity: DebugUtilsMessageSeverity::ERROR | DebugUtilsMessageSeverity::WARNING,
message_type: DebugUtilsMessageType::GENERAL,
user_callback,
_ne: crate::NonExhaustive(()),
}
}
#[inline]
pub(crate) fn validate(&self, instance: &Instance) -> Result<(), Box<ValidationError>> {
self.validate_raw(instance.api_version(), instance.enabled_extensions())
}
pub(crate) fn validate_raw(
&self,
instance_api_version: Version,
instance_extensions: &InstanceExtensions,
) -> Result<(), Box<ValidationError>> {
let &DebugUtilsMessengerCreateInfo {
message_severity,
message_type,
user_callback: _,
_ne: _,
} = self;
message_severity
.validate_instance_raw(instance_api_version, instance_extensions)
.map_err(|err| {
err.add_context("message_severity").set_vuids(&[
"VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-parameter",
])
})?;
if message_severity.is_empty() {
return Err(Box::new(ValidationError {
context: "message_severity".into(),
problem: "is empty".into(),
vuids: &["VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-requiredbitmask"],
..Default::default()
}));
}
message_type
.validate_instance_raw(instance_api_version, instance_extensions)
.map_err(|err| {
err.add_context("message_type")
.set_vuids(&["VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-parameter"])
})?;
if message_type.is_empty() {
return Err(Box::new(ValidationError {
context: "message_type".into(),
problem: "is empty".into(),
vuids: &["VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-requiredbitmask"],
..Default::default()
}));
}
Ok(())
}
}
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 DebugUtilsMessengerCallback(CallbackData);
type CallbackData = Box<
dyn Fn(DebugUtilsMessageSeverity, DebugUtilsMessageType, DebugUtilsMessengerCallbackData<'_>)
+ RefUnwindSafe
+ Send
+ Sync,
>;
impl DebugUtilsMessengerCallback {
pub unsafe fn new(
func: impl Fn(
DebugUtilsMessageSeverity,
DebugUtilsMessageType,
DebugUtilsMessengerCallbackData<'_>,
) + RefUnwindSafe
+ Send
+ Sync
+ 'static,
) -> Arc<Self> {
Arc::new(Self(Box::new(func)))
}
pub(crate) fn as_ptr(&self) -> *const CallbackData {
&self.0 as _
}
}
pub(super) unsafe extern "system" fn trampoline(
message_severity_vk: ash::vk::DebugUtilsMessageSeverityFlagsEXT,
message_types_vk: ash::vk::DebugUtilsMessageTypeFlagsEXT,
callback_data_vk: *const ash::vk::DebugUtilsMessengerCallbackDataEXT,
user_data_vk: *mut c_void,
) -> ash::vk::Bool32 {
let _ = catch_unwind(AssertUnwindSafe(move || {
let ash::vk::DebugUtilsMessengerCallbackDataEXT {
s_type: _,
p_next: _,
flags: _,
p_message_id_name,
message_id_number,
p_message,
queue_label_count,
p_queue_labels,
cmd_buf_label_count,
p_cmd_buf_labels,
object_count,
p_objects,
} = *callback_data_vk;
let callback_data = DebugUtilsMessengerCallbackData {
message_id_name: p_message_id_name
.as_ref()
.map(|p_message_id_name| CStr::from_ptr(p_message_id_name).to_str().unwrap()),
message_id_number,
message: CStr::from_ptr(p_message).to_str().unwrap(),
queue_labels: DebugUtilsMessengerCallbackLabelIter(
slice::from_raw_parts(p_queue_labels, queue_label_count as usize).iter(),
),
cmd_buf_labels: DebugUtilsMessengerCallbackLabelIter(
slice::from_raw_parts(p_cmd_buf_labels, cmd_buf_label_count as usize).iter(),
),
objects: DebugUtilsMessengerCallbackObjectNameInfoIter(
slice::from_raw_parts(p_objects, object_count as usize).iter(),
),
};
let user_callback = &*(user_data_vk as *mut CallbackData as *const CallbackData);
user_callback(
message_severity_vk.into(),
message_types_vk.into(),
callback_data,
);
}));
ash::vk::FALSE
}
#[non_exhaustive]
pub struct DebugUtilsMessengerCallbackData<'a> {
pub message_id_name: Option<&'a str>,
pub message_id_number: i32,
pub message: &'a str,
pub queue_labels: DebugUtilsMessengerCallbackLabelIter<'a>,
pub cmd_buf_labels: DebugUtilsMessengerCallbackLabelIter<'a>,
pub objects: DebugUtilsMessengerCallbackObjectNameInfoIter<'a>,
}
#[non_exhaustive]
pub struct DebugUtilsMessengerCallbackLabel<'a> {
pub label_name: &'a str,
pub color: &'a [f32; 4],
}
#[derive(Clone, Debug)]
pub struct DebugUtilsMessengerCallbackLabelIter<'a>(slice::Iter<'a, ash::vk::DebugUtilsLabelEXT>);
impl<'a> Iterator for DebugUtilsMessengerCallbackLabelIter<'a> {
type Item = DebugUtilsMessengerCallbackLabel<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|label| unsafe {
let &ash::vk::DebugUtilsLabelEXT {
s_type: _,
p_next: _,
p_label_name,
ref color,
} = label;
DebugUtilsMessengerCallbackLabel {
label_name: CStr::from_ptr(p_label_name).to_str().unwrap(),
color,
}
})
}
}
#[non_exhaustive]
pub struct DebugUtilsMessengerCallbackObjectNameInfo<'a> {
pub object_type: ash::vk::ObjectType,
pub object_handle: u64,
pub object_name: Option<&'a str>,
}
#[derive(Clone, Debug)]
pub struct DebugUtilsMessengerCallbackObjectNameInfoIter<'a>(
slice::Iter<'a, ash::vk::DebugUtilsObjectNameInfoEXT>,
);
impl<'a> Iterator for DebugUtilsMessengerCallbackObjectNameInfoIter<'a> {
type Item = DebugUtilsMessengerCallbackObjectNameInfo<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|info| unsafe {
let &ash::vk::DebugUtilsObjectNameInfoEXT {
s_type: _,
p_next: _,
object_type,
object_handle,
p_object_name,
} = info;
DebugUtilsMessengerCallbackObjectNameInfo {
object_type,
object_handle,
object_name: p_object_name
.as_ref()
.map(|p_object_name| CStr::from_ptr(p_object_name).to_str().unwrap()),
}
})
}
}
vulkan_bitflags! {
#[non_exhaustive]
DebugUtilsMessageSeverity = DebugUtilsMessageSeverityFlagsEXT(u32);
ERROR = ERROR,
WARNING = WARNING,
INFO = 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! {
#[non_exhaustive]
ValidationFeatureEnable = ValidationFeatureEnableEXT(i32);
GpuAssisted = GPU_ASSISTED,
GpuAssistedReserveBindingSlot = GPU_ASSISTED_RESERVE_BINDING_SLOT,
BestPractices = BEST_PRACTICES,
DebugPrintf = DEBUG_PRINTF,
SynchronizationValidation = SYNCHRONIZATION_VALIDATION,
}
vulkan_enum! {
#[non_exhaustive]
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,
message_type: DebugUtilsMessageType::GENERAL
| DebugUtilsMessageType::VALIDATION
| DebugUtilsMessageType::PERFORMANCE,
..DebugUtilsMessengerCreateInfo::user_callback(
DebugUtilsMessengerCallback::new(|_, _, _| {}),
)
},
)
}
.unwrap();
thread::spawn(move || {
drop(callback);
});
}
}