diff options
Diffstat (limited to 'src/instance/mod.rs')
-rw-r--r-- | src/instance/mod.rs | 880 |
1 files changed, 852 insertions, 28 deletions
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!(); + } +} |