// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // 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, Box> { /// // 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>, // 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, fns: InstanceFunctions, extensions: InstanceExtensions, layers: SmallVec<[CString; 16]>, function_pointers: OwnedOrRef>>, } // 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, InstanceCreationError> where L: IntoIterator, { let layers = layers .into_iter() .map(|layer| CString::new(layer).unwrap()) .collect::>(); 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>, app_infos: Option<&ApplicationInfo>, max_api_version: Version, extensions: &InstanceExtensions, layers: L, ) -> Result, InstanceCreationError> where L: IntoIterator, { let layers = layers .into_iter() .map(|layer| CString::new(layer).unwrap()) .collect::>(); 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>>, ) -> Result, 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::>(); let extensions_list: Vec = extensions.into(); let extensions_ptrs = extensions_list .iter() .map(|extension| extension.as_ptr()) .collect::>(); // 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) -> Arc { 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 { self.layers.iter() } } impl fmt::Debug for Instance { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, "", 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(&self, state: &mut H) { self.instance.hash(state); } } // Same as Cow but less annoying. enum OwnedOrRef { Owned(T), Ref(&'static T), } impl Deref for OwnedOrRef { 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>, /// An opaque number that contains the version number of the application. pub application_version: Option, /// Name of the engine used to power the application. pub engine_name: Option>, /// An opaque number that contains the version number of the engine. pub engine_version: Option, } 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 for InstanceCreationError { #[inline] fn from(err: OomError) -> InstanceCreationError { InstanceCreationError::OomError(err) } } impl From for InstanceCreationError { #[inline] fn from(err: LoadingError) -> InstanceCreationError { InstanceCreationError::LoadingError(err) } } impl From for InstanceCreationError { #[inline] fn from(err: ExtensionRestrictionError) -> Self { Self::ExtensionRestrictionNotMet(err) } } impl From 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()); } }