aboutsummaryrefslogtreecommitdiff
path: root/src/instance/debug.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/instance/debug.rs')
-rw-r--r--src/instance/debug.rs461
1 files changed, 461 insertions, 0 deletions
diff --git a/src/instance/debug.rs b/src/instance/debug.rs
new file mode 100644
index 0000000..2bc0615
--- /dev/null
+++ b/src/instance/debug.rs
@@ -0,0 +1,461 @@
+// 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;
+ });
+ }
+}