use self::physical::PhysicalDevice;
pub(crate) use self::{features::FeaturesFfi, properties::PropertiesFfi};
pub use self::{
features::{FeatureRestriction, FeatureRestrictionError, Features},
properties::Properties,
queue::{Queue, QueueError, QueueFamilyProperties, QueueFlags, QueueGuard},
};
pub use crate::{
device::extensions::DeviceExtensions,
extensions::{ExtensionRestriction, ExtensionRestrictionError},
fns::DeviceFunctions,
};
use crate::{
instance::Instance, memory::ExternalMemoryHandleType, OomError, RequirementNotMet,
RequiresOneOf, Version, VulkanError, VulkanObject,
};
use ash::vk::Handle;
use parking_lot::Mutex;
use smallvec::SmallVec;
use std::{
error::Error,
ffi::CString,
fmt::{Display, Error as FmtError, Formatter},
fs::File,
mem::MaybeUninit,
num::NonZeroU64,
ops::Deref,
ptr,
sync::{
atomic::{AtomicU32, Ordering},
Arc,
},
};
pub(crate) mod extensions;
pub(crate) mod features;
pub mod physical;
pub(crate) mod properties;
mod queue;
#[derive(Debug)]
pub struct Device {
handle: ash::vk::Device,
physical_device: Arc<PhysicalDevice>,
id: NonZeroU64,
api_version: Version,
fns: DeviceFunctions,
enabled_extensions: DeviceExtensions,
enabled_features: Features,
active_queue_family_indices: SmallVec<[u32; 2]>,
pub(crate) allocation_count: AtomicU32,
fence_pool: Mutex<Vec<ash::vk::Fence>>,
semaphore_pool: Mutex<Vec<ash::vk::Semaphore>>,
event_pool: Mutex<Vec<ash::vk::Event>>,
}
impl Device {
pub fn new(
physical_device: Arc<PhysicalDevice>,
create_info: DeviceCreateInfo,
) -> Result<(Arc<Device>, impl ExactSizeIterator<Item = Arc<Queue>>), DeviceCreationError> {
let DeviceCreateInfo {
mut enabled_extensions,
mut enabled_features,
queue_create_infos,
_ne: _,
} = create_info;
let instance = physical_device.instance();
let fns_i = instance.fns();
let api_version = physical_device.api_version();
struct QueueToGet {
queue_family_index: u32,
id: u32,
}
assert!(!queue_create_infos.is_empty());
let mut queue_create_infos_vk: SmallVec<[_; 2]> =
SmallVec::with_capacity(queue_create_infos.len());
let mut active_queue_family_indices: SmallVec<[_; 2]> =
SmallVec::with_capacity(queue_create_infos.len());
let mut queues_to_get: SmallVec<[_; 2]> = SmallVec::with_capacity(queue_create_infos.len());
for queue_create_info in &queue_create_infos {
let &QueueCreateInfo {
queue_family_index,
ref queues,
_ne: _,
} = queue_create_info;
let queue_family_properties =
&physical_device.queue_family_properties()[queue_family_index as usize];
assert!(
queue_create_infos
.iter()
.filter(|qc2| qc2.queue_family_index == queue_family_index)
.count()
== 1
);
assert!(!queues.is_empty());
assert!(queues
.iter()
.all(|&priority| (0.0..=1.0).contains(&priority)));
if queues.len() > queue_family_properties.queue_count as usize {
return Err(DeviceCreationError::TooManyQueuesForFamily);
}
queue_create_infos_vk.push(ash::vk::DeviceQueueCreateInfo {
flags: ash::vk::DeviceQueueCreateFlags::empty(),
queue_family_index,
queue_count: queues.len() as u32,
p_queue_priorities: queues.as_ptr(), ..Default::default()
});
active_queue_family_indices.push(queue_family_index);
queues_to_get.extend((0..queues.len() as u32).map(move |id| QueueToGet {
queue_family_index,
id,
}));
}
active_queue_family_indices.sort_unstable();
active_queue_family_indices.dedup();
let supported_extensions = physical_device.supported_extensions();
if supported_extensions.khr_portability_subset {
enabled_extensions.khr_portability_subset = true;
}
enabled_extensions.check_requirements(
supported_extensions,
api_version,
instance.enabled_extensions(),
)?;
let enabled_extensions_strings = Vec::<CString>::from(&enabled_extensions);
let enabled_extensions_ptrs = enabled_extensions_strings
.iter()
.map(|extension| extension.as_ptr())
.collect::<SmallVec<[_; 16]>>();
enabled_features.robust_buffer_access = true;
enabled_features.check_requirements(
physical_device.supported_features(),
api_version,
&enabled_extensions,
)?;
let mut features_ffi = FeaturesFfi::default();
features_ffi.make_chain(
api_version,
&enabled_extensions,
instance.enabled_extensions(),
);
features_ffi.write(&enabled_features);
let enabled_layers_cstr: Vec<CString> = instance
.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 has_khr_get_physical_device_properties2 = instance
.enabled_extensions()
.khr_get_physical_device_properties2;
let mut create_info = ash::vk::DeviceCreateInfo {
flags: ash::vk::DeviceCreateFlags::empty(),
queue_create_info_count: queue_create_infos_vk.len() as u32,
p_queue_create_infos: queue_create_infos_vk.as_ptr(),
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(),
p_enabled_features: ptr::null(),
..Default::default()
};
if has_khr_get_physical_device_properties2 {
create_info.p_next = features_ffi.head_as_ref() as *const _ as _;
} else {
create_info.p_enabled_features = &features_ffi.head_as_ref().features;
}
let handle = unsafe {
let mut output = MaybeUninit::uninit();
(fns_i.v1_0.create_device)(
physical_device.handle(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
let fns = DeviceFunctions::load(|name| unsafe {
(fns_i.v1_0.get_device_proc_addr)(handle, name.as_ptr())
.map_or(ptr::null(), |func| func as _)
});
let device = Arc::new(Device {
handle,
physical_device,
id: Self::next_id(),
api_version,
fns,
enabled_extensions,
enabled_features,
active_queue_family_indices,
allocation_count: AtomicU32::new(0),
fence_pool: Mutex::new(Vec::new()),
semaphore_pool: Mutex::new(Vec::new()),
event_pool: Mutex::new(Vec::new()),
});
let queues_iter = {
let device = device.clone();
queues_to_get.into_iter().map(
move |QueueToGet {
queue_family_index,
id,
}| unsafe {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.get_device_queue)(
handle,
queue_family_index,
id,
output.as_mut_ptr(),
);
Queue::from_handle(device.clone(), output.assume_init(), queue_family_index, id)
},
)
};
Ok((device, queues_iter))
}
#[inline]
pub fn api_version(&self) -> Version {
self.api_version
}
#[inline]
pub fn fns(&self) -> &DeviceFunctions {
&self.fns
}
#[inline]
pub fn physical_device(&self) -> &Arc<PhysicalDevice> {
&self.physical_device
}
#[inline]
pub fn instance(&self) -> &Arc<Instance> {
self.physical_device.instance()
}
#[inline]
pub fn active_queue_family_indices(&self) -> &[u32] {
&self.active_queue_family_indices
}
#[inline]
pub fn enabled_extensions(&self) -> &DeviceExtensions {
&self.enabled_extensions
}
#[inline]
pub fn enabled_features(&self) -> &Features {
&self.enabled_features
}
#[inline]
pub fn allocation_count(&self) -> u32 {
self.allocation_count.load(Ordering::Acquire)
}
pub(crate) fn fence_pool(&self) -> &Mutex<Vec<ash::vk::Fence>> {
&self.fence_pool
}
pub(crate) fn semaphore_pool(&self) -> &Mutex<Vec<ash::vk::Semaphore>> {
&self.semaphore_pool
}
pub(crate) fn event_pool(&self) -> &Mutex<Vec<ash::vk::Event>> {
&self.event_pool
}
#[cfg_attr(not(unix), allow(unused_variables))]
#[inline]
pub unsafe fn memory_fd_properties(
&self,
handle_type: ExternalMemoryHandleType,
file: File,
) -> Result<MemoryFdProperties, MemoryFdPropertiesError> {
if !self.enabled_extensions().khr_external_memory_fd {
return Err(MemoryFdPropertiesError::NotSupported);
}
#[cfg(not(unix))]
unreachable!("`khr_external_memory_fd` was somehow enabled on a non-Unix system");
#[cfg(unix)]
{
use std::os::unix::io::IntoRawFd;
handle_type.validate_device(self)?;
if handle_type == ExternalMemoryHandleType::OpaqueFd {
return Err(MemoryFdPropertiesError::InvalidExternalHandleType);
}
let mut memory_fd_properties = ash::vk::MemoryFdPropertiesKHR::default();
let fns = self.fns();
(fns.khr_external_memory_fd.get_memory_fd_properties_khr)(
self.handle,
handle_type.into(),
file.into_raw_fd(),
&mut memory_fd_properties,
)
.result()
.map_err(VulkanError::from)?;
Ok(MemoryFdProperties {
memory_type_bits: memory_fd_properties.memory_type_bits,
})
}
}
pub fn set_debug_utils_object_name<T: VulkanObject + DeviceOwned>(
&self,
object: &T,
object_name: Option<&str>,
) -> Result<(), OomError> {
assert!(object.device().handle() == self.handle());
let object_name_vk = object_name.map(|object_name| CString::new(object_name).unwrap());
let info = ash::vk::DebugUtilsObjectNameInfoEXT {
object_type: T::Handle::TYPE,
object_handle: object.handle().as_raw(),
p_object_name: object_name_vk.map_or(ptr::null(), |object_name| object_name.as_ptr()),
..Default::default()
};
unsafe {
let fns = self.instance().fns();
(fns.ext_debug_utils.set_debug_utils_object_name_ext)(self.handle, &info)
.result()
.map_err(VulkanError::from)?;
}
Ok(())
}
#[inline]
pub unsafe fn wait_idle(&self) -> Result<(), OomError> {
let fns = self.fns();
(fns.v1_0.device_wait_idle)(self.handle)
.result()
.map_err(VulkanError::from)?;
Ok(())
}
}
impl Drop for Device {
#[inline]
fn drop(&mut self) {
let fns = self.fns();
unsafe {
for &raw_fence in self.fence_pool.lock().iter() {
(fns.v1_0.destroy_fence)(self.handle, raw_fence, ptr::null());
}
for &raw_sem in self.semaphore_pool.lock().iter() {
(fns.v1_0.destroy_semaphore)(self.handle, raw_sem, ptr::null());
}
for &raw_event in self.event_pool.lock().iter() {
(fns.v1_0.destroy_event)(self.handle, raw_event, ptr::null());
}
(fns.v1_0.destroy_device)(self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for Device {
type Handle = ash::vk::Device;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
crate::impl_id_counter!(Device);
#[derive(Copy, Clone, Debug)]
pub enum DeviceCreationError {
InitializationFailed,
TooManyObjects,
DeviceLost,
FeatureNotPresent,
ExtensionNotPresent,
TooManyQueuesForFamily,
PriorityOutOfRange,
OutOfHostMemory,
OutOfDeviceMemory,
ExtensionRestrictionNotMet(ExtensionRestrictionError),
FeatureRestrictionNotMet(FeatureRestrictionError),
}
impl Error for DeviceCreationError {}
impl Display for DeviceCreationError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::InitializationFailed => write!(
f,
"failed to create the device for an implementation-specific reason",
),
Self::OutOfHostMemory => write!(f, "no memory available on the host"),
Self::OutOfDeviceMemory => {
write!(f, "no memory available on the graphical device")
}
Self::DeviceLost => write!(f, "failed to connect to the device"),
Self::TooManyQueuesForFamily => {
write!(f, "tried to create too many queues for a given family")
}
Self::FeatureNotPresent => write!(
f,
"some of the requested features are unsupported by the physical device",
),
Self::PriorityOutOfRange => write!(
f,
"the priority of one of the queues is out of the [0.0; 1.0] range",
),
Self::ExtensionNotPresent => write!(
f,
"some of the requested device extensions are not supported by the physical device",
),
Self::TooManyObjects => write!(
f,
"you have reached the limit to the number of devices that can be created from the \
same physical device",
),
Self::ExtensionRestrictionNotMet(err) => err.fmt(f),
Self::FeatureRestrictionNotMet(err) => err.fmt(f),
}
}
}
impl From<VulkanError> for DeviceCreationError {
fn from(err: VulkanError) -> Self {
match err {
VulkanError::InitializationFailed => Self::InitializationFailed,
VulkanError::OutOfHostMemory => Self::OutOfHostMemory,
VulkanError::OutOfDeviceMemory => Self::OutOfDeviceMemory,
VulkanError::DeviceLost => Self::DeviceLost,
VulkanError::ExtensionNotPresent => Self::ExtensionNotPresent,
VulkanError::FeatureNotPresent => Self::FeatureNotPresent,
VulkanError::TooManyObjects => Self::TooManyObjects,
_ => panic!("Unexpected error value"),
}
}
}
impl From<ExtensionRestrictionError> for DeviceCreationError {
fn from(err: ExtensionRestrictionError) -> Self {
Self::ExtensionRestrictionNotMet(err)
}
}
impl From<FeatureRestrictionError> for DeviceCreationError {
fn from(err: FeatureRestrictionError) -> Self {
Self::FeatureRestrictionNotMet(err)
}
}
#[derive(Clone, Debug)]
pub struct DeviceCreateInfo {
pub enabled_extensions: DeviceExtensions,
pub enabled_features: Features,
pub queue_create_infos: Vec<QueueCreateInfo>,
pub _ne: crate::NonExhaustive,
}
impl Default for DeviceCreateInfo {
#[inline]
fn default() -> Self {
Self {
enabled_extensions: DeviceExtensions::empty(),
enabled_features: Features::empty(),
queue_create_infos: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
pub struct QueueCreateInfo {
pub queue_family_index: u32,
pub queues: Vec<f32>,
pub _ne: crate::NonExhaustive,
}
impl Default for QueueCreateInfo {
#[inline]
fn default() -> Self {
Self {
queue_family_index: 0,
queues: vec![0.5],
_ne: crate::NonExhaustive(()),
}
}
}
pub unsafe trait DeviceOwned {
fn device(&self) -> &Arc<Device>;
}
unsafe impl<T> DeviceOwned for T
where
T: Deref,
T::Target: DeviceOwned,
{
fn device(&self) -> &Arc<Device> {
(**self).device()
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct MemoryFdProperties {
pub memory_type_bits: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MemoryFdPropertiesError {
OutOfHostMemory,
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
InvalidExternalHandle,
InvalidExternalHandleType,
NotSupported,
}
impl Error for MemoryFdPropertiesError {}
impl Display for MemoryFdPropertiesError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
match self {
Self::OutOfHostMemory => write!(f, "no memory available on the host"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::InvalidExternalHandle => {
write!(f, "the provided external handle was not valid")
}
Self::InvalidExternalHandleType => {
write!(f, "the provided external handle type was not valid")
}
Self::NotSupported => write!(
f,
"the `khr_external_memory_fd` extension was not enabled on the device",
),
}
}
}
impl From<VulkanError> for MemoryFdPropertiesError {
fn from(err: VulkanError) -> Self {
match err {
VulkanError::OutOfHostMemory => Self::OutOfHostMemory,
VulkanError::InvalidExternalHandle => Self::InvalidExternalHandle,
_ => panic!("Unexpected error value"),
}
}
}
impl From<RequirementNotMet> for MemoryFdPropertiesError {
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
#[cfg(test)]
mod tests {
use crate::device::{
Device, DeviceCreateInfo, DeviceCreationError, FeatureRestriction, FeatureRestrictionError,
Features, QueueCreateInfo,
};
use std::sync::Arc;
#[test]
fn one_ref() {
let (mut device, _) = gfx_dev_and_queue!();
assert!(Arc::get_mut(&mut device).is_some());
}
#[test]
fn too_many_queues() {
let instance = instance!();
let physical_device = match instance.enumerate_physical_devices().unwrap().next() {
Some(p) => p,
None => return,
};
let queue_family_index = 0;
let queue_family_properties =
&physical_device.queue_family_properties()[queue_family_index as usize];
let queues = (0..queue_family_properties.queue_count + 1)
.map(|_| (0.5))
.collect();
match Device::new(
physical_device,
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queue_family_index,
queues,
..Default::default()
}],
..Default::default()
},
) {
Err(DeviceCreationError::TooManyQueuesForFamily) => (), _ => panic!(),
};
}
#[test]
fn unsupported_features() {
let instance = instance!();
let physical_device = match instance.enumerate_physical_devices().unwrap().next() {
Some(p) => p,
None => return,
};
let features = Features::all();
if physical_device.supported_features().contains(&features) {
return;
}
match Device::new(
physical_device,
DeviceCreateInfo {
enabled_features: features,
queue_create_infos: vec![QueueCreateInfo {
queue_family_index: 0,
..Default::default()
}],
..Default::default()
},
) {
Err(DeviceCreationError::FeatureRestrictionNotMet(FeatureRestrictionError {
restriction: FeatureRestriction::NotSupported,
..
})) => (), _ => panic!(),
};
}
#[test]
fn priority_out_of_range() {
let instance = instance!();
let physical_device = match instance.enumerate_physical_devices().unwrap().next() {
Some(p) => p,
None => return,
};
assert_should_panic!({
Device::new(
physical_device.clone(),
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queue_family_index: 0,
queues: vec![1.4],
..Default::default()
}],
..Default::default()
},
)
});
assert_should_panic!({
Device::new(
physical_device,
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queue_family_index: 0,
queues: vec![-0.2],
..Default::default()
}],
..Default::default()
},
)
});
}
}