use self::debug::{
DebugUtilsMessengerCallback, DebugUtilsMessengerCreateInfo, ValidationFeatureDisable,
ValidationFeatureEnable,
};
pub use self::layers::LayerProperties;
use crate::{
cache::WeakArcOnceCache,
device::physical::{
PhysicalDevice, PhysicalDeviceGroupProperties, PhysicalDeviceGroupPropertiesRaw,
},
instance::debug::trampoline,
macros::{impl_id_counter, vulkan_bitflags},
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanLibrary,
VulkanObject,
};
pub use crate::{fns::InstanceFunctions, version::Version};
use ash::vk::Handle;
use parking_lot::RwLock;
use smallvec::SmallVec;
use std::{
borrow::Cow,
ffi::{c_void, CString},
fmt::{Debug, Error as FmtError, Formatter},
mem::MaybeUninit,
num::NonZeroU64,
ops::Deref,
panic::{RefUnwindSafe, UnwindSafe},
ptr, slice,
sync::Arc,
};
pub mod debug;
mod layers;
include!(concat!(env!("OUT_DIR"), "/instance_extensions.rs"));
pub struct Instance {
handle: ash::vk::Instance,
fns: InstanceFunctions,
id: NonZeroU64,
flags: InstanceCreateFlags,
api_version: Version,
enabled_extensions: InstanceExtensions,
enabled_layers: Vec<String>,
library: Arc<VulkanLibrary>,
max_api_version: Version,
_user_callbacks: Vec<Arc<DebugUtilsMessengerCallback>>,
physical_devices: WeakArcOnceCache<ash::vk::PhysicalDevice, PhysicalDevice>,
physical_device_groups: RwLock<(bool, Vec<PhysicalDeviceGroupPropertiesRaw>)>,
}
impl UnwindSafe for Instance {}
impl RefUnwindSafe for Instance {}
impl Instance {
#[inline]
pub fn new(
library: Arc<VulkanLibrary>,
mut create_info: InstanceCreateInfo,
) -> Result<Arc<Instance>, Validated<VulkanError>> {
create_info.max_api_version.get_or_insert_with(|| {
let api_version = library.api_version();
if api_version < Version::V1_1 {
api_version
} else {
Version::HEADER_VERSION
}
});
Self::validate_new(&library, &create_info)?;
unsafe { Ok(Self::new_unchecked(library, create_info)?) }
}
fn validate_new(
library: &VulkanLibrary,
create_info: &InstanceCreateInfo,
) -> Result<(), Box<ValidationError>> {
create_info
.validate(library)
.map_err(|err| err.add_context("create_info"))?;
let &InstanceCreateInfo {
flags: _,
application_name: _,
application_version: _,
engine_name: _,
engine_version: _,
max_api_version,
ref enabled_layers,
ref enabled_extensions,
debug_utils_messengers: _,
enabled_validation_features: _,
disabled_validation_features: _,
_ne,
} = create_info;
let api_version = std::cmp::min(max_api_version.unwrap_or_default(), library.api_version());
let supported_extensions = library
.supported_extensions_with_layers(enabled_layers.iter().map(String::as_str))
.unwrap();
enabled_extensions
.check_requirements(&supported_extensions, api_version)
.map_err(|err| {
Box::new(ValidationError {
context: "create_info.enabled_extensions".into(),
vuids: &["VUID-vkCreateInstance-ppEnabledExtensionNames-01388"],
..ValidationError::from_error(err)
})
})?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
library: Arc<VulkanLibrary>,
mut create_info: InstanceCreateInfo,
) -> Result<Arc<Instance>, VulkanError> {
create_info.max_api_version.get_or_insert_with(|| {
let api_version = library.api_version();
if api_version < Version::V1_1 {
api_version
} else {
Version::HEADER_VERSION
}
});
create_info.enabled_extensions.enable_dependencies(
std::cmp::min(
create_info.max_api_version.unwrap_or_default(),
library.api_version(),
),
&library
.supported_extensions_with_layers(
create_info.enabled_layers.iter().map(String::as_str),
)
.unwrap(),
);
let &InstanceCreateInfo {
mut flags,
ref application_name,
application_version,
ref engine_name,
engine_version,
max_api_version,
ref enabled_layers,
ref enabled_extensions,
ref debug_utils_messengers,
ref enabled_validation_features,
ref disabled_validation_features,
_ne: _,
} = &create_info;
let mut enabled_extensions = Cow::Borrowed(enabled_extensions);
if flags.intersects(InstanceCreateFlags::ENUMERATE_PORTABILITY) {
if library
.supported_extensions_with_layers(enabled_layers.iter().map(String::as_str))?
.khr_portability_enumeration
{
enabled_extensions.to_mut().khr_portability_enumeration = true;
} else {
flags -= InstanceCreateFlags::ENUMERATE_PORTABILITY;
}
}
let enabled_layers_vk: Vec<CString> = enabled_layers
.iter()
.map(|name| CString::new(name.clone()).unwrap())
.collect();
let enabled_layers_ptrs_vk = enabled_layers_vk
.iter()
.map(|layer| layer.as_ptr())
.collect::<SmallVec<[_; 2]>>();
let enabled_extensions_vk: Vec<CString> = enabled_extensions.as_ref().into();
let enabled_extensions_ptrs_vk = enabled_extensions_vk
.iter()
.map(|extension| extension.as_ptr())
.collect::<SmallVec<[_; 2]>>();
let application_name_vk = application_name
.as_ref()
.map(|name| CString::new(name.clone()).unwrap());
let engine_name_vk = engine_name
.as_ref()
.map(|name| CString::new(name.clone()).unwrap());
let application_info_vk = ash::vk::ApplicationInfo {
p_application_name: application_name_vk
.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_vk
.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
.unwrap()
.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: flags.into(),
p_application_info: &application_info_vk,
enabled_layer_count: enabled_layers_ptrs_vk.len() as u32,
pp_enabled_layer_names: enabled_layers_ptrs_vk.as_ptr(),
enabled_extension_count: enabled_extensions_ptrs_vk.len() as u32,
pp_enabled_extension_names: enabled_extensions_ptrs_vk.as_ptr(),
..Default::default()
};
let mut validation_features_vk = None;
if !enabled_validation_features.is_empty() || !disabled_validation_features.is_empty() {
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 mut debug_utils_messenger_create_infos_vk: Vec<_> = debug_utils_messengers
.iter()
.map(|create_info| {
let &DebugUtilsMessengerCreateInfo {
message_type,
message_severity,
ref user_callback,
_ne: _,
} = 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_ptr() as *const c_void as *mut _,
..Default::default()
}
})
.collect();
for i in 1..debug_utils_messenger_create_infos_vk.len() {
debug_utils_messenger_create_infos_vk[i - 1].p_next =
&debug_utils_messenger_create_infos_vk[i] as *const _ as *const _;
}
if let Some(info) = debug_utils_messenger_create_infos_vk.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()
};
Ok(Self::from_handle(library, handle, create_info))
}
pub unsafe fn from_handle(
library: Arc<VulkanLibrary>,
handle: ash::vk::Instance,
mut create_info: InstanceCreateInfo,
) -> Arc<Self> {
create_info.max_api_version.get_or_insert_with(|| {
let api_version = library.api_version();
if api_version < Version::V1_1 {
api_version
} else {
Version::HEADER_VERSION
}
});
let InstanceCreateInfo {
flags,
application_name: _,
application_version: _,
engine_name: _,
engine_version: _,
max_api_version,
enabled_layers,
enabled_extensions,
debug_utils_messengers,
enabled_validation_features: _,
disabled_validation_features: _,
_ne: _,
} = create_info;
let max_api_version = max_api_version.unwrap();
let api_version = std::cmp::min(max_api_version, library.api_version());
Arc::new(Instance {
handle,
fns: InstanceFunctions::load(|name| {
library
.get_instance_proc_addr(handle, name.as_ptr())
.map_or(ptr::null(), |func| func as _)
}),
id: Self::next_id(),
flags,
api_version,
enabled_extensions,
enabled_layers,
library,
max_api_version,
_user_callbacks: debug_utils_messengers
.into_iter()
.map(|m| m.user_callback)
.collect(),
physical_devices: WeakArcOnceCache::new(),
physical_device_groups: RwLock::new((false, Vec::new())),
})
}
#[inline]
pub fn library(&self) -> &Arc<VulkanLibrary> {
&self.library
}
#[inline]
pub fn flags(&self) -> InstanceCreateFlags {
self.flags
}
#[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| {
self.physical_devices.get_or_try_insert(handle, |&handle| {
PhysicalDevice::from_handle(self.clone(), handle)
})
})
.collect::<Result<_, _>>()?;
Ok(physical_devices.into_iter())
}
}
#[inline]
pub fn enumerate_physical_device_groups(
self: &Arc<Self>,
) -> Result<impl ExactSizeIterator<Item = PhysicalDeviceGroupProperties>, Validated<VulkanError>>
{
self.validate_enumerate_physical_device_groups()?;
unsafe { Ok(self.enumerate_physical_device_groups_unchecked()?) }
}
fn validate_enumerate_physical_device_groups(&self) -> Result<(), Box<ValidationError>> {
if !(self.api_version() >= Version::V1_1
|| self.enabled_extensions().khr_device_group_creation)
{
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]),
RequiresAllOf(&[Requires::InstanceExtension("khr_device_group_creation")]),
]),
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn enumerate_physical_device_groups_unchecked(
self: &Arc<Self>,
) -> Result<impl ExactSizeIterator<Item = PhysicalDeviceGroupProperties>, VulkanError> {
let fns = self.fns();
let enumerate_physical_device_groups = if self.api_version() >= Version::V1_1 {
fns.v1_1.enumerate_physical_device_groups
} else {
fns.khr_device_group_creation
.enumerate_physical_device_groups_khr
};
let properties_vk = loop {
let mut count = 0;
enumerate_physical_device_groups(self.handle, &mut count, ptr::null_mut())
.result()
.map_err(VulkanError::from)?;
let mut properties = Vec::with_capacity(count as usize);
let result =
enumerate_physical_device_groups(self.handle, &mut count, properties.as_mut_ptr());
match result {
ash::vk::Result::SUCCESS => {
properties.set_len(count as usize);
break properties;
}
ash::vk::Result::INCOMPLETE => (),
err => return Err(VulkanError::from(err)),
}
};
let mut properties: SmallVec<[_; 4]> = SmallVec::with_capacity(properties_vk.len());
let mut properties_raw: Vec<_> = Vec::with_capacity(properties_vk.len());
for properties_vk in properties_vk {
let &ash::vk::PhysicalDeviceGroupProperties {
physical_device_count,
physical_devices,
subset_allocation,
..
} = &properties_vk;
properties.push(PhysicalDeviceGroupProperties {
physical_devices: physical_devices[..physical_device_count as usize]
.iter()
.map(|&handle| {
self.physical_devices.get_or_try_insert(handle, |&handle| {
PhysicalDevice::from_handle(self.clone(), handle)
})
})
.collect::<Result<_, _>>()?,
subset_allocation: subset_allocation != ash::vk::FALSE,
});
properties_raw.push(PhysicalDeviceGroupPropertiesRaw {
physical_device_count,
physical_devices,
subset_allocation,
});
}
*self.physical_device_groups.write() = (true, properties_raw);
Ok(properties.into_iter())
}
pub fn is_same_device_group<'a>(
self: &Arc<Self>,
physical_devices: impl IntoIterator<Item = &'a PhysicalDevice>,
) -> bool {
let mut physical_devices = physical_devices.into_iter();
let first = match physical_devices.next() {
Some(x) => x,
None => return false,
};
if first.instance() != self {
return false;
}
let lock = {
let lock = self.physical_device_groups.read();
if lock.0 {
lock
} else {
drop(lock);
if self.enumerate_physical_device_groups().is_err() {
return false;
}
self.physical_device_groups.read()
}
};
lock.1
.iter()
.find_map(|properties_raw| {
let group = &properties_raw.physical_devices
[..properties_raw.physical_device_count as usize];
group.contains(&first.handle()).then_some(group)
})
.map_or(false, |group| {
physical_devices.all(|physical_device| {
physical_device.instance() == self && group.contains(&physical_device.handle())
})
})
}
}
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,
flags,
api_version,
enabled_extensions,
enabled_layers,
library,
max_api_version,
_user_callbacks: _,
physical_devices: _,
physical_device_groups: _,
} = self;
f.debug_struct("Instance")
.field("handle", handle)
.field("fns", fns)
.field("id", id)
.field("flags", flags)
.field("api_version", api_version)
.field("enabled_extensions", enabled_extensions)
.field("enabled_layers", enabled_layers)
.field("library", library)
.field("max_api_version", max_api_version)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub struct InstanceCreateInfo {
pub flags: InstanceCreateFlags,
pub application_name: Option<String>,
pub application_version: Version,
pub engine_name: Option<String>,
pub engine_version: Version,
pub max_api_version: Option<Version>,
pub enabled_layers: Vec<String>,
pub enabled_extensions: InstanceExtensions,
pub debug_utils_messengers: Vec<DebugUtilsMessengerCreateInfo>,
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 {
flags: InstanceCreateFlags::empty(),
application_name: None,
application_version: Version::major_minor(0, 0),
engine_name: None,
engine_version: Version::major_minor(0, 0),
max_api_version: None,
enabled_layers: Vec::new(),
enabled_extensions: InstanceExtensions::empty(),
debug_utils_messengers: Vec::new(),
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()
}
}
pub(crate) fn validate(&self, library: &VulkanLibrary) -> Result<(), Box<ValidationError>> {
let &Self {
flags,
application_name: _,
application_version: _,
engine_name: _,
engine_version: _,
max_api_version,
enabled_layers: _,
ref enabled_extensions,
ref debug_utils_messengers,
ref enabled_validation_features,
ref disabled_validation_features,
_ne: _,
} = self;
let max_api_version = max_api_version.unwrap_or_default();
let api_version = std::cmp::min(max_api_version, library.api_version());
if max_api_version < Version::V1_0 {
return Err(Box::new(ValidationError {
context: "max_api_version".into(),
problem: "is less than 1.0".into(),
vuids: &["VUID-VkApplicationInfo-apiVersion-04010"],
..Default::default()
}));
}
flags
.validate_instance_raw(api_version, enabled_extensions)
.map_err(|err| {
err.add_context("flags")
.set_vuids(&["VUID-VkInstanceCreateInfo-flags-parameter"])
})?;
if !debug_utils_messengers.is_empty() {
if !enabled_extensions.ext_debug_utils {
return Err(Box::new(ValidationError {
context: "debug_utils_messengers".into(),
problem: "is not empty".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::InstanceExtension("ext_debug_utils"),
])]),
vuids: &["VUID-VkInstanceCreateInfo-pNext-04926"],
}));
}
for (index, messenger_create_info) in debug_utils_messengers.iter().enumerate() {
messenger_create_info
.validate_raw(api_version, enabled_extensions)
.map_err(|err| err.add_context(format!("debug_utils_messengers[{}]", index)))?;
}
}
if !enabled_validation_features.is_empty() {
if !enabled_extensions.ext_validation_features {
return Err(Box::new(ValidationError {
context: "enabled_validation_features".into(),
problem: "is not empty".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::InstanceExtension("ext_validation_features"),
])]),
..Default::default()
}));
}
for (index, enabled) in enabled_validation_features.iter().enumerate() {
enabled
.validate_instance_raw(api_version, enabled_extensions)
.map_err(|err| {
err.add_context(format!("enabled_validation_features[{}]", index))
.set_vuids(&[
"VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-parameter",
])
})?;
}
if enabled_validation_features
.contains(&ValidationFeatureEnable::GpuAssistedReserveBindingSlot)
&& !enabled_validation_features.contains(&ValidationFeatureEnable::GpuAssisted)
{
return Err(Box::new(ValidationError {
context: "enabled_validation_features".into(),
problem: "contains `ValidationFeatureEnable::GpuAssistedReserveBindingSlot`, \
but does not also contain \
`ValidationFeatureEnable::GpuAssisted`"
.into(),
vuids: &["VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-02967"],
..Default::default()
}));
}
if enabled_validation_features.contains(&ValidationFeatureEnable::DebugPrintf)
&& enabled_validation_features.contains(&ValidationFeatureEnable::GpuAssisted)
{
return Err(Box::new(ValidationError {
context: "enabled_validation_features".into(),
problem: "contains both `ValidationFeatureEnable::DebugPrintf` and \
`ValidationFeatureEnable::GpuAssisted`"
.into(),
vuids: &["VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-02968"],
..Default::default()
}));
}
}
if !disabled_validation_features.is_empty() {
if !enabled_extensions.ext_validation_features {
return Err(Box::new(ValidationError {
context: "disabled_validation_features".into(),
problem: "is not empty".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
Requires::InstanceExtension("ext_validation_features"),
])]),
..Default::default()
}));
}
for (index, disabled) in disabled_validation_features.iter().enumerate() {
disabled
.validate_instance_raw(api_version, enabled_extensions)
.map_err(|err| {
err.add_context(format!("disabled_validation_features[{}]", index)).set_vuids(
&[
"VUID-VkValidationFeaturesEXT-pDisabledValidationFeatures-parameter",
],
)
})?;
}
}
Ok(())
}
}
vulkan_bitflags! {
#[non_exhaustive]
InstanceCreateFlags = InstanceCreateFlags(u32);
ENUMERATE_PORTABILITY = ENUMERATE_PORTABILITY_KHR,
}
pub unsafe trait InstanceOwned {
fn instance(&self) -> &Arc<Instance>;
}
unsafe impl<T> InstanceOwned for T
where
T: Deref,
T::Target: InstanceOwned,
{
fn instance(&self) -> &Arc<Instance> {
(**self).instance()
}
}
#[derive(PartialEq, Eq, Hash)]
#[repr(transparent)]
pub(crate) struct InstanceOwnedDebugWrapper<T>(pub(crate) T);
impl<T> InstanceOwnedDebugWrapper<T> {
pub fn cast_slice_inner(slice: &[Self]) -> &[T] {
unsafe { slice::from_raw_parts(slice as *const _ as *const _, slice.len()) }
}
}
impl<T> Debug for InstanceOwnedDebugWrapper<T>
where
T: VulkanObject + InstanceOwned,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
write!(
f,
"0x{:x} (instance: 0x{:x})",
self.handle().as_raw(),
self.instance().handle().as_raw(),
)
}
}
impl<T> Deref for InstanceOwnedDebugWrapper<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use crate::instance::InstanceExtensions;
use std::ffi::CString;
#[test]
fn empty_extensions() {
let i: Vec<CString> = (&InstanceExtensions::empty()).into();
assert!(i.get(0).is_none());
}
#[test]
fn into_iter() {
let extensions = InstanceExtensions {
khr_display: true,
..InstanceExtensions::empty()
};
for (name, enabled) in extensions {
if name == "VK_KHR_display" {
assert!(enabled);
} else {
assert!(!enabled);
}
}
}
#[test]
fn create_instance() {
let _ = instance!();
}
}