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, CString},
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)?;
Ok(unsafe { 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 create_info_vk = create_info.to_vk();
let handle = {
let fns = instance.fns();
let mut output = MaybeUninit::uninit();
unsafe {
(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)?;
unsafe { output.assume_init() }
};
Ok(DebugUtilsMessenger {
handle,
instance: DebugWrapper(instance),
_user_callback: create_info.user_callback,
})
}
}
impl Drop for DebugUtilsMessenger {
#[inline]
fn drop(&mut self) {
let fns = self.instance.fns();
unsafe {
(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(())
}
pub(crate) fn to_vk(&self) -> ash::vk::DebugUtilsMessengerCreateInfoEXT<'static> {
let &Self {
message_type,
message_severity,
ref user_callback,
_ne: _,
} = self;
ash::vk::DebugUtilsMessengerCreateInfoEXT::default()
.flags(ash::vk::DebugUtilsMessengerCreateFlagsEXT::empty())
.message_severity(message_severity.into())
.message_type(message_type.into())
.pfn_user_callback(Some(trampoline))
.user_data(user_callback.as_ptr().cast_mut().cast())
}
}
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 {
ptr::addr_of!(self.0)
}
}
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 {
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,
..
} = unsafe { *callback_data_vk };
let callback_data = DebugUtilsMessengerCallbackData {
message_id_name: unsafe { p_message_id_name.as_ref() }.map(|p_message_id_name| {
unsafe { CStr::from_ptr(p_message_id_name) }
.to_str()
.unwrap()
}),
message_id_number,
message: unsafe { CStr::from_ptr(p_message) }.to_str().unwrap(),
queue_labels: DebugUtilsMessengerCallbackLabelIter(
if p_queue_labels.is_null() {
&[]
} else {
unsafe { slice::from_raw_parts(p_queue_labels, queue_label_count as usize) }
}
.iter(),
),
cmd_buf_labels: DebugUtilsMessengerCallbackLabelIter(
if p_cmd_buf_labels.is_null() {
&[]
} else {
unsafe { slice::from_raw_parts(p_cmd_buf_labels, cmd_buf_label_count as usize) }
}
.iter(),
),
objects: DebugUtilsMessengerCallbackObjectNameInfoIter(
if p_objects.is_null() {
&[]
} else {
unsafe { slice::from_raw_parts(p_objects, object_count as usize) }
}
.iter(),
),
};
let user_callback: &CallbackData = unsafe { &*user_data_vk.cast_const().cast() };
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<'a>>,
);
impl<'a> Iterator for DebugUtilsMessengerCallbackLabelIter<'a> {
type Item = DebugUtilsMessengerCallbackLabel<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|label| DebugUtilsMessengerCallbackLabel {
label_name: unsafe { label.label_name_as_c_str() }
.unwrap()
.to_str()
.unwrap(),
color: &label.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<'a>>,
);
impl<'a> Iterator for DebugUtilsMessengerCallbackObjectNameInfoIter<'a> {
type Item = DebugUtilsMessengerCallbackObjectNameInfo<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|info| {
let &ash::vk::DebugUtilsObjectNameInfoEXT {
object_type,
object_handle,
p_object_name,
..
} = info;
DebugUtilsMessengerCallbackObjectNameInfo {
object_type,
object_handle,
object_name: unsafe { p_object_name.as_ref() }.map(|p_object_name| {
unsafe { 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(()),
}
}
}
impl DebugUtilsLabel {
pub(crate) fn to_vk<'a>(
&self,
fields1_vk: &'a DebugUtilsLabelFields1Vk,
) -> ash::vk::DebugUtilsLabelEXT<'a> {
let &Self {
label_name: _,
color,
_ne,
} = self;
let DebugUtilsLabelFields1Vk { label_name_vk } = fields1_vk;
ash::vk::DebugUtilsLabelEXT::default()
.label_name(label_name_vk)
.color(color)
}
pub(crate) fn to_vk_fields1(&self) -> DebugUtilsLabelFields1Vk {
let label_name_vk = CString::new(self.label_name.as_str()).unwrap();
DebugUtilsLabelFields1Vk { label_name_vk }
}
}
pub(crate) struct DebugUtilsLabelFields1Vk {
pub(crate) label_name_vk: CString,
}
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 = DebugUtilsMessenger::new(
instance,
DebugUtilsMessengerCreateInfo {
message_severity: DebugUtilsMessageSeverity::ERROR,
message_type: DebugUtilsMessageType::GENERAL
| DebugUtilsMessageType::VALIDATION
| DebugUtilsMessageType::PERFORMANCE,
..DebugUtilsMessengerCreateInfo::user_callback(unsafe {
DebugUtilsMessengerCallback::new(|_, _, _| {})
})
},
)
.unwrap();
thread::spawn(move || {
drop(callback);
});
}
}