diff options
Diffstat (limited to 'src/instance/instance.rs')
-rw-r--r-- | src/instance/instance.rs | 693 |
1 files changed, 0 insertions, 693 deletions
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()); - } -} |