blob: 2bc061525a856c25ea82e3cb4db90c1505019c5f [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 callback called by intermediate layers or by the driver.
//!
//! When working on an application, it is recommended to register a debug callback. 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
//!
//! # Example
//!
//! ```
//! # use vulkano::instance::Instance;
//! # use std::sync::Arc;
//! # let instance: Arc<Instance> = return;
//! use vulkano::instance::debug::DebugCallback;
//!
//! let _callback = DebugCallback::errors_and_warnings(&instance, |msg| {
//! println!("Debug callback: {:?}", msg.description);
//! }).ok();
//! ```
//!
//! The type of `msg` in the callback is [`Message`](struct.Message.html).
//!
//! 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 `DebugCallback`'s constructor in a
//! variable, it will be immediately destroyed and your callback will not work.
//!
use crate::check_errors;
use crate::instance::Instance;
use crate::Error;
use crate::VulkanObject;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::mem::MaybeUninit;
use std::os::raw::c_void;
use std::panic;
use std::ptr;
use std::sync::Arc;
/// Registration of a callback called by validation layers.
///
/// The callback can be called as long as this object is alive.
#[must_use = "The DebugCallback object must be kept alive for as long as you want your callback \
to be called"]
pub struct DebugCallback {
instance: Arc<Instance>,
debug_report_callback: ash::vk::DebugUtilsMessengerEXT,
user_callback: Box<Box<dyn Fn(&Message) + Send>>,
}
impl DebugCallback {
/// Initializes a debug callback.
///
/// Panics generated by calling `user_callback` are ignored.
pub fn new<F>(
instance: &Arc<Instance>,
severity: MessageSeverity,
ty: MessageType,
user_callback: F,
) -> Result<DebugCallback, DebugCallbackCreationError>
where
F: Fn(&Message) + 'static + Send + panic::RefUnwindSafe,
{
if !instance.enabled_extensions().ext_debug_utils {
return Err(DebugCallbackCreationError::MissingExtension);
}
// 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(Box::new(user_callback) as Box<_>);
unsafe extern "system" fn callback(
severity: ash::vk::DebugUtilsMessageSeverityFlagsEXT,
ty: ash::vk::DebugUtilsMessageTypeFlagsEXT,
callback_data: *const ash::vk::DebugUtilsMessengerCallbackDataEXT,
user_data: *mut c_void,
) -> ash::vk::Bool32 {
let user_callback = user_data as *mut Box<dyn Fn()> as *const _;
let user_callback: &Box<dyn Fn(&Message)> = &*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: MessageSeverity {
information: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::INFO)
.is_empty(),
warning: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::WARNING)
.is_empty(),
error: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::ERROR)
.is_empty(),
verbose: !(severity & ash::vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE)
.is_empty(),
},
ty: MessageType {
general: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::GENERAL).is_empty(),
validation: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION)
.is_empty(),
performance: !(ty & ash::vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE)
.is_empty(),
},
layer_prefix,
description,
};
// Since we box the closure, the type system doesn't detect that the `UnwindSafe`
// bound is enforced. Therefore we enforce it manually.
let _ = panic::catch_unwind(panic::AssertUnwindSafe(move || {
user_callback(&message);
}));
ash::vk::FALSE
}
let severity = {
let mut flags = ash::vk::DebugUtilsMessageSeverityFlagsEXT::empty();
if severity.information {
flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::INFO;
}
if severity.warning {
flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::WARNING;
}
if severity.error {
flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::ERROR;
}
if severity.verbose {
flags |= ash::vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
}
flags
};
let ty = {
let mut flags = ash::vk::DebugUtilsMessageTypeFlagsEXT::empty();
if ty.general {
flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::GENERAL;
}
if ty.validation {
flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION;
}
if ty.performance {
flags |= ash::vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE;
}
flags
};
let infos = ash::vk::DebugUtilsMessengerCreateInfoEXT {
flags: ash::vk::DebugUtilsMessengerCreateFlagsEXT::empty(),
message_severity: severity,
message_type: ty,
pfn_user_callback: Some(callback),
p_user_data: &*user_callback as &Box<_> as *const Box<_> as *const c_void as *mut _,
..Default::default()
};
let fns = instance.fns();
let debug_report_callback = unsafe {
let mut output = MaybeUninit::uninit();
check_errors(fns.ext_debug_utils.create_debug_utils_messenger_ext(
instance.internal_object(),
&infos,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
Ok(DebugCallback {
instance: instance.clone(),
debug_report_callback,
user_callback,
})
}
/// Initializes a debug callback with errors and warnings.
///
/// Shortcut for `new(instance, MessageTypes::errors_and_warnings(), user_callback)`.
#[inline]
pub fn errors_and_warnings<F>(
instance: &Arc<Instance>,
user_callback: F,
) -> Result<DebugCallback, DebugCallbackCreationError>
where
F: Fn(&Message) + Send + 'static + panic::RefUnwindSafe,
{
DebugCallback::new(
instance,
MessageSeverity::errors_and_warnings(),
MessageType::general(),
user_callback,
)
}
}
impl Drop for DebugCallback {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.instance.fns();
fns.ext_debug_utils.destroy_debug_utils_messenger_ext(
self.instance.internal_object(),
self.debug_report_callback,
ptr::null(),
);
}
}
}
/// A message received by the callback.
pub struct Message<'a> {
/// Severity of message.
pub severity: MessageSeverity,
/// Type of message,
pub ty: MessageType,
/// 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,
}
/// Severity of message.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MessageSeverity {
/// An error that may cause undefined results, including an application crash.
pub error: bool,
/// An unexpected use.
pub warning: bool,
/// An informational message that may be handy when debugging an application.
pub information: bool,
/// Diagnostic information from the loader and layers.
pub verbose: bool,
}
impl MessageSeverity {
/// Builds a `MessageSeverity` with all fields set to `false` expect `error`.
#[inline]
pub const fn errors() -> MessageSeverity {
MessageSeverity {
error: true,
..MessageSeverity::none()
}
}
/// Builds a `MessageSeverity` with all fields set to `false` expect `warning`.
#[inline]
pub const fn warnings() -> MessageSeverity {
MessageSeverity {
warning: true,
..MessageSeverity::none()
}
}
/// Builds a `MessageSeverity` with all fields set to `false` expect `information`.
#[inline]
pub const fn information() -> MessageSeverity {
MessageSeverity {
information: true,
..MessageSeverity::none()
}
}
/// Builds a `MessageSeverity` with all fields set to `false` expect `verbose`.
#[inline]
pub const fn verbose() -> MessageSeverity {
MessageSeverity {
verbose: true,
..MessageSeverity::none()
}
}
/// Builds a `MessageSeverity` with all fields set to `false` expect `error`, `warning`
/// and `performance_warning`.
#[inline]
pub const fn errors_and_warnings() -> MessageSeverity {
MessageSeverity {
error: true,
warning: true,
..MessageSeverity::none()
}
}
/// Builds a `MessageSeverity` with all fields set to `false`.
#[inline]
pub const fn none() -> MessageSeverity {
MessageSeverity {
error: false,
warning: false,
information: false,
verbose: false,
}
}
/// Builds a `MessageSeverity` with all fields set to `true`.
#[inline]
pub const fn all() -> MessageSeverity {
MessageSeverity {
error: true,
warning: true,
information: true,
verbose: true,
}
}
}
impl std::ops::BitOr for MessageSeverity {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
MessageSeverity {
error: self.error | rhs.error,
warning: self.warning | rhs.warning,
information: self.information | rhs.information,
verbose: self.verbose | rhs.verbose,
}
}
}
/// Type of message.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct MessageType {
/// Specifies that some general event has occurred.
pub general: bool,
/// Specifies that something has occurred during validation against the vulkan specification
pub validation: bool,
/// Specifies a potentially non-optimal use of Vulkan
pub performance: bool,
}
impl MessageType {
/// Builds a `MessageType` with general field set to `true`.
#[inline]
pub const fn general() -> MessageType {
MessageType {
general: true,
validation: false,
performance: false,
}
}
/// Builds a `MessageType` with validation field set to `true`.
#[inline]
pub const fn validation() -> MessageType {
MessageType {
general: false,
validation: true,
performance: false,
}
}
/// Builds a `MessageType` with performance field set to `true`.
#[inline]
pub const fn performance() -> MessageType {
MessageType {
general: false,
validation: false,
performance: true,
}
}
/// Builds a `MessageType` with all fields set to `true`.
#[inline]
pub const fn all() -> MessageType {
MessageType {
general: true,
validation: true,
performance: true,
}
}
/// Builds a `MessageType` with all fields set to `false`.
#[inline]
pub const fn none() -> MessageType {
MessageType {
general: false,
validation: false,
performance: false,
}
}
}
impl std::ops::BitOr for MessageType {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
MessageType {
general: self.general | rhs.general,
validation: self.validation | rhs.validation,
performance: self.performance | rhs.performance,
}
}
}
/// Error that can happen when creating a debug callback.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DebugCallbackCreationError {
/// The `EXT_debug_utils` extension was not enabled.
MissingExtension,
}
impl error::Error for DebugCallbackCreationError {}
impl fmt::Display for DebugCallbackCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
DebugCallbackCreationError::MissingExtension => {
"the `EXT_debug_utils` extension was not enabled"
}
}
)
}
}
impl From<Error> for DebugCallbackCreationError {
#[inline]
fn from(err: Error) -> DebugCallbackCreationError {
panic!("unexpected error: {:?}", err)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn ensure_sendable() {
// It's useful to be able to initialize a DebugCallback on one thread
// and keep it alive on another thread.
let instance = instance!();
let severity = MessageSeverity::none();
let ty = MessageType::all();
let callback = DebugCallback::new(&instance, severity, ty, |_| {});
thread::spawn(move || {
let _ = callback;
});
}
}