blob: 7e5a75e81fb30c882d78b1590a644c9595ed44ab [file] [log] [blame]
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Debug messenger called by intermediate layers or by the driver.
//!
//! When working on an application, it is recommended to register a debug messenger. For example if
//! you enable the validation layers provided by the official Vulkan SDK, they will warn you about
//! invalid API usages or performance problems by calling this callback. The callback can also
//! be called by the driver or by whatever intermediate layer is activated.
//!
//! Note that the vulkano library can also emit messages to warn you about performance issues.
//! TODO: ^ that's not the case yet, need to choose whether we keep this idea
//!
//! # Examples
//!
//! ```
//! # use vulkano::instance::Instance;
//! # use std::sync::Arc;
//! # let instance: Arc<Instance> = return;
//! use vulkano::instance::debug::{DebugUtilsMessenger, DebugUtilsMessengerCreateInfo};
//!
//! let _callback = unsafe {
//! DebugUtilsMessenger::new(
//! instance,
//! DebugUtilsMessengerCreateInfo::user_callback(Arc::new(|msg| {
//! println!("Debug callback: {:?}", msg.description);
//! })),
//! ).ok()
//! };
//! ```
//!
//! The type of `msg` in the callback is [`Message`].
//!
//! Note that you must keep the `_callback` object alive for as long as you want your callback to
//! be callable. If you don't store the return value of `DebugUtilsMessenger`'s constructor in a
//! variable, it will be immediately destroyed and your callback will not work.
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>;
/// Registration of a callback called by validation layers.
///
/// The callback can be called as long as this object is alive.
#[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 {
/// Initializes a debug callback.
///
/// # Panics
///
/// - Panics if the `message_severity` or `message_type` members of `create_info` are empty.
///
/// # Safety
///
/// - `create_info.user_callback` must not make any calls to the Vulkan API.
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::new`",
requires_one_of: RequiresOneOf {
instance_extensions: &["ext_debug_utils"],
..Default::default()
},
});
}
// VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-parameter
message_severity.validate_instance(instance)?;
// VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-requiredbitmask
assert!(!message_severity.is_empty());
// VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-parameter
message_type.validate_instance(instance)?;
// VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-requiredbitmask
assert!(!message_type.is_empty());
// VUID-PFN_vkDebugUtilsMessengerCallbackEXT-None-04769
// Can't be checked, creation is unsafe.
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;
// Note that we need to double-box the callback, because a `*const Fn()` is a fat pointer
// that can't be cast to a `*const c_void`.
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 {
// Since we box the closure, the type system doesn't detect that the `UnwindSafe`
// bound is enforced. Therefore we enforce it manually.
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
}
/// Error that can happen when creating a `DebugUtilsMessenger`.
#[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,
}
}
}
/// Parameters to create a `DebugUtilsMessenger`.
#[derive(Clone)]
pub struct DebugUtilsMessengerCreateInfo {
/// The message severity types that the callback should be called for.
///
/// The value must not be empty.
///
/// The default value is `MessageSeverity::errors_and_warnings()`.
pub message_severity: DebugUtilsMessageSeverity,
/// The message types that the callback should be called for.
///
/// The value must not be empty.
///
/// The default value is `MessageType::general()`.
pub message_type: DebugUtilsMessageType,
/// The closure that should be called.
///
/// The closure must not make any calls to the Vulkan API.
/// If the closure panics, the panic is caught and ignored.
///
/// The callback is provided inside an `Arc` so that it can be shared across multiple
/// messengers.
pub user_callback: UserCallback,
pub _ne: crate::NonExhaustive,
}
impl DebugUtilsMessengerCreateInfo {
/// Returns a `DebugUtilsMessengerCreateInfo` with the specified `user_callback`.
#[inline]
pub fn user_callback(user_callback: UserCallback) -> Self {
Self {
message_severity: DebugUtilsMessageSeverity::ERROR | DebugUtilsMessageSeverity::WARNING,
message_type: DebugUtilsMessageType::GENERAL,
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()
}
}
/// A message received by the callback.
pub struct Message<'a> {
/// Severity of message.
pub severity: DebugUtilsMessageSeverity,
/// Type of message,
pub ty: DebugUtilsMessageType,
/// Prefix of the layer that reported this message or `None` if unknown.
pub layer_prefix: Option<&'a str>,
/// Description of the message.
pub description: &'a str,
}
vulkan_bitflags! {
#[non_exhaustive]
/// Severity of message.
DebugUtilsMessageSeverity = DebugUtilsMessageSeverityFlagsEXT(u32);
/// An error that may cause undefined results, including an application crash.
ERROR = ERROR,
/// An unexpected use.
WARNING = WARNING,
/// An informational message that may be handy when debugging an application.
INFO = INFO,
/// Diagnostic information from the loader and layers.
VERBOSE = VERBOSE,
}
vulkan_bitflags! {
#[non_exhaustive]
/// Type of message.
DebugUtilsMessageType = DebugUtilsMessageTypeFlagsEXT(u32);
/// Specifies that some general event has occurred.
GENERAL = GENERAL,
/// Specifies that something has occurred during validation against the vulkan specification
VALIDATION = VALIDATION,
/// Specifies a potentially non-optimal use of Vulkan
PERFORMANCE = PERFORMANCE,
}
/// A label to associate with a span of work in a queue.
///
/// When debugging, labels can be useful to identify which queue, or where in a specific queue,
/// something happened.
#[derive(Clone, Debug)]
pub struct DebugUtilsLabel {
/// The name of the label.
///
/// The default value is empty.
pub label_name: String,
/// An RGBA color value that is associated with the label, with values in the range `0.0..=1.0`.
///
/// If set to `[0.0; 4]`, the value is ignored.
///
/// The default value is `[0.0; 4]`.
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]
/// Features of the validation layer to enable.
ValidationFeatureEnable = ValidationFeatureEnableEXT(i32);
/// The validation layer will use shader programs running on the GPU to provide additional
/// validation.
///
/// This must not be used together with `DebugPrintf`.
GpuAssisted = GPU_ASSISTED,
/// The validation layer will reserve and use one descriptor set slot for its own use.
/// The limit reported by
/// [`max_bound_descriptor_sets`](crate::device::Properties::max_bound_descriptor_sets)
/// will be reduced by 1.
///
/// `GpuAssisted` must also be enabled.
GpuAssistedReserveBindingSlot = GPU_ASSISTED_RESERVE_BINDING_SLOT,
/// The validation layer will report recommendations that are not strictly errors,
/// but that may be considered good Vulkan practice.
BestPractices = BEST_PRACTICES,
/// The validation layer will process `debugPrintfEXT` operations in shaders, and send them
/// to the debug callback.
///
/// This must not be used together with `GpuAssisted`.
DebugPrintf = DEBUG_PRINTF,
/// The validation layer will report errors relating to synchronization, such as data races and
/// the use of synchronization primitives.
SynchronizationValidation = SYNCHRONIZATION_VALIDATION,
}
vulkan_enum! {
#[non_exhaustive]
/// Features of the validation layer to disable.
ValidationFeatureDisable = ValidationFeatureDisableEXT(i32);
/// All validation is disabled.
All = ALL,
/// Shader validation is disabled.
Shaders = SHADERS,
/// Thread safety validation is disabled.
ThreadSafety = THREAD_SAFETY,
/// Stateless parameter validation is disabled.
ApiParameters = API_PARAMETERS,
/// Object lifetime validation is disabled.
ObjectLifetimes = OBJECT_LIFETIMES,
/// Core validation checks are disabled.
///
/// This also disables shader validation and GPU-assisted validation.
CoreChecks = CORE_CHECKS,
/// Protection against duplicate non-dispatchable handles is disabled.
UniqueHandles = UNIQUE_HANDLES,
/// Results of shader validation will not be cached, and are validated from scratch each time.
ShaderValidationCache = SHADER_VALIDATION_CACHE,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
instance::{InstanceCreateInfo, InstanceExtensions},
VulkanLibrary,
};
use std::thread;
#[test]
fn ensure_sendable() {
// It's useful to be able to initialize a DebugUtilsMessenger on one thread
// and keep it alive on another thread.
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(Arc::new(|_| {}))
},
)
}
.unwrap();
thread::spawn(move || {
drop(callback);
});
}
}