blob: 50712d2f408fd37fa31e1d6e74a9faa53bb1cefd [file] [log] [blame]
// 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());
}
}