diff options
Diffstat (limited to 'src/instance')
-rw-r--r-- | src/instance/debug.rs | 755 | ||||
-rw-r--r-- | src/instance/extensions.rs | 153 | ||||
-rw-r--r-- | src/instance/instance.rs | 693 | ||||
-rw-r--r-- | src/instance/layers.rs | 194 | ||||
-rw-r--r-- | src/instance/loader.rs | 316 | ||||
-rw-r--r-- | src/instance/mod.rs | 880 |
6 files changed, 1321 insertions, 1670 deletions
diff --git a/src/instance/debug.rs b/src/instance/debug.rs index 2bc0615..7e5a75e 100644 --- a/src/instance/debug.rs +++ b/src/instance/debug.rs @@ -7,9 +7,9 @@ // notice may not be copied, modified, or distributed except // according to those terms. -//! Debug callback called by intermediate layers or by the driver. +//! Debug messenger called by intermediate layers or by the driver. //! -//! When working on an application, it is recommended to register a debug callback. For example if +//! 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. @@ -17,445 +17,528 @@ //! 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 +//! # Examples //! //! ``` //! # use vulkano::instance::Instance; //! # use std::sync::Arc; //! # let instance: Arc<Instance> = return; -//! use vulkano::instance::debug::DebugCallback; +//! use vulkano::instance::debug::{DebugUtilsMessenger, DebugUtilsMessengerCreateInfo}; //! -//! let _callback = DebugCallback::errors_and_warnings(&instance, |msg| { -//! println!("Debug callback: {:?}", msg.description); -//! }).ok(); +//! 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`](struct.Message.html). +//! 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 `DebugCallback`'s constructor in a +//! 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 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; +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 DebugCallback object must be kept alive for as long as you want your callback \ - to be called"] -pub struct DebugCallback { +#[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>, - debug_report_callback: ash::vk::DebugUtilsMessengerEXT, - user_callback: Box<Box<dyn Fn(&Message) + Send>>, + _user_callback: Box<UserCallback>, } -impl DebugCallback { +impl DebugUtilsMessenger { /// 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, - { + /// # 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(DebugCallbackCreationError::MissingExtension); + return Err(DebugUtilsMessengerCreationError::RequirementNotMet { + required_for: "`DebugUtilsMessenger::new`", + requires_one_of: RequiresOneOf { + instance_extensions: &["ext_debug_utils"], + ..Default::default() + }, + }); } - // 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, - }; + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-parameter + message_severity.validate_instance(instance)?; - // 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); - })); + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-requiredbitmask + assert!(!message_severity.is_empty()); - ash::vk::FALSE - } + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-parameter + message_type.validate_instance(instance)?; - 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 - }; + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-requiredbitmask + assert!(!message_type.is_empty()); - 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 - }; + // 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 infos = ash::vk::DebugUtilsMessengerCreateInfoEXT { + let create_info = 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 _, + 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 debug_report_callback = unsafe { + let handle = { let mut output = MaybeUninit::uninit(); - check_errors(fns.ext_debug_utils.create_debug_utils_messenger_ext( - instance.internal_object(), - &infos, + (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(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, - ) + Ok((handle, user_callback)) } } -impl Drop for DebugCallback { +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.internal_object(), - self.debug_report_callback, + (fns.ext_debug_utils.destroy_debug_utils_messenger_ext)( + self.instance.handle(), + self.handle, 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, +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() + } } -/// 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, +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 } -impl MessageSeverity { - /// Builds a `MessageSeverity` with all fields set to `false` expect `error`. - #[inline] - pub const fn errors() -> MessageSeverity { - MessageSeverity { - error: true, - ..MessageSeverity::none() - } - } +/// 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, + }, +} - /// Builds a `MessageSeverity` with all fields set to `false` expect `warning`. - #[inline] - pub const fn warnings() -> MessageSeverity { - MessageSeverity { - warning: true, - ..MessageSeverity::none() +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, + ), } } +} - /// Builds a `MessageSeverity` with all fields set to `false` expect `information`. - #[inline] - pub const fn information() -> MessageSeverity { - MessageSeverity { - information: true, - ..MessageSeverity::none() - } +impl From<VulkanError> for DebugUtilsMessengerCreationError { + fn from(err: VulkanError) -> DebugUtilsMessengerCreationError { + panic!("unexpected error: {:?}", err) } +} - /// Builds a `MessageSeverity` with all fields set to `false` expect `verbose`. - #[inline] - pub const fn verbose() -> MessageSeverity { - MessageSeverity { - verbose: true, - ..MessageSeverity::none() +impl From<RequirementNotMet> for DebugUtilsMessengerCreationError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, } } +} - /// 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() - } - } +/// 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, - /// Builds a `MessageSeverity` with all fields set to `false`. - #[inline] - pub const fn none() -> MessageSeverity { - MessageSeverity { - error: false, - warning: false, - information: false, - verbose: false, - } - } + /// 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, - /// Builds a `MessageSeverity` with all fields set to `true`. + pub _ne: crate::NonExhaustive, +} + +impl DebugUtilsMessengerCreateInfo { + /// Returns a `DebugUtilsMessengerCreateInfo` with the specified `user_callback`. #[inline] - pub const fn all() -> MessageSeverity { - MessageSeverity { - error: true, - warning: true, - information: true, - verbose: true, + 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 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, - } +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() } } -/// Type of message. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct MessageType { +/// 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. - pub general: bool, + GENERAL = GENERAL, + /// Specifies that something has occurred during validation against the vulkan specification - pub validation: bool, + VALIDATION = VALIDATION, + /// Specifies a potentially non-optimal use of Vulkan - pub performance: bool, + PERFORMANCE = PERFORMANCE, } -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, - } - } +/// 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, - /// Builds a `MessageType` with performance field set to `true`. - #[inline] - pub const fn performance() -> MessageType { - MessageType { - general: false, - validation: false, - performance: true, - } - } + /// 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], - /// Builds a `MessageType` with all fields set to `true`. - #[inline] - pub const fn all() -> MessageType { - MessageType { - general: true, - validation: true, - performance: true, - } - } + pub _ne: crate::NonExhaustive, +} - /// Builds a `MessageType` with all fields set to `false`. +impl Default for DebugUtilsLabel { #[inline] - pub const fn none() -> MessageType { - MessageType { - general: false, - validation: false, - performance: false, + fn default() -> Self { + Self { + label_name: String::new(), + color: [0.0; 4], + _ne: crate::NonExhaustive(()), } } } -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, - } - } -} +vulkan_enum! { + #[non_exhaustive] -/// 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, -} + /// Features of the validation layer to enable. + ValidationFeatureEnable = ValidationFeatureEnableEXT(i32); -impl error::Error for DebugCallbackCreationError {} + /// 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, -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" - } - } - ) - } + /// 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, } -impl From<Error> for DebugCallbackCreationError { - #[inline] - fn from(err: Error) -> DebugCallbackCreationError { - panic!("unexpected error: {:?}", err) - } +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 DebugCallback on one thread + // It's useful to be able to initialize a DebugUtilsMessenger 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, |_| {}); + 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 || { - let _ = callback; + drop(callback); }); } } diff --git a/src/instance/extensions.rs b/src/instance/extensions.rs index dab0b12..3aac844 100644 --- a/src/instance/extensions.rs +++ b/src/instance/extensions.rs @@ -7,138 +7,10 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use crate::check_errors; -pub use crate::extensions::{ - ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError, -}; -use crate::instance::loader; -use crate::instance::loader::LoadingError; -use std::ffi::CStr; -use std::ptr; +pub use crate::extensions::{ExtensionRestriction, ExtensionRestrictionError}; -macro_rules! instance_extensions { - ( - $($member:ident => { - doc: $doc:expr, - raw: $raw:expr, - requires_core: $requires_core:expr, - requires_instance_extensions: [$($requires_instance_extension:ident),*]$(,)? - },)* - ) => ( - extensions! { - InstanceExtensions, - $($member => { - doc: $doc, - raw: $raw, - requires_core: $requires_core, - requires_device_extensions: [], - requires_instance_extensions: [$($requires_instance_extension),*], - },)* - } - - impl InstanceExtensions { - /// Checks enabled extensions against the instance version and each other. - pub(super) fn check_requirements( - &self, - supported: &InstanceExtensions, - api_version: crate::Version, - ) -> Result<(), crate::extensions::ExtensionRestrictionError> { - $( - if self.$member { - if !supported.$member { - return Err(crate::extensions::ExtensionRestrictionError { - extension: stringify!($member), - restriction: crate::extensions::ExtensionRestriction::NotSupported, - }); - } - - if api_version < $requires_core { - return Err(crate::extensions::ExtensionRestrictionError { - extension: stringify!($member), - restriction: crate::extensions::ExtensionRestriction::RequiresCore($requires_core), - }); - } else { - $( - if !self.$requires_instance_extension { - return Err(crate::extensions::ExtensionRestrictionError { - extension: stringify!($member), - restriction: crate::extensions::ExtensionRestriction::RequiresInstanceExtension(stringify!($requires_instance_extension)), - }); - } - )* - } - } - )* - Ok(()) - } - } - ); -} - -pub use crate::autogen::InstanceExtensions; -pub(crate) use instance_extensions; - -impl InstanceExtensions { - /// See the docs of supported_by_core(). - pub fn supported_by_core_raw() -> Result<Self, SupportedExtensionsError> { - InstanceExtensions::supported_by_core_raw_with_loader(loader::auto_loader()?) - } - - /// Returns an `InstanceExtensions` object with extensions supported by the core driver. - pub fn supported_by_core() -> Result<Self, LoadingError> { - match InstanceExtensions::supported_by_core_raw() { - Ok(l) => Ok(l), - Err(SupportedExtensionsError::LoadingError(e)) => Err(e), - Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e), - } - } - - /// Same as `supported_by_core`, but allows specifying a loader. - pub fn supported_by_core_with_loader<L>( - ptrs: &loader::FunctionPointers<L>, - ) -> Result<Self, LoadingError> - where - L: loader::Loader, - { - match InstanceExtensions::supported_by_core_raw_with_loader(ptrs) { - Ok(l) => Ok(l), - Err(SupportedExtensionsError::LoadingError(e)) => Err(e), - Err(SupportedExtensionsError::OomError(e)) => panic!("{:?}", e), - } - } - - /// See the docs of supported_by_core(). - pub fn supported_by_core_raw_with_loader<L>( - ptrs: &loader::FunctionPointers<L>, - ) -> Result<Self, SupportedExtensionsError> - where - L: loader::Loader, - { - let fns = ptrs.fns(); - - let properties: Vec<ash::vk::ExtensionProperties> = unsafe { - let mut num = 0; - check_errors(fns.v1_0.enumerate_instance_extension_properties( - ptr::null(), - &mut num, - ptr::null_mut(), - ))?; - - let mut properties = Vec::with_capacity(num as usize); - check_errors(fns.v1_0.enumerate_instance_extension_properties( - ptr::null(), - &mut num, - properties.as_mut_ptr(), - ))?; - properties.set_len(num as usize); - properties - }; - - Ok(Self::from(properties.iter().map(|property| unsafe { - CStr::from_ptr(property.extension_name.as_ptr()) - }))) - } -} +// Generated by build.rs +include!(concat!(env!("OUT_DIR"), "/instance_extensions.rs")); #[cfg(test)] mod tests { @@ -147,7 +19,22 @@ mod tests { #[test] fn empty_extensions() { - let i: Vec<CString> = (&InstanceExtensions::none()).into(); - assert!(i.iter().next().is_none()); + let i: Vec<CString> = (&InstanceExtensions::empty()).into(); + assert!(i.get(0).is_none()); + } + + #[test] + fn into_iter() { + let extensions = InstanceExtensions { + khr_display: true, + ..InstanceExtensions::empty() + }; + for (name, enabled) in extensions { + if name == "VK_KHR_display" { + assert!(enabled); + } else { + assert!(!enabled); + } + } } } diff --git a/src/instance/instance.rs b/src/instance/instance.rs deleted file mode 100644 index 50712d2..0000000 --- a/src/instance/instance.rs +++ /dev/null @@ -1,693 +0,0 @@ -// 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. - -use crate::check_errors; -use crate::device::physical::{init_physical_devices, PhysicalDeviceInfo}; -use crate::extensions::ExtensionRestrictionError; -use crate::fns::InstanceFunctions; -use crate::instance::loader; -use crate::instance::loader::FunctionPointers; -use crate::instance::loader::Loader; -use crate::instance::loader::LoadingError; -use crate::instance::InstanceExtensions; -use crate::Error; -use crate::OomError; -use crate::Version; -use crate::VulkanObject; -use smallvec::SmallVec; -use std::borrow::Cow; -use std::convert::TryInto; -use std::error; -use std::ffi::CString; -use std::fmt; -use std::hash::Hash; -use std::hash::Hasher; -use std::mem::MaybeUninit; -use std::ops::Deref; -use std::ptr; -use std::slice; -use std::sync::Arc; - -/// An instance of a Vulkan context. This is the main object that should be created by an -/// application before everything else. -/// -/// # Application info -/// -/// When you create an instance, you have the possibility to pass an `ApplicationInfo` struct as -/// the first parameter. This struct contains various information about your application, most -/// notably its name and engine. -/// -/// Passing such a structure allows for example the driver to let the user configure the driver's -/// behavior for your application alone through a control panel. -/// -/// ```no_run -/// # #[macro_use] extern crate vulkano; -/// # fn main() { -/// use vulkano::instance::{Instance, InstanceExtensions}; -/// use vulkano::Version; -/// -/// // Builds an `ApplicationInfo` by looking at the content of the `Cargo.toml` file at -/// // compile-time. -/// let app_infos = app_info_from_cargo_toml!(); -/// -/// let _instance = Instance::new(Some(&app_infos), Version::V1_1, &InstanceExtensions::none(), None).unwrap(); -/// # } -/// ``` -/// -/// # API versions -/// -/// Both an `Instance` and a [`Device`](crate::device::Device) have a highest version of the Vulkan -/// API that they support. This places a limit on what Vulkan functions and features are available -/// to use when used on a particular instance or device. It is possible for the instance and the -/// device to support different versions. The supported version for an instance can be queried -/// before creation with -/// [`FunctionPointers::api_version`](crate::instance::loader::FunctionPointers::api_version), -/// while for a device it can be retrieved with -/// [`PhysicalDevice::api_version`](crate::instance::PhysicalDevice::api_version). -/// -/// When creating an `Instance`, you have to specify a maximum API version that you will use. -/// This restricts the API version that is available for the instance and any devices created from -/// it. For example, if both instance and device potentially support Vulkan 1.2, but you specify -/// 1.1 as the maximum API version when creating the `Instance`, then you can only use Vulkan 1.1 -/// functions, even though they could theoretically support a higher version. You can think of it -/// as a promise never to use any functionality from a higher version. -/// -/// The maximum API version is not a _minimum_, so it is possible to set it to a higher version than -/// what the instance or device inherently support. The final API version that you are able to use -/// on an instance or device is the lower of the supported API version and the chosen maximum API -/// version of the `Instance`. -/// -/// However, due to a quirk in how the Vulkan 1.0 specification was written, if the instance only -/// supports Vulkan 1.0, then it is not possible to specify a maximum API version higher than 1.0. -/// Trying to create an `Instance` will return an `IncompatibleDriver` error. Consequently, it is -/// not possible to use a higher device API version with an instance that only supports 1.0. -/// -/// # Extensions -/// -/// When creating an `Instance`, you must provide a list of extensions that must be enabled on the -/// newly-created instance. Trying to enable an extension that is not supported by the system will -/// result in an error. -/// -/// Contrary to OpenGL, it is not possible to use the features of an extension if it was not -/// explicitly enabled. -/// -/// Extensions are especially important to take into account if you want to render images on the -/// screen, as the only way to do so is to use the `VK_KHR_surface` extension. More information -/// about this in the `swapchain` module. -/// -/// For example, here is how we create an instance with the `VK_KHR_surface` and -/// `VK_KHR_android_surface` extensions enabled, which will allow us to render images to an -/// Android screen. You can compile and run this code on any system, but it is highly unlikely to -/// succeed on anything else than an Android-running device. -/// -/// ```no_run -/// use vulkano::instance::Instance; -/// use vulkano::instance::InstanceExtensions; -/// use vulkano::Version; -/// -/// let extensions = InstanceExtensions { -/// khr_surface: true, -/// khr_android_surface: true, -/// .. InstanceExtensions::none() -/// }; -/// -/// let instance = match Instance::new(None, Version::V1_1, &extensions, None) { -/// Ok(i) => i, -/// Err(err) => panic!("Couldn't build instance: {:?}", err) -/// }; -/// ``` -/// -/// # Layers -/// -/// When creating an `Instance`, you have the possibility to pass a list of **layers** that will -/// be activated on the newly-created instance. The list of available layers can be retrieved by -/// calling [the `layers_list` function](fn.layers_list.html). -/// -/// A layer is a component that will hook and potentially modify the Vulkan function calls. -/// For example, activating a layer could add a frames-per-second counter on the screen, or it -/// could send information to a debugger that will debug your application. -/// -/// > **Note**: From an application's point of view, layers "just exist". In practice, on Windows -/// > and Linux, layers can be installed by third party installers or by package managers and can -/// > also be activated by setting the value of the `VK_INSTANCE_LAYERS` environment variable -/// > before starting the program. See the documentation of the official Vulkan loader for these -/// > platforms. -/// -/// > **Note**: In practice, the most common use of layers right now is for debugging purposes. -/// > To do so, you are encouraged to set the `VK_INSTANCE_LAYERS` environment variable on Windows -/// > or Linux instead of modifying the source code of your program. For example: -/// > `export VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_api_dump` on Linux if you installed the Vulkan SDK -/// > will print the list of raw Vulkan function calls. -/// -/// ## Example -/// -/// ``` -/// # use std::sync::Arc; -/// # use std::error::Error; -/// # use vulkano::instance; -/// # use vulkano::instance::Instance; -/// # use vulkano::instance::InstanceExtensions; -/// # use vulkano::Version; -/// # fn test() -> Result<Arc<Instance>, Box<dyn Error>> { -/// // For the sake of the example, we activate all the layers that -/// // contain the word "foo" in their description. -/// let layers: Vec<_> = instance::layers_list()? -/// .filter(|l| l.description().contains("foo")) -/// .collect(); -/// -/// let layer_names = layers.iter() -/// .map(|l| l.name()); -/// -/// let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), layer_names)?; -/// # Ok(instance) -/// # } -/// ``` -// TODO: mention that extensions must be supported by layers as well -pub struct Instance { - instance: ash::vk::Instance, - //alloc: Option<Box<Alloc + Send + Sync>>, - - // The highest version that is supported for this instance. - // This is the minimum of Instance::max_api_version and FunctionPointers::api_version. - api_version: Version, - - // The highest allowed API version for instances and devices created from it. - max_api_version: Version, - - pub(crate) physical_device_infos: Vec<PhysicalDeviceInfo>, - fns: InstanceFunctions, - extensions: InstanceExtensions, - layers: SmallVec<[CString; 16]>, - function_pointers: OwnedOrRef<FunctionPointers<Box<dyn Loader + Send + Sync>>>, -} - -// TODO: fix the underlying cause instead -impl ::std::panic::UnwindSafe for Instance {} -impl ::std::panic::RefUnwindSafe for Instance {} - -impl Instance { - /// Initializes a new instance of Vulkan. - /// - /// See the documentation of `Instance` or of [the `instance` module](index.html) for more - /// details. - /// - /// # Example - /// - /// ```no_run - /// use vulkano::instance::Instance; - /// use vulkano::instance::InstanceExtensions; - /// use vulkano::Version; - /// - /// let instance = match Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None) { - /// Ok(i) => i, - /// Err(err) => panic!("Couldn't build instance: {:?}", err) - /// }; - /// ``` - /// - /// # Panic - /// - /// - Panics if the version numbers passed in `ApplicationInfo` are too large can't be - /// converted into a Vulkan version number. - /// - Panics if the application name or engine name contain a null character. - // TODO: add a test for these ^ - // TODO: if no allocator is specified by the user, use Rust's allocator instead of leaving - // the choice to Vulkan - pub fn new<'a, L>( - app_infos: Option<&ApplicationInfo>, - max_api_version: Version, - extensions: &InstanceExtensions, - layers: L, - ) -> Result<Arc<Instance>, InstanceCreationError> - where - L: IntoIterator<Item = &'a str>, - { - let layers = layers - .into_iter() - .map(|layer| CString::new(layer).unwrap()) - .collect::<SmallVec<[_; 16]>>(); - - Instance::new_inner( - app_infos, - max_api_version, - extensions, - layers, - OwnedOrRef::Ref(loader::auto_loader()?), - ) - } - - /// Same as `new`, but allows specifying a loader where to load Vulkan from. - pub fn with_loader<'a, L>( - loader: FunctionPointers<Box<dyn Loader + Send + Sync>>, - app_infos: Option<&ApplicationInfo>, - max_api_version: Version, - extensions: &InstanceExtensions, - layers: L, - ) -> Result<Arc<Instance>, InstanceCreationError> - where - L: IntoIterator<Item = &'a str>, - { - let layers = layers - .into_iter() - .map(|layer| CString::new(layer).unwrap()) - .collect::<SmallVec<[_; 16]>>(); - - Instance::new_inner( - app_infos, - max_api_version, - extensions, - layers, - OwnedOrRef::Owned(loader), - ) - } - - fn new_inner( - app_infos: Option<&ApplicationInfo>, - max_api_version: Version, - extensions: &InstanceExtensions, - layers: SmallVec<[CString; 16]>, - function_pointers: OwnedOrRef<FunctionPointers<Box<dyn Loader + Send + Sync>>>, - ) -> Result<Arc<Instance>, InstanceCreationError> { - let api_version = std::cmp::min(max_api_version, function_pointers.api_version()?); - - // Check if the extensions are correct - extensions.check_requirements( - &InstanceExtensions::supported_by_core_with_loader(&function_pointers)?, - api_version, - )?; - - // TODO: For now there are still buggy drivers that will segfault if you don't pass any - // appinfos. Therefore for now we ensure that it can't be `None`. - let def = Default::default(); - let app_infos = match app_infos { - Some(a) => Some(a), - None => Some(&def), - }; - - // Building the CStrings from the `str`s within `app_infos`. - // They need to be created ahead of time, since we pass pointers to them. - let app_infos_strings = if let Some(app_infos) = app_infos { - Some(( - app_infos - .application_name - .clone() - .map(|n| CString::new(n.as_bytes().to_owned()).unwrap()), - app_infos - .engine_name - .clone() - .map(|n| CString::new(n.as_bytes().to_owned()).unwrap()), - )) - } else { - None - }; - - // Building the `vk::ApplicationInfo` if required. - let app_infos = if let Some(app_infos) = app_infos { - Some(ash::vk::ApplicationInfo { - p_application_name: app_infos_strings - .as_ref() - .unwrap() - .0 - .as_ref() - .map(|s| s.as_ptr()) - .unwrap_or(ptr::null()), - application_version: app_infos - .application_version - .map(|v| v.try_into().expect("Version out of range")) - .unwrap_or(0), - p_engine_name: app_infos_strings - .as_ref() - .unwrap() - .1 - .as_ref() - .map(|s| s.as_ptr()) - .unwrap_or(ptr::null()), - engine_version: app_infos - .engine_version - .map(|v| v.try_into().expect("Version out of range")) - .unwrap_or(0), - api_version: max_api_version.try_into().expect("Version out of range"), - ..Default::default() - }) - } else { - None - }; - - // FIXME: check whether each layer is supported - let layers_ptrs = layers - .iter() - .map(|layer| layer.as_ptr()) - .collect::<SmallVec<[_; 16]>>(); - - let extensions_list: Vec<CString> = extensions.into(); - let extensions_ptrs = extensions_list - .iter() - .map(|extension| extension.as_ptr()) - .collect::<SmallVec<[_; 32]>>(); - - // Creating the Vulkan instance. - let instance = unsafe { - let mut output = MaybeUninit::uninit(); - let infos = ash::vk::InstanceCreateInfo { - flags: ash::vk::InstanceCreateFlags::empty(), - p_application_info: if let Some(app) = app_infos.as_ref() { - app as *const _ - } else { - ptr::null() - }, - enabled_layer_count: layers_ptrs.len() as u32, - pp_enabled_layer_names: layers_ptrs.as_ptr(), - enabled_extension_count: extensions_ptrs.len() as u32, - pp_enabled_extension_names: extensions_ptrs.as_ptr(), - ..Default::default() - }; - - let fns = function_pointers.fns(); - check_errors( - fns.v1_0 - .create_instance(&infos, ptr::null(), output.as_mut_ptr()), - )?; - output.assume_init() - }; - - // Loading the function pointers of the newly-created instance. - let fns = { - InstanceFunctions::load(|name| { - function_pointers.get_instance_proc_addr(instance, name.as_ptr()) - }) - }; - - let mut instance = Instance { - instance, - api_version, - max_api_version, - //alloc: None, - physical_device_infos: Vec::new(), - fns, - extensions: extensions.clone(), - layers, - function_pointers, - }; - - // Enumerating all physical devices. - instance.physical_device_infos = init_physical_devices(&instance)?; - - Ok(Arc::new(instance)) - } - - /*/// Same as `new`, but provides an allocator that will be used by the Vulkan library whenever - /// it needs to allocate memory on the host. - /// - /// Note that this allocator can be overridden when you create a `Device`, a `MemoryPool`, etc. - pub fn with_alloc(app_infos: Option<&ApplicationInfo>, alloc: Box<Alloc + Send + Sync>) -> Arc<Instance> { - unimplemented!() - }*/ - - /// Returns the Vulkan version supported by the instance. - /// - /// This is the lower of the - /// [driver's supported version](crate::instance::loader::FunctionPointers::api_version) and - /// [`max_api_version`](Instance::max_api_version). - #[inline] - pub fn api_version(&self) -> Version { - self.api_version - } - - /// Returns the maximum Vulkan version that was specified when creating the instance. - #[inline] - pub fn max_api_version(&self) -> Version { - self.max_api_version - } - - /// Grants access to the Vulkan functions of the instance. - #[inline] - pub fn fns(&self) -> &InstanceFunctions { - &self.fns - } - - /// Returns the extensions that have been enabled on the instance. - #[inline] - pub fn enabled_extensions(&self) -> &InstanceExtensions { - &self.extensions - } - - /// Returns the layers that have been enabled on the instance. - #[doc(hidden)] - #[inline] - pub fn enabled_layers(&self) -> slice::Iter<CString> { - self.layers.iter() - } -} - -impl fmt::Debug for Instance { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(fmt, "<Vulkan instance {:?}>", self.instance) - } -} - -unsafe impl VulkanObject for Instance { - type Object = ash::vk::Instance; - - #[inline] - fn internal_object(&self) -> ash::vk::Instance { - self.instance - } -} - -impl Drop for Instance { - #[inline] - fn drop(&mut self) { - unsafe { - self.fns.v1_0.destroy_instance(self.instance, ptr::null()); - } - } -} - -impl PartialEq for Instance { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.instance == other.instance - } -} - -impl Eq for Instance {} - -impl Hash for Instance { - #[inline] - fn hash<H: Hasher>(&self, state: &mut H) { - self.instance.hash(state); - } -} - -// Same as Cow but less annoying. -enum OwnedOrRef<T: 'static> { - Owned(T), - Ref(&'static T), -} - -impl<T> Deref for OwnedOrRef<T> { - type Target = T; - #[inline] - fn deref(&self) -> &T { - match *self { - OwnedOrRef::Owned(ref v) => v, - OwnedOrRef::Ref(v) => v, - } - } -} - -/// Information that can be given to the Vulkan driver so that it can identify your application. -// TODO: better documentation for struct and methods -#[derive(Debug, Clone)] -pub struct ApplicationInfo<'a> { - /// Name of the application. - pub application_name: Option<Cow<'a, str>>, - /// An opaque number that contains the version number of the application. - pub application_version: Option<Version>, - /// Name of the engine used to power the application. - pub engine_name: Option<Cow<'a, str>>, - /// An opaque number that contains the version number of the engine. - pub engine_version: Option<Version>, -} - -impl<'a> ApplicationInfo<'a> { - /// Builds an `ApplicationInfo` from the information gathered by Cargo. - /// - /// # Panic - /// - /// - Panics if the required environment variables are missing, which happens if the project - /// wasn't built by Cargo. - /// - #[deprecated(note = "Please use the `app_info_from_cargo_toml!` macro instead")] - pub fn from_cargo_toml() -> ApplicationInfo<'a> { - let version = Version { - major: 0, - minor: 0, - patch: 0, - }; - - let name = ""; - - ApplicationInfo { - application_name: Some(name.into()), - application_version: Some(version), - engine_name: None, - engine_version: None, - } - } -} - -/// Builds an `ApplicationInfo` from the information gathered by Cargo. -/// -/// # Panic -/// -/// - Panics if the required environment variables are missing, which happens if the project -/// wasn't built by Cargo. -/// -#[macro_export] -macro_rules! app_info_from_cargo_toml { - () => {{ - let version = $crate::instance::Version { - major: 0, - minor: 0, - patch: 0, - }; - - let name = ""; - - $crate::instance::ApplicationInfo { - application_name: Some(name.into()), - application_version: Some(version), - engine_name: None, - engine_version: None, - } - }}; -} - -impl<'a> Default for ApplicationInfo<'a> { - fn default() -> ApplicationInfo<'a> { - ApplicationInfo { - application_name: None, - application_version: None, - engine_name: None, - engine_version: None, - } - } -} - -/// Error that can happen when creating an instance. -#[derive(Clone, Debug)] -pub enum InstanceCreationError { - /// Failed to load the Vulkan shared library. - LoadingError(LoadingError), - /// Not enough memory. - OomError(OomError), - /// Failed to initialize for an implementation-specific reason. - InitializationFailed, - /// One of the requested layers is missing. - LayerNotPresent, - /// One of the requested extensions is not supported by the implementation. - ExtensionNotPresent, - /// The version requested is not supported by the implementation. - IncompatibleDriver, - /// A restriction for an extension was not met. - ExtensionRestrictionNotMet(ExtensionRestrictionError), -} - -impl error::Error for InstanceCreationError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - InstanceCreationError::LoadingError(ref err) => Some(err), - InstanceCreationError::OomError(ref err) => Some(err), - _ => None, - } - } -} - -impl fmt::Display for InstanceCreationError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - InstanceCreationError::LoadingError(_) => { - write!(fmt, "failed to load the Vulkan shared library") - } - InstanceCreationError::OomError(_) => write!(fmt, "not enough memory available"), - InstanceCreationError::InitializationFailed => write!(fmt, "initialization failed"), - InstanceCreationError::LayerNotPresent => write!(fmt, "layer not present"), - InstanceCreationError::ExtensionNotPresent => write!(fmt, "extension not present"), - InstanceCreationError::IncompatibleDriver => write!(fmt, "incompatible driver"), - InstanceCreationError::ExtensionRestrictionNotMet(err) => err.fmt(fmt), - } - } -} - -impl From<OomError> for InstanceCreationError { - #[inline] - fn from(err: OomError) -> InstanceCreationError { - InstanceCreationError::OomError(err) - } -} - -impl From<LoadingError> for InstanceCreationError { - #[inline] - fn from(err: LoadingError) -> InstanceCreationError { - InstanceCreationError::LoadingError(err) - } -} - -impl From<ExtensionRestrictionError> for InstanceCreationError { - #[inline] - fn from(err: ExtensionRestrictionError) -> Self { - Self::ExtensionRestrictionNotMet(err) - } -} - -impl From<Error> for InstanceCreationError { - #[inline] - fn from(err: Error) -> InstanceCreationError { - match err { - err @ Error::OutOfHostMemory => InstanceCreationError::OomError(OomError::from(err)), - err @ Error::OutOfDeviceMemory => InstanceCreationError::OomError(OomError::from(err)), - Error::InitializationFailed => InstanceCreationError::InitializationFailed, - Error::LayerNotPresent => InstanceCreationError::LayerNotPresent, - Error::ExtensionNotPresent => InstanceCreationError::ExtensionNotPresent, - Error::IncompatibleDriver => InstanceCreationError::IncompatibleDriver, - _ => panic!("unexpected error: {:?}", err), - } - } -} - -#[cfg(test)] -mod tests { - use crate::device::physical::PhysicalDevice; - - #[test] - fn create_instance() { - let _ = instance!(); - } - - #[test] - fn queue_family_by_id() { - let instance = instance!(); - - let phys = match PhysicalDevice::enumerate(&instance).next() { - Some(p) => p, - None => return, - }; - - let queue_family = match phys.queue_families().next() { - Some(q) => q, - None => return, - }; - - let by_id = phys.queue_family_by_id(queue_family.id()).unwrap(); - assert_eq!(by_id.id(), queue_family.id()); - } -} diff --git a/src/instance/layers.rs b/src/instance/layers.rs index 6c56090..3ea264b 100644 --- a/src/instance/layers.rs +++ b/src/instance/layers.rs @@ -7,79 +7,13 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use std::error; -use std::ffi::CStr; -use std::fmt; -use std::ptr; -use std::vec::IntoIter; - -use crate::check_errors; -use crate::instance::loader; -use crate::instance::loader::LoadingError; -use crate::Error; -use crate::OomError; use crate::Version; - -/// Queries the list of layers that are available when creating an instance. -/// -/// On success, this function returns an iterator that produces -/// [`LayerProperties`](struct.LayerProperties.html) objects. In order to enable a layer, you need -/// to pass its name (returned by `LayerProperties::name()`) when creating the -/// [`Instance`](struct.Instance.html). -/// -/// This function returns an error if it failed to load the Vulkan library. -/// -/// > **Note**: It is possible that one of the layers enumerated here is no longer available when -/// > you create the `Instance`. This will lead to an error when calling `Instance::new`. The -/// > author isn't aware of any situation where this would happen, but it is theoretically possible -/// > according to the specifications. -/// -/// # Example -/// -/// ```no_run -/// use vulkano::instance; -/// -/// for layer in instance::layers_list().unwrap() { -/// println!("Available layer: {}", layer.name()); -/// } -/// ``` -pub fn layers_list() -> Result<LayersIterator, LayersListError> { - layers_list_from_loader(loader::auto_loader()?) -} - -/// Same as `layers_list()`, but allows specifying a loader. -pub fn layers_list_from_loader<L>( - ptrs: &loader::FunctionPointers<L>, -) -> Result<LayersIterator, LayersListError> -where - L: loader::Loader, -{ - unsafe { - let fns = ptrs.fns(); - - let mut num = 0; - check_errors( - fns.v1_0 - .enumerate_instance_layer_properties(&mut num, ptr::null_mut()), - )?; - - let mut layers: Vec<ash::vk::LayerProperties> = Vec::with_capacity(num as usize); - check_errors({ - fns.v1_0 - .enumerate_instance_layer_properties(&mut num, layers.as_mut_ptr()) - })?; - layers.set_len(num as usize); - - Ok(LayersIterator { - iter: layers.into_iter(), - }) - } -} +use std::ffi::CStr; /// Properties of a layer. #[derive(Clone)] pub struct LayerProperties { - props: ash::vk::LayerProperties, + pub(crate) props: ash::vk::LayerProperties, } impl LayerProperties { @@ -88,12 +22,14 @@ impl LayerProperties { /// If you want to enable this layer on an instance, you need to pass this value to /// `Instance::new`. /// - /// # Example + /// # Examples /// /// ```no_run - /// use vulkano::instance; + /// use vulkano::VulkanLibrary; /// - /// for layer in instance::layers_list().unwrap() { + /// let library = VulkanLibrary::new().unwrap(); + /// + /// for layer in library.layer_properties().unwrap() { /// println!("Layer name: {}", layer.name()); /// } /// ``` @@ -110,12 +46,14 @@ impl LayerProperties { /// /// This description is chosen by the layer itself. /// - /// # Example + /// # Examples /// /// ```no_run - /// use vulkano::instance; + /// use vulkano::VulkanLibrary; + /// + /// let library = VulkanLibrary::new().unwrap(); /// - /// for layer in instance::layers_list().unwrap() { + /// for layer in library.layer_properties().unwrap() { /// println!("Layer description: {}", layer.description()); /// } /// ``` @@ -130,13 +68,14 @@ impl LayerProperties { /// Returns the version of Vulkan supported by this layer. /// - /// # Example + /// # Examples /// /// ```no_run - /// use vulkano::instance; - /// use vulkano::instance::Version; + /// use vulkano::{Version, VulkanLibrary}; + /// + /// let library = VulkanLibrary::new().unwrap(); /// - /// for layer in instance::layers_list().unwrap() { + /// for layer in library.layer_properties().unwrap() { /// if layer.vulkan_version() >= Version::major_minor(2, 0) { /// println!("Layer {} requires Vulkan 2.0", layer.name()); /// } @@ -151,12 +90,14 @@ impl LayerProperties { /// /// The number is chosen by the layer itself. It can be used for bug reports for example. /// - /// # Example + /// # Examples /// /// ```no_run - /// use vulkano::instance; + /// use vulkano::VulkanLibrary; /// - /// for layer in instance::layers_list().unwrap() { + /// let library = VulkanLibrary::new().unwrap(); + /// + /// for layer in library.layer_properties().unwrap() { /// println!("Layer {} - Version: {}", layer.name(), layer.implementation_version()); /// } /// ``` @@ -166,97 +107,22 @@ impl LayerProperties { } } -/// Error that can happen when loading the list of layers. -#[derive(Clone, Debug)] -pub enum LayersListError { - /// Failed to load the Vulkan shared library. - LoadingError(LoadingError), - /// Not enough memory. - OomError(OomError), -} - -impl error::Error for LayersListError { - #[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - LayersListError::LoadingError(ref err) => Some(err), - LayersListError::OomError(ref err) => Some(err), - } - } -} - -impl fmt::Display for LayersListError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - LayersListError::LoadingError(_) => "failed to load the Vulkan shared library", - LayersListError::OomError(_) => "not enough memory available", - } - ) - } -} - -impl From<OomError> for LayersListError { - #[inline] - fn from(err: OomError) -> LayersListError { - LayersListError::OomError(err) - } -} - -impl From<LoadingError> for LayersListError { - #[inline] - fn from(err: LoadingError) -> LayersListError { - LayersListError::LoadingError(err) - } -} - -impl From<Error> for LayersListError { - #[inline] - fn from(err: Error) -> LayersListError { - match err { - err @ Error::OutOfHostMemory => LayersListError::OomError(OomError::from(err)), - err @ Error::OutOfDeviceMemory => LayersListError::OomError(OomError::from(err)), - _ => panic!("unexpected error: {:?}", err), - } - } -} - -/// Iterator that produces the list of layers that are available. -// TODO: #[derive(Debug, Clone)] -pub struct LayersIterator { - iter: IntoIter<ash::vk::LayerProperties>, -} - -impl Iterator for LayersIterator { - type Item = LayerProperties; - - #[inline] - fn next(&mut self) -> Option<LayerProperties> { - self.iter.next().map(|p| LayerProperties { props: p }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -impl ExactSizeIterator for LayersIterator {} - #[cfg(test)] mod tests { - use crate::instance; + use crate::VulkanLibrary; #[test] fn layers_list() { - let mut list = match instance::layers_list() { + let library = match VulkanLibrary::new() { + Ok(x) => x, + Err(_) => return, + }; + + let list = match library.layer_properties() { Ok(l) => l, Err(_) => return, }; - while let Some(_) = list.next() {} + for _ in list {} } } diff --git a/src/instance/loader.rs b/src/instance/loader.rs deleted file mode 100644 index 7714fd2..0000000 --- a/src/instance/loader.rs +++ /dev/null @@ -1,316 +0,0 @@ -// 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. - -//! Vulkan implementation loading system. -//! -//! Before vulkano can do anything, it first needs to find an implementation of Vulkan. A Vulkan -//! implementation is defined as a single `vkGetInstanceProcAddr` function, which can be accessed -//! through the `Loader` trait. -//! -//! This module provides various implementations of the `Loader` trait. -//! -//! Once you have a struct that implements `Loader`, you can create a `FunctionPointers` struct -//! from it and use this `FunctionPointers` struct to build an `Instance`. -//! -//! By default vulkano will use the `auto_loader()` function, which tries to automatically load -//! a Vulkan implementation from the system. - -use crate::check_errors; -use crate::fns::EntryFunctions; -use crate::OomError; -use crate::SafeDeref; -use crate::Version; -use lazy_static::lazy_static; -use shared_library; -use std::error; -use std::ffi::CStr; -use std::fmt; -use std::mem; -use std::ops::Deref; -use std::os::raw::c_char; -use std::os::raw::c_void; -use std::path::Path; - -/// Implemented on objects that grant access to a Vulkan implementation. -pub unsafe trait Loader { - /// Calls the `vkGetInstanceProcAddr` function. The parameters are the same. - /// - /// The returned function must stay valid for as long as `self` is alive. - fn get_instance_proc_addr( - &self, - instance: ash::vk::Instance, - name: *const c_char, - ) -> *const c_void; -} - -unsafe impl<T> Loader for T -where - T: SafeDeref, - T::Target: Loader, -{ - #[inline] - fn get_instance_proc_addr( - &self, - instance: ash::vk::Instance, - name: *const c_char, - ) -> *const c_void { - (**self).get_instance_proc_addr(instance, name) - } -} - -/// Implementation of `Loader` that loads Vulkan from a dynamic library. -pub struct DynamicLibraryLoader { - vk_lib: shared_library::dynamic_library::DynamicLibrary, - get_proc_addr: - extern "system" fn(instance: ash::vk::Instance, pName: *const c_char) -> *const c_void, -} - -impl DynamicLibraryLoader { - /// Tries to load the dynamic library at the given path, and tries to - /// load `vkGetInstanceProcAddr` in it. - /// - /// # Safety - /// - /// - The dynamic library must be a valid Vulkan implementation. - /// - pub unsafe fn new<P>(path: P) -> Result<DynamicLibraryLoader, LoadingError> - where - P: AsRef<Path>, - { - let vk_lib = shared_library::dynamic_library::DynamicLibrary::open(Some(path.as_ref())) - .map_err(LoadingError::LibraryLoadFailure)?; - - let get_proc_addr = { - let ptr: *mut c_void = vk_lib - .symbol("vkGetInstanceProcAddr") - .map_err(|_| LoadingError::MissingEntryPoint("vkGetInstanceProcAddr".to_owned()))?; - mem::transmute(ptr) - }; - - Ok(DynamicLibraryLoader { - vk_lib, - get_proc_addr, - }) - } -} - -unsafe impl Loader for DynamicLibraryLoader { - #[inline] - fn get_instance_proc_addr( - &self, - instance: ash::vk::Instance, - name: *const c_char, - ) -> *const c_void { - (self.get_proc_addr)(instance, name) - } -} - -/// Wraps around a loader and contains function pointers. -pub struct FunctionPointers<L> { - loader: L, - fns: EntryFunctions, -} - -impl<L> FunctionPointers<L> { - /// Loads some global function pointer from the loader. - pub fn new(loader: L) -> FunctionPointers<L> - where - L: Loader, - { - let fns = EntryFunctions::load(|name| unsafe { - mem::transmute(loader.get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr())) - }); - - FunctionPointers { loader, fns } - } - - /// Returns the collection of Vulkan entry points from the Vulkan loader. - #[inline] - pub fn fns(&self) -> &EntryFunctions { - &self.fns - } - - /// Returns the highest Vulkan version that is supported for instances. - pub fn api_version(&self) -> Result<Version, OomError> - where - L: Loader, - { - // Per the Vulkan spec: - // If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a - // Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion - // to determine the version of Vulkan. - unsafe { - let name = CStr::from_bytes_with_nul_unchecked(b"vkEnumerateInstanceVersion\0"); - let func = self.get_instance_proc_addr(ash::vk::Instance::null(), name.as_ptr()); - - if func.is_null() { - Ok(Version { - major: 1, - minor: 0, - patch: 0, - }) - } else { - type Pfn = extern "system" fn(pApiVersion: *mut u32) -> ash::vk::Result; - let func: Pfn = mem::transmute(func); - let mut api_version = 0; - check_errors(func(&mut api_version))?; - Ok(Version::from(api_version)) - } - } - } - - /// Calls `get_instance_proc_addr` on the underlying loader. - #[inline] - pub fn get_instance_proc_addr( - &self, - instance: ash::vk::Instance, - name: *const c_char, - ) -> *const c_void - where - L: Loader, - { - self.loader.get_instance_proc_addr(instance, name) - } -} - -/// Expression that returns a loader that assumes that Vulkan is linked to the executable you're -/// compiling. -/// -/// If you use this macro, you must linked to a library that provides the `vkGetInstanceProcAddr` -/// symbol. -/// -/// This is provided as a macro and not as a regular function, because the macro contains an -/// `extern {}` block. -// TODO: should this be unsafe? -#[macro_export] -macro_rules! statically_linked_vulkan_loader { - () => {{ - extern "C" { - fn vkGetInstanceProcAddr( - instance: ash::vk::Instance, - pName: *const c_char, - ) -> ash::vk::PFN_vkVoidFunction; - } - - struct StaticallyLinkedVulkanLoader; - unsafe impl Loader for StaticallyLinkedVulkanLoader { - fn get_instance_proc_addr( - &self, - instance: ash::vk::Instance, - name: *const c_char, - ) -> extern "system" fn() -> () { - unsafe { vkGetInstanceProcAddr(instance, name) } - } - } - - StaticallyLinkedVulkanLoader - }}; -} - -/// Returns the default `FunctionPointers` for this system. -/// -/// This function tries to auto-guess where to find the Vulkan implementation, and loads it in a -/// `lazy_static!`. The content of the lazy_static is then returned, or an error if we failed to -/// load Vulkan. -pub fn auto_loader( -) -> Result<&'static FunctionPointers<Box<dyn Loader + Send + Sync>>, LoadingError> { - #[cfg(target_os = "ios")] - #[allow(non_snake_case)] - fn def_loader_impl() -> Result<Box<Loader + Send + Sync>, LoadingError> { - let loader = statically_linked_vulkan_loader!(); - Ok(Box::new(loader)) - } - - #[cfg(not(target_os = "ios"))] - fn def_loader_impl() -> Result<Box<dyn Loader + Send + Sync>, LoadingError> { - #[cfg(windows)] - fn get_path() -> &'static Path { - Path::new("vulkan-1.dll") - } - #[cfg(all(unix, not(target_os = "android"), not(target_os = "macos")))] - fn get_path() -> &'static Path { - Path::new("libvulkan.so.1") - } - #[cfg(target_os = "macos")] - fn get_path() -> &'static Path { - Path::new("libvulkan.1.dylib") - } - #[cfg(target_os = "android")] - fn get_path() -> &'static Path { - Path::new("libvulkan.so") - } - - let loader = unsafe { DynamicLibraryLoader::new(get_path())? }; - - Ok(Box::new(loader)) - } - - lazy_static! { - static ref DEFAULT_LOADER: Result<FunctionPointers<Box<dyn Loader + Send + Sync>>, LoadingError> = - def_loader_impl().map(FunctionPointers::new); - } - - match DEFAULT_LOADER.deref() { - &Ok(ref ptr) => Ok(ptr), - &Err(ref err) => Err(err.clone()), - } -} - -/// Error that can happen when loading the Vulkan loader. -#[derive(Debug, Clone)] -pub enum LoadingError { - /// Failed to load the Vulkan shared library. - LibraryLoadFailure(String), // TODO: meh for error type, but this needs changes in shared_library - - /// One of the entry points required to be supported by the Vulkan implementation is missing. - MissingEntryPoint(String), -} - -impl error::Error for LoadingError { - /*#[inline] - fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match *self { - LoadingError::LibraryLoadFailure(ref err) => Some(err), - _ => None - } - }*/ -} - -impl fmt::Display for LoadingError { - #[inline] - fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!( - fmt, - "{}", - match *self { - LoadingError::LibraryLoadFailure(_) => "failed to load the Vulkan shared library", - LoadingError::MissingEntryPoint(_) => { - "one of the entry points required to be supported by the Vulkan implementation \ - is missing" - } - } - ) - } -} - -#[cfg(test)] -mod tests { - use crate::instance::loader::DynamicLibraryLoader; - use crate::instance::loader::LoadingError; - - #[test] - fn dl_open_error() { - unsafe { - match DynamicLibraryLoader::new("_non_existing_library.void") { - Err(LoadingError::LibraryLoadFailure(_)) => (), - _ => panic!(), - } - } - } -} diff --git a/src/instance/mod.rs b/src/instance/mod.rs index 5e85e7f..55e0626 100644 --- a/src/instance/mod.rs +++ b/src/instance/mod.rs @@ -9,32 +9,35 @@ //! API entry point. //! -//! The first thing to do before you start using Vulkan is to create an `Instance` object. +//! The first thing to do after loading the Vulkan library is to create an `Instance` object. //! //! For example: //! //! ```no_run -//! use vulkano::instance::Instance; -//! use vulkano::instance::InstanceExtensions; -//! use vulkano::Version; -//! -//! let instance = match Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None) { -//! Ok(i) => i, -//! Err(err) => panic!("Couldn't build instance: {:?}", err) +//! use vulkano::{ +//! instance::{Instance, InstanceExtensions}, +//! Version, VulkanLibrary, //! }; +//! +//! let library = VulkanLibrary::new() +//! .unwrap_or_else(|err| panic!("Couldn't load Vulkan library: {:?}", err)); +//! let instance = Instance::new(library, Default::default()) +//! .unwrap_or_else(|err| panic!("Couldn't create instance: {:?}", err)); //! ``` //! //! Creating an instance initializes everything and allows you to enumerate physical devices, //! ie. all the Vulkan implementations that are available on the system. //! //! ```no_run -//! # use vulkano::instance::Instance; -//! # use vulkano::instance::InstanceExtensions; -//! # use vulkano::Version; +//! # use vulkano::{ +//! # instance::{Instance, InstanceExtensions}, +//! # Version, VulkanLibrary, +//! # }; //! use vulkano::device::physical::PhysicalDevice; //! -//! # let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None).unwrap(); -//! for physical_device in PhysicalDevice::enumerate(&instance) { +//! # let library = VulkanLibrary::new().unwrap(); +//! # let instance = Instance::new(library, Default::default()).unwrap(); +//! for physical_device in instance.enumerate_physical_devices().unwrap() { //! println!("Available device: {}", physical_device.properties().device_name); //! } //! ``` @@ -42,7 +45,7 @@ //! # Enumerating physical devices and creating a device //! //! After you have created an instance, the next step is usually to enumerate the physical devices -//! that are available on the system with `PhysicalDevice::enumerate()` (see above). +//! that are available on the system with `Instance::enumerate_physical_devices()` (see above). //! //! When choosing which physical device to use, keep in mind that physical devices may or may not //! be able to draw to a certain surface (ie. to a window or a monitor), or may even not be able @@ -50,23 +53,844 @@ //! //! Once you have chosen a physical device, you can create a `Device` object from it. See the //! `device` module for more info. +//! +//! # Portability subset devices and the `enumerate_portability` flag +//! +//! Certain devices, currently those on MacOS and iOS systems, do not fully conform to the Vulkan +//! specification. They are usable as normal devices, but they do not implement everything that +//! is required; some mandatory parts of Vulkan are missing. These are known as +//! "portability subset" devices. +//! +//! A portability subset device will advertise support for the +//! [`khr_portability_subset`](crate::device::DeviceExtensions::khr_portability_subset) device +//! extension. This extension must always be enabled when it is supported, and Vulkano will +//! automatically enable it when creating the device. When it is enabled, some parts of Vulkan that +//! are available in standard Vulkan will not be available by default, but they can be used by +//! enabling corresponding features when creating the device, if the device supports them. +//! +//! Because these devices are non-conformant, Vulkan programs that rely on full compliance may +//! not work (crash or have validation errors) when run on them, if they happen to use a part of +//! Vulkan that is missing from the non-conformant device. Therefore, Vulkan hides them from +//! the user by default when calling `enumerate_physical_devices` on the instance. If there are no +//! conformant devices on the system, `Instance::new` will return an `IncompatibleDriver` error. +//! +//! In order to enumerate portability subset devices, you must set the +//! [`InstanceCreateInfo::enumerate_portability`] flag when creating the instance. However, if you +//! do this, your program must be prepared to handle the non-conformant aspects of these devices, +//! and must enable the appropriate features when creating the `Device` if you intend to use them. -pub use self::extensions::InstanceExtensions; -pub use self::instance::ApplicationInfo; -pub use self::instance::Instance; -pub use self::instance::InstanceCreationError; -pub use self::layers::layers_list; -pub use self::layers::LayerProperties; -pub use self::layers::LayersIterator; -pub use self::layers::LayersListError; -pub use self::loader::LoadingError; -pub use crate::extensions::{ - ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError, +use self::debug::{ + DebugUtilsMessengerCreateInfo, UserCallback, ValidationFeatureDisable, ValidationFeatureEnable, +}; +pub use self::{extensions::InstanceExtensions, layers::LayerProperties}; +use crate::{ + device::physical::PhysicalDevice, instance::debug::trampoline, macros::impl_id_counter, + OomError, RequiresOneOf, VulkanError, VulkanLibrary, VulkanObject, +}; +pub use crate::{ + extensions::{ExtensionRestriction, ExtensionRestrictionError}, + fns::InstanceFunctions, + version::Version, +}; +use smallvec::SmallVec; +use std::{ + error::Error, + ffi::{c_void, CString}, + fmt::{Debug, Display, Error as FmtError, Formatter}, + mem::MaybeUninit, + num::NonZeroU64, + panic::{RefUnwindSafe, UnwindSafe}, + ptr, + sync::Arc, }; -pub use crate::version::Version; pub mod debug; pub(crate) mod extensions; -mod instance; mod layers; -pub mod loader; + +/// An instance of a Vulkan context. This is the main object that should be created by an +/// application before everything else. +/// +/// # Application and engine info +/// +/// When you create an instance, you have the possibility to set information about your application +/// and its engine. +/// +/// Providing this information allows for example the driver to let the user configure the driver's +/// behavior for your application alone through a control panel. +/// +/// ```no_run +/// # #[macro_use] extern crate vulkano; +/// # fn main() { +/// use vulkano::{ +/// instance::{Instance, InstanceCreateInfo, InstanceExtensions}, +/// Version, VulkanLibrary, +/// }; +/// +/// let library = VulkanLibrary::new().unwrap(); +/// let _instance = Instance::new( +/// library, +/// InstanceCreateInfo::application_from_cargo_toml(), +/// ).unwrap(); +/// # } +/// ``` +/// +/// # API versions +/// +/// Both an `Instance` and a [`Device`](crate::device::Device) have a highest version of the Vulkan +/// API that they support. This places a limit on what Vulkan functions and features are available +/// to use when used on a particular instance or device. It is possible for the instance and the +/// device to support different versions. The supported version for an instance can be queried +/// before creation with +/// [`VulkanLibrary::api_version`](crate::VulkanLibrary::api_version), +/// while for a device it can be retrieved with +/// [`PhysicalDevice::api_version`](crate::device::physical::PhysicalDevice::api_version). +/// +/// When creating an `Instance`, you have to specify a maximum API version that you will use. +/// This restricts the API version that is available for the instance and any devices created from +/// it. For example, if both instance and device potentially support Vulkan 1.2, but you specify +/// 1.1 as the maximum API version when creating the `Instance`, then you can only use Vulkan 1.1 +/// functions, even though they could theoretically support a higher version. You can think of it +/// as a promise never to use any functionality from a higher version. +/// +/// The maximum API version is not a _minimum_, so it is possible to set it to a higher version than +/// what the instance or device inherently support. The final API version that you are able to use +/// on an instance or device is the lower of the supported API version and the chosen maximum API +/// version of the `Instance`. +/// +/// Due to a quirk in how the Vulkan 1.0 specification was written, if the instance only +/// supports Vulkan 1.0, then it is not possible to specify a maximum API version higher than 1.0. +/// Trying to create an `Instance` will return an `IncompatibleDriver` error. Consequently, it is +/// not possible to use a higher device API version with an instance that only supports 1.0. +/// +/// # Extensions +/// +/// When creating an `Instance`, you must provide a list of extensions that must be enabled on the +/// newly-created instance. Trying to enable an extension that is not supported by the system will +/// result in an error. +/// +/// Contrary to OpenGL, it is not possible to use the features of an extension if it was not +/// explicitly enabled. +/// +/// Extensions are especially important to take into account if you want to render images on the +/// screen, as the only way to do so is to use the `VK_KHR_surface` extension. More information +/// about this in the `swapchain` module. +/// +/// For example, here is how we create an instance with the `VK_KHR_surface` and +/// `VK_KHR_android_surface` extensions enabled, which will allow us to render images to an +/// Android screen. You can compile and run this code on any system, but it is highly unlikely to +/// succeed on anything else than an Android-running device. +/// +/// ```no_run +/// use vulkano::{ +/// instance::{Instance, InstanceCreateInfo, InstanceExtensions}, +/// Version, VulkanLibrary, +/// }; +/// +/// let library = VulkanLibrary::new() +/// .unwrap_or_else(|err| panic!("Couldn't load Vulkan library: {:?}", err)); +/// +/// let extensions = InstanceExtensions { +/// khr_surface: true, +/// khr_android_surface: true, +/// .. InstanceExtensions::empty() +/// }; +/// +/// let instance = Instance::new( +/// library, +/// InstanceCreateInfo { +/// enabled_extensions: extensions, +/// ..Default::default() +/// }, +/// ) +/// .unwrap_or_else(|err| panic!("Couldn't create instance: {:?}", err)); +/// ``` +/// +/// # Layers +/// +/// When creating an `Instance`, you have the possibility to pass a list of **layers** that will +/// be activated on the newly-created instance. The list of available layers can be retrieved by +/// calling the [`layer_properties`](crate::VulkanLibrary::layer_properties) method of +/// `VulkanLibrary`. +/// +/// A layer is a component that will hook and potentially modify the Vulkan function calls. +/// For example, activating a layer could add a frames-per-second counter on the screen, or it +/// could send information to a debugger that will debug your application. +/// +/// > **Note**: From an application's point of view, layers "just exist". In practice, on Windows +/// > and Linux, layers can be installed by third party installers or by package managers and can +/// > also be activated by setting the value of the `VK_INSTANCE_LAYERS` environment variable +/// > before starting the program. See the documentation of the official Vulkan loader for these +/// > platforms. +/// +/// > **Note**: In practice, the most common use of layers right now is for debugging purposes. +/// > To do so, you are encouraged to set the `VK_INSTANCE_LAYERS` environment variable on Windows +/// > or Linux instead of modifying the source code of your program. For example: +/// > `export VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_api_dump` on Linux if you installed the Vulkan SDK +/// > will print the list of raw Vulkan function calls. +/// +/// ## Examples +/// +/// ``` +/// # use std::{sync::Arc, error::Error}; +/// # use vulkano::{ +/// # instance::{Instance, InstanceCreateInfo, InstanceExtensions}, +/// # Version, VulkanLibrary, +/// # }; +/// # fn test() -> Result<Arc<Instance>, Box<dyn Error>> { +/// let library = VulkanLibrary::new()?; +/// +/// // For the sake of the example, we activate all the layers that +/// // contain the word "foo" in their description. +/// let layers: Vec<_> = library.layer_properties()? +/// .filter(|l| l.description().contains("foo")) +/// .collect(); +/// +/// let instance = Instance::new( +/// library, +/// InstanceCreateInfo { +/// enabled_layers: layers.iter().map(|l| l.name().to_owned()).collect(), +/// ..Default::default() +/// }, +/// )?; +/// # Ok(instance) +/// # } +/// ``` +// TODO: mention that extensions must be supported by layers as well +pub struct Instance { + handle: ash::vk::Instance, + fns: InstanceFunctions, + id: NonZeroU64, + + api_version: Version, + enabled_extensions: InstanceExtensions, + enabled_layers: Vec<String>, + library: Arc<VulkanLibrary>, + max_api_version: Version, + _user_callbacks: Vec<Box<UserCallback>>, +} + +// TODO: fix the underlying cause instead +impl UnwindSafe for Instance {} +impl RefUnwindSafe for Instance {} + +impl Instance { + /// Creates a new `Instance`. + /// + /// # Panics + /// + /// - Panics if any version numbers in `create_info` contain a field too large to be converted + /// into a Vulkan version number. + /// - Panics if `create_info.max_api_version` is not at least `V1_0`. + pub fn new( + library: Arc<VulkanLibrary>, + create_info: InstanceCreateInfo, + ) -> Result<Arc<Instance>, InstanceCreationError> { + unsafe { Self::with_debug_utils_messengers(library, create_info, []) } + } + + /// Creates a new `Instance` with debug messengers to use during the creation and destruction + /// of the instance. + /// + /// The debug messengers are not used at any other time, + /// [`DebugUtilsMessenger`](crate::instance::debug::DebugUtilsMessenger) should be used for + /// that. + /// + /// If `debug_utils_messengers` is not empty, the `ext_debug_utils` extension must be set in + /// `enabled_extensions`. + /// + /// # Panics + /// + /// - Panics if the `message_severity` or `message_type` members of any element of + /// `debug_utils_messengers` are empty. + /// + /// # Safety + /// + /// - The `user_callback` of each element of `debug_utils_messengers` must not make any calls + /// to the Vulkan API. + pub unsafe fn with_debug_utils_messengers( + library: Arc<VulkanLibrary>, + create_info: InstanceCreateInfo, + debug_utils_messengers: impl IntoIterator<Item = DebugUtilsMessengerCreateInfo>, + ) -> Result<Arc<Instance>, InstanceCreationError> { + let InstanceCreateInfo { + application_name, + application_version, + mut enabled_extensions, + enabled_layers, + engine_name, + engine_version, + max_api_version, + enumerate_portability, + enabled_validation_features, + disabled_validation_features, + _ne: _, + } = create_info; + + let (api_version, max_api_version) = { + let api_version = library.api_version(); + let max_api_version = if let Some(max_api_version) = max_api_version { + max_api_version + } else if api_version < Version::V1_1 { + api_version + } else { + Version::HEADER_VERSION + }; + + (std::cmp::min(max_api_version, api_version), max_api_version) + }; + + // VUID-VkApplicationInfo-apiVersion-04010 + assert!(max_api_version >= Version::V1_0); + let supported_extensions = + library.supported_extensions_with_layers(enabled_layers.iter().map(String::as_str))?; + let mut flags = ash::vk::InstanceCreateFlags::empty(); + + if enumerate_portability && supported_extensions.khr_portability_enumeration { + enabled_extensions.khr_portability_enumeration = true; + flags |= ash::vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR; + } + + // Check if the extensions are correct + enabled_extensions.check_requirements(&supported_extensions, api_version)?; + + // FIXME: check whether each layer is supported + let enabled_layers_cstr: Vec<CString> = enabled_layers + .iter() + .map(|name| CString::new(name.clone()).unwrap()) + .collect(); + let enabled_layers_ptrs = enabled_layers_cstr + .iter() + .map(|layer| layer.as_ptr()) + .collect::<SmallVec<[_; 2]>>(); + + let enabled_extensions_cstr: Vec<CString> = (&enabled_extensions).into(); + let enabled_extensions_ptrs = enabled_extensions_cstr + .iter() + .map(|extension| extension.as_ptr()) + .collect::<SmallVec<[_; 2]>>(); + + let application_name_cstr = application_name.map(|name| CString::new(name).unwrap()); + let engine_name_cstr = engine_name.map(|name| CString::new(name).unwrap()); + let application_info = ash::vk::ApplicationInfo { + p_application_name: application_name_cstr + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(ptr::null()), + application_version: application_version + .try_into() + .expect("Version out of range"), + p_engine_name: engine_name_cstr + .as_ref() + .map(|s| s.as_ptr()) + .unwrap_or(ptr::null()), + engine_version: engine_version.try_into().expect("Version out of range"), + api_version: max_api_version.try_into().expect("Version out of range"), + ..Default::default() + }; + + let enable_validation_features_vk: SmallVec<[_; 5]> = enabled_validation_features + .iter() + .copied() + .map(Into::into) + .collect(); + let disable_validation_features_vk: SmallVec<[_; 8]> = disabled_validation_features + .iter() + .copied() + .map(Into::into) + .collect(); + + let mut create_info_vk = ash::vk::InstanceCreateInfo { + flags, + p_application_info: &application_info, + enabled_layer_count: enabled_layers_ptrs.len() as u32, + pp_enabled_layer_names: enabled_layers_ptrs.as_ptr(), + enabled_extension_count: enabled_extensions_ptrs.len() as u32, + pp_enabled_extension_names: enabled_extensions_ptrs.as_ptr(), + ..Default::default() + }; + let mut validation_features_vk = None; + + if !enabled_validation_features.is_empty() || !disabled_validation_features.is_empty() { + if !enabled_extensions.ext_validation_features { + return Err(InstanceCreationError::RequirementNotMet { + required_for: "`create_info.enabled_validation_features` or \ + `create_info.disabled_validation_features` are not empty", + requires_one_of: RequiresOneOf { + instance_extensions: &["ext_validation_features"], + ..Default::default() + }, + }); + } + + // VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-02967 + assert!( + !enabled_validation_features + .contains(&ValidationFeatureEnable::GpuAssistedReserveBindingSlot) + || enabled_validation_features.contains(&ValidationFeatureEnable::GpuAssisted) + ); + + // VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-02968 + assert!( + !(enabled_validation_features.contains(&ValidationFeatureEnable::DebugPrintf) + && enabled_validation_features.contains(&ValidationFeatureEnable::GpuAssisted)) + ); + + let next = validation_features_vk.insert(ash::vk::ValidationFeaturesEXT { + enabled_validation_feature_count: enable_validation_features_vk.len() as u32, + p_enabled_validation_features: enable_validation_features_vk.as_ptr(), + disabled_validation_feature_count: disable_validation_features_vk.len() as u32, + p_disabled_validation_features: disable_validation_features_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; + } + + // Handle debug messengers + let debug_utils_messengers = debug_utils_messengers.into_iter(); + let mut debug_utils_messenger_create_infos = + Vec::with_capacity(debug_utils_messengers.size_hint().0); + let mut user_callbacks = Vec::with_capacity(debug_utils_messengers.size_hint().0); + + for create_info in debug_utils_messengers { + let DebugUtilsMessengerCreateInfo { + message_type, + message_severity, + user_callback, + _ne: _, + } = create_info; + + // VUID-VkInstanceCreateInfo-pNext-04926 + if !enabled_extensions.ext_debug_utils { + return Err(InstanceCreationError::RequirementNotMet { + required_for: "`create_info.debug_utils_messengers` is not empty", + requires_one_of: RequiresOneOf { + instance_extensions: &["ext_debug_utils"], + ..Default::default() + }, + }); + } + + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-parameter + // TODO: message_severity.validate_instance()?; + + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-requiredbitmask + assert!(!message_severity.is_empty()); + + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-parameter + // TODO: message_type.validate_instance()?; + + // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-requiredbitmask + assert!(!message_type.is_empty()); + + // VUID-PFN_vkDebugUtilsMessengerCallbackEXT-None-04769 + // Can't be checked, creation is unsafe. + + 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() + }; + + debug_utils_messenger_create_infos.push(create_info); + user_callbacks.push(user_callback); + } + + for i in 1..debug_utils_messenger_create_infos.len() { + debug_utils_messenger_create_infos[i - 1].p_next = + &debug_utils_messenger_create_infos[i] as *const _ as *const _; + } + + if let Some(info) = debug_utils_messenger_create_infos.first() { + create_info_vk.p_next = info as *const _ as *const _; + } + + // Creating the Vulkan instance. + let handle = { + let mut output = MaybeUninit::uninit(); + let fns = library.fns(); + (fns.v1_0.create_instance)(&create_info_vk, ptr::null(), output.as_mut_ptr()) + .result() + .map_err(VulkanError::from)?; + output.assume_init() + }; + + // Loading the function pointers of the newly-created instance. + let fns = { + InstanceFunctions::load(|name| { + library + .get_instance_proc_addr(handle, name.as_ptr()) + .map_or(ptr::null(), |func| func as _) + }) + }; + + Ok(Arc::new(Instance { + handle, + fns, + id: Self::next_id(), + api_version, + enabled_extensions, + enabled_layers, + library, + max_api_version, + _user_callbacks: user_callbacks, + })) + } + + /// Returns the Vulkan library used to create this instance. + #[inline] + pub fn library(&self) -> &Arc<VulkanLibrary> { + &self.library + } + + /// Returns the Vulkan version supported by the instance. + /// + /// This is the lower of the + /// [driver's supported version](crate::VulkanLibrary::api_version) and + /// [`max_api_version`](Instance::max_api_version). + #[inline] + pub fn api_version(&self) -> Version { + self.api_version + } + + /// Returns the maximum Vulkan version that was specified when creating the instance. + #[inline] + pub fn max_api_version(&self) -> Version { + self.max_api_version + } + + /// Returns pointers to the raw Vulkan functions of the instance. + #[inline] + pub fn fns(&self) -> &InstanceFunctions { + &self.fns + } + + /// Returns the extensions that have been enabled on the instance. + #[inline] + pub fn enabled_extensions(&self) -> &InstanceExtensions { + &self.enabled_extensions + } + + /// Returns the layers that have been enabled on the instance. + #[inline] + pub fn enabled_layers(&self) -> &[String] { + &self.enabled_layers + } + + /// Returns an iterator that enumerates the physical devices available. + /// + /// # Examples + /// + /// ```no_run + /// # use vulkano::{ + /// # instance::{Instance, InstanceExtensions}, + /// # Version, VulkanLibrary, + /// # }; + /// + /// # let library = VulkanLibrary::new().unwrap(); + /// # let instance = Instance::new(library, Default::default()).unwrap(); + /// for physical_device in instance.enumerate_physical_devices().unwrap() { + /// println!("Available device: {}", physical_device.properties().device_name); + /// } + /// ``` + pub fn enumerate_physical_devices( + self: &Arc<Self>, + ) -> Result<impl ExactSizeIterator<Item = Arc<PhysicalDevice>>, VulkanError> { + let fns = self.fns(); + + unsafe { + let handles = loop { + let mut count = 0; + (fns.v1_0.enumerate_physical_devices)(self.handle, &mut count, ptr::null_mut()) + .result() + .map_err(VulkanError::from)?; + + let mut handles = Vec::with_capacity(count as usize); + let result = (fns.v1_0.enumerate_physical_devices)( + self.handle, + &mut count, + handles.as_mut_ptr(), + ); + + match result { + ash::vk::Result::SUCCESS => { + handles.set_len(count as usize); + break handles; + } + ash::vk::Result::INCOMPLETE => (), + err => return Err(VulkanError::from(err)), + } + }; + + let physical_devices: SmallVec<[_; 4]> = handles + .into_iter() + .map(|handle| PhysicalDevice::from_handle(self.clone(), handle)) + .collect::<Result<_, _>>()?; + + Ok(physical_devices.into_iter()) + } + } +} + +impl Drop for Instance { + #[inline] + fn drop(&mut self) { + let fns = self.fns(); + + unsafe { + (fns.v1_0.destroy_instance)(self.handle, ptr::null()); + } + } +} + +unsafe impl VulkanObject for Instance { + type Handle = ash::vk::Instance; + + #[inline] + fn handle(&self) -> Self::Handle { + self.handle + } +} + +impl_id_counter!(Instance); + +impl Debug for Instance { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + let Self { + handle, + fns, + id: _, + api_version, + enabled_extensions, + enabled_layers, + library: function_pointers, + max_api_version, + _user_callbacks: _, + } = self; + + f.debug_struct("Instance") + .field("handle", handle) + .field("fns", fns) + .field("api_version", api_version) + .field("enabled_extensions", enabled_extensions) + .field("enabled_layers", enabled_layers) + .field("function_pointers", function_pointers) + .field("max_api_version", max_api_version) + .finish_non_exhaustive() + } +} + +/// Parameters to create a new `Instance`. +#[derive(Debug)] +pub struct InstanceCreateInfo { + /// A string of your choice stating the name of your application. + /// + /// The default value is `None`. + pub application_name: Option<String>, + + /// A version number of your choice specifying the version of your application. + /// + /// The default value is zero. + pub application_version: Version, + + /// The extensions to enable on the instance. + /// + /// The default value is [`InstanceExtensions::empty()`]. + pub enabled_extensions: InstanceExtensions, + + /// The layers to enable on the instance. + /// + /// The default value is empty. + pub enabled_layers: Vec<String>, + + /// A string of your choice stating the name of the engine used to power the application. + pub engine_name: Option<String>, + + /// A version number of your choice specifying the version of the engine used to power the + /// application. + /// + /// The default value is zero. + pub engine_version: Version, + + /// The highest Vulkan API version that the application will use with the instance. + /// + /// Usually, you will want to leave this at the default. + /// + /// The default value is [`Version::HEADER_VERSION`], but if the + /// supported instance version is 1.0, then it will be 1.0. + pub max_api_version: Option<Version>, + + /// Include [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag) + /// devices when enumerating physical devices. + /// + /// If you enable this flag, you must ensure that your program is prepared to handle the + /// non-conformant aspects of these devices. + /// + /// If this flag is not enabled, and there are no fully-conformant devices on the system, then + /// [`Instance::new`] will return an `IncompatibleDriver` error. + /// + /// The default value is `false`. + /// + /// # Notes + /// + /// If this flag is enabled, and the + /// [`khr_portability_enumeration`](crate::instance::InstanceExtensions::khr_portability_enumeration) + /// extension is supported, it will be enabled automatically when creating the instance. + /// If the extension is not supported, this flag will be ignored. + pub enumerate_portability: bool, + + /// Features of the validation layer to enable. + /// + /// If not empty, the + /// [`ext_validation_features`](crate::instance::InstanceExtensions::ext_validation_features) + /// extension must be enabled on the instance. + pub enabled_validation_features: Vec<ValidationFeatureEnable>, + + /// Features of the validation layer to disable. + /// + /// If not empty, the + /// [`ext_validation_features`](crate::instance::InstanceExtensions::ext_validation_features) + /// extension must be enabled on the instance. + pub disabled_validation_features: Vec<ValidationFeatureDisable>, + + pub _ne: crate::NonExhaustive, +} + +impl Default for InstanceCreateInfo { + #[inline] + fn default() -> Self { + Self { + application_name: None, + application_version: Version::major_minor(0, 0), + enabled_extensions: InstanceExtensions::empty(), + enabled_layers: Vec::new(), + engine_name: None, + engine_version: Version::major_minor(0, 0), + max_api_version: None, + enumerate_portability: false, + enabled_validation_features: Vec::new(), + disabled_validation_features: Vec::new(), + _ne: crate::NonExhaustive(()), + } + } +} + +impl InstanceCreateInfo { + /// Returns an `InstanceCreateInfo` with the `application_name` and `application_version` set + /// from information in your crate's Cargo.toml file. + /// + /// # Panics + /// + /// - Panics if the required environment variables are missing, which happens if the project + /// wasn't built by Cargo. + #[inline] + pub fn application_from_cargo_toml() -> Self { + Self { + application_name: Some("vulkano".to_owned()), + application_version: Version { + major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(), + minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(), + patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(), + }, + ..Default::default() + } + } +} + +/// Error that can happen when creating an instance. +#[derive(Clone, Debug)] +pub enum InstanceCreationError { + /// Not enough memory. + OomError(OomError), + + /// Failed to initialize for an implementation-specific reason. + InitializationFailed, + + /// One of the requested layers is missing. + LayerNotPresent, + + /// One of the requested extensions is not supported by the implementation. + ExtensionNotPresent, + + /// The version requested is not supported by the implementation. + IncompatibleDriver, + + /// A restriction for an extension was not met. + ExtensionRestrictionNotMet(ExtensionRestrictionError), + + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, +} + +impl Error for InstanceCreationError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + Self::OomError(err) => Some(err), + _ => None, + } + } +} + +impl Display for InstanceCreationError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::OomError(_) => write!(f, "not enough memory available"), + Self::InitializationFailed => write!(f, "initialization failed"), + Self::LayerNotPresent => write!(f, "layer not present"), + Self::ExtensionNotPresent => write!(f, "extension not present"), + Self::IncompatibleDriver => write!(f, "incompatible driver"), + Self::ExtensionRestrictionNotMet(err) => Display::fmt(err, f), + 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<OomError> for InstanceCreationError { + fn from(err: OomError) -> Self { + Self::OomError(err) + } +} + +impl From<ExtensionRestrictionError> for InstanceCreationError { + fn from(err: ExtensionRestrictionError) -> Self { + Self::ExtensionRestrictionNotMet(err) + } +} + +impl From<VulkanError> for InstanceCreationError { + fn from(err: VulkanError) -> Self { + match err { + err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)), + err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)), + VulkanError::InitializationFailed => Self::InitializationFailed, + VulkanError::LayerNotPresent => Self::LayerNotPresent, + VulkanError::ExtensionNotPresent => Self::ExtensionNotPresent, + VulkanError::IncompatibleDriver => Self::IncompatibleDriver, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +#[cfg(test)] +mod tests { + + #[test] + fn create_instance() { + let _ = instance!(); + } +} |