use self::debug::{
DebugUtilsMessengerCreateInfo, UserCallback, ValidationFeatureDisable, ValidationFeatureEnable,
};
pub use self::{extensions::InstanceExtensions, layers::LayerProperties};
use crate::{
device::physical::PhysicalDevice, instance::debug::trampoline, 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 mod debug;
pub(crate) mod extensions;
mod layers;
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>>,
}
impl UnwindSafe for Instance {}
impl RefUnwindSafe for Instance {}
impl Instance {
pub fn new(
library: Arc<VulkanLibrary>,
create_info: InstanceCreateInfo,
) -> Result<Arc<Instance>, InstanceCreationError> {
unsafe { Self::with_debug_utils_messengers(library, create_info, []) }
}
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)
};
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;
}
enabled_extensions.check_requirements(&supported_extensions, api_version)?;
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: "`enabled_validation_features` or `disabled_validation_features` are not empty",
requires_one_of: RequiresOneOf {
instance_extensions: &["ext_validation_features"],
..Default::default()
},
});
}
assert!(
!enabled_validation_features
.contains(&ValidationFeatureEnable::GpuAssistedReserveBindingSlot)
|| enabled_validation_features.contains(&ValidationFeatureEnable::GpuAssisted)
);
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 _;
}
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;
if !enabled_extensions.ext_debug_utils {
return Err(InstanceCreationError::RequirementNotMet {
required_for: "`debug_utils_messengers` is not empty",
requires_one_of: RequiresOneOf {
instance_extensions: &["ext_debug_utils"],
..Default::default()
},
});
}
assert!(!message_severity.is_empty());
assert!(!message_type.is_empty());
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 _;
}
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()
};
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,
}))
}
#[inline]
pub fn library(&self) -> &Arc<VulkanLibrary> {
&self.library
}
#[inline]
pub fn api_version(&self) -> Version {
self.api_version
}
#[inline]
pub fn max_api_version(&self) -> Version {
self.max_api_version
}
#[inline]
pub fn fns(&self) -> &InstanceFunctions {
&self.fns
}
#[inline]
pub fn enabled_extensions(&self) -> &InstanceExtensions {
&self.enabled_extensions
}
#[inline]
pub fn enabled_layers(&self) -> &[String] {
&self.enabled_layers
}
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
}
}
crate::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()
}
}
#[derive(Debug)]
pub struct InstanceCreateInfo {
pub application_name: Option<String>,
pub application_version: Version,
pub enabled_extensions: InstanceExtensions,
pub enabled_layers: Vec<String>,
pub engine_name: Option<String>,
pub engine_version: Version,
pub max_api_version: Option<Version>,
pub enumerate_portability: bool,
pub enabled_validation_features: Vec<ValidationFeatureEnable>,
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 {
#[inline]
pub fn application_from_cargo_toml() -> Self {
Self {
application_name: Some(env!("CARGO_PKG_NAME").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()
}
}
}
#[derive(Clone, Debug)]
pub enum InstanceCreationError {
OomError(OomError),
InitializationFailed,
LayerNotPresent,
ExtensionNotPresent,
IncompatibleDriver,
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!();
}
}