use self::physical::{PhysicalDevice, QueueFamily};
pub(crate) use self::{features::FeaturesFfi, properties::PropertiesFfi};
pub use self::{
features::{FeatureRestriction, FeatureRestrictionError, Features},
properties::Properties,
};
use crate::{
check_errors,
command_buffer::pool::StandardCommandPool,
descriptor_set::pool::StdDescriptorPool,
instance::Instance,
memory::{pool::StdMemoryPool, ExternalMemoryHandleType},
Error, OomError, SynchronizedVulkanObject, Version, VulkanObject,
};
pub use crate::{
device::extensions::DeviceExtensions,
extensions::{ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError},
fns::DeviceFunctions,
};
use ash::vk::Handle;
use smallvec::SmallVec;
use std::{
collections::{hash_map::Entry, HashMap},
error,
ffi::{CStr, CString},
fmt,
fs::File,
hash::{Hash, Hasher},
mem::{self, MaybeUninit},
ops::Deref,
ptr,
sync::{Arc, Mutex, MutexGuard, Weak},
};
pub(crate) mod extensions;
pub(crate) mod features;
pub mod physical;
pub(crate) mod properties;
#[derive(Debug)]
pub struct Device {
handle: ash::vk::Device,
instance: Arc<Instance>,
physical_device: usize,
api_version: Version,
fns: DeviceFunctions,
standard_pool: Mutex<Weak<StdMemoryPool>>,
standard_descriptor_pool: Mutex<Weak<StdDescriptorPool>>,
standard_command_pools: Mutex<HashMap<u32, Weak<StandardCommandPool>>>,
enabled_extensions: DeviceExtensions,
enabled_features: Features,
active_queue_families: SmallVec<[u32; 2]>,
allocation_count: Mutex<u32>,
fence_pool: Mutex<Vec<ash::vk::Fence>>,
semaphore_pool: Mutex<Vec<ash::vk::Semaphore>>,
event_pool: Mutex<Vec<ash::vk::Event>>,
}
unsafe impl Send for Device {}
unsafe impl Sync for Device {}
impl Device {
pub fn new(
physical_device: PhysicalDevice,
create_info: DeviceCreateInfo,
) -> Result<(Arc<Device>, impl ExactSizeIterator<Item = Arc<Queue>>), DeviceCreationError> {
let DeviceCreateInfo {
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 {
family: 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_families: SmallVec<[_; 2]> =
SmallVec::with_capacity(queue_create_infos.len());
let mut queues_to_get: SmallVec<[_; 2]> = SmallVec::with_capacity(queue_create_infos.len());
for QueueCreateInfo {
family,
queues,
_ne: _,
} in &queue_create_infos
{
assert_eq!(
family.physical_device().internal_object(),
physical_device.internal_object()
);
assert!(
queue_create_infos
.iter()
.filter(|qc2| qc2.family == *family)
.count()
== 1
);
assert!(!queues.is_empty());
assert!(queues
.iter()
.all(|&priority| priority >= 0.0 && priority <= 1.0));
if queues.len() > family.queues_count() {
return Err(DeviceCreationError::TooManyQueuesForFamily);
}
let family = family.id();
queue_create_infos_vk.push(ash::vk::DeviceQueueCreateInfo {
flags: ash::vk::DeviceQueueCreateFlags::empty(),
queue_family_index: family,
queue_count: queues.len() as u32,
p_queue_priorities: queues.as_ptr(), ..Default::default()
});
active_queue_families.push(family);
queues_to_get.extend((0..queues.len() as u32).map(move |id| QueueToGet { family, id }));
}
active_queue_families.sort_unstable();
active_queue_families.dedup();
enabled_extensions.check_requirements(
physical_device.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();
check_errors(fns_i.v1_0.create_device(
physical_device.internal_object(),
&create_info,
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
let fns = DeviceFunctions::load(|name| unsafe {
mem::transmute(fns_i.v1_0.get_device_proc_addr(handle, name.as_ptr()))
});
let device = Arc::new(Device {
handle,
instance: physical_device.instance().clone(),
physical_device: physical_device.index(),
api_version,
fns,
standard_pool: Mutex::new(Weak::new()),
standard_descriptor_pool: Mutex::new(Weak::new()),
standard_command_pools: Mutex::new(Default::default()),
enabled_extensions,
enabled_features,
active_queue_families,
allocation_count: Mutex::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 { family, id }| unsafe {
let mut output = MaybeUninit::uninit();
device
.fns()
.v1_0
.get_device_queue(handle, family, id, output.as_mut_ptr());
Arc::new(Queue {
handle: Mutex::new(output.assume_init()),
device: device.clone(),
family,
id,
})
})
};
Ok((device, queues_iter))
}
#[inline]
pub fn api_version(&self) -> Version {
self.api_version
}
#[inline]
pub fn fns(&self) -> &DeviceFunctions {
&self.fns
}
pub unsafe fn wait(&self) -> Result<(), OomError> {
check_errors(self.fns.v1_0.device_wait_idle(self.handle))?;
Ok(())
}
#[inline]
pub fn instance(&self) -> &Arc<Instance> {
&self.instance
}
#[inline]
pub fn physical_device(&self) -> PhysicalDevice {
PhysicalDevice::from_index(&self.instance, self.physical_device).unwrap()
}
#[inline]
pub fn active_queue_families<'a>(&'a self) -> impl ExactSizeIterator<Item = QueueFamily<'a>> {
let physical_device = self.physical_device();
self.active_queue_families
.iter()
.map(move |&id| physical_device.queue_family_by_id(id).unwrap())
}
#[inline]
pub fn enabled_extensions(&self) -> &DeviceExtensions {
&self.enabled_extensions
}
#[inline]
pub fn enabled_features(&self) -> &Features {
&self.enabled_features
}
pub fn standard_pool(me: &Arc<Self>) -> Arc<StdMemoryPool> {
let mut pool = me.standard_pool.lock().unwrap();
if let Some(p) = pool.upgrade() {
return p;
}
let new_pool = StdMemoryPool::new(me.clone());
*pool = Arc::downgrade(&new_pool);
new_pool
}
pub fn standard_descriptor_pool(me: &Arc<Self>) -> Arc<StdDescriptorPool> {
let mut pool = me.standard_descriptor_pool.lock().unwrap();
if let Some(p) = pool.upgrade() {
return p;
}
let new_pool = Arc::new(StdDescriptorPool::new(me.clone()));
*pool = Arc::downgrade(&new_pool);
new_pool
}
pub fn standard_command_pool(me: &Arc<Self>, queue: QueueFamily) -> Arc<StandardCommandPool> {
let mut standard_command_pools = me.standard_command_pools.lock().unwrap();
match standard_command_pools.entry(queue.id()) {
Entry::Occupied(mut entry) => {
if let Some(pool) = entry.get().upgrade() {
return pool;
}
let new_pool = Arc::new(StandardCommandPool::new(me.clone(), queue));
*entry.get_mut() = Arc::downgrade(&new_pool);
new_pool
}
Entry::Vacant(entry) => {
let new_pool = Arc::new(StandardCommandPool::new(me.clone(), queue));
entry.insert(Arc::downgrade(&new_pool));
new_pool
}
}
}
pub(crate) fn allocation_count(&self) -> &Mutex<u32> {
&self.allocation_count
}
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
}
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;
if handle_type == ExternalMemoryHandleType::OpaqueFd {
return Err(MemoryFdPropertiesError::InvalidExternalHandleType);
}
let mut memory_fd_properties = ash::vk::MemoryFdPropertiesKHR::default();
let fns = self.fns();
check_errors(fns.khr_external_memory_fd.get_memory_fd_properties_khr(
self.handle,
handle_type.into(),
file.into_raw_fd(),
&mut memory_fd_properties,
))?;
Ok(MemoryFdProperties {
memory_type_bits: memory_fd_properties.memory_type_bits,
})
}
}
pub fn set_object_name<T: VulkanObject + DeviceOwned>(
&self,
object: &T,
name: &CStr,
) -> Result<(), OomError> {
assert!(object.device().internal_object() == self.internal_object());
unsafe {
self.set_object_name_raw(T::Object::TYPE, object.internal_object().as_raw(), name)
}
}
pub unsafe fn set_object_name_raw(
&self,
ty: ash::vk::ObjectType,
object: u64,
name: &CStr,
) -> Result<(), OomError> {
let info = ash::vk::DebugUtilsObjectNameInfoEXT {
object_type: ty,
object_handle: object,
p_object_name: name.as_ptr(),
..Default::default()
};
check_errors(
self.instance
.fns()
.ext_debug_utils
.set_debug_utils_object_name_ext(self.handle, &info),
)?;
Ok(())
}
}
impl Drop for Device {
#[inline]
fn drop(&mut self) {
unsafe {
for &raw_fence in self.fence_pool.lock().unwrap().iter() {
self.fns
.v1_0
.destroy_fence(self.handle, raw_fence, ptr::null());
}
for &raw_sem in self.semaphore_pool.lock().unwrap().iter() {
self.fns
.v1_0
.destroy_semaphore(self.handle, raw_sem, ptr::null());
}
for &raw_event in self.event_pool.lock().unwrap().iter() {
self.fns
.v1_0
.destroy_event(self.handle, raw_event, ptr::null());
}
self.fns.v1_0.destroy_device(self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for Device {
type Object = ash::vk::Device;
#[inline]
fn internal_object(&self) -> ash::vk::Device {
self.handle
}
}
impl PartialEq for Device {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.instance == other.instance
}
}
impl Eq for Device {}
impl Hash for Device {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.instance.hash(state);
}
}
#[derive(Copy, Clone, Debug)]
pub enum DeviceCreationError {
InitializationFailed,
TooManyObjects,
DeviceLost,
FeatureNotPresent,
ExtensionNotPresent,
TooManyQueuesForFamily,
PriorityOutOfRange,
OutOfHostMemory,
OutOfDeviceMemory,
ExtensionRestrictionNotMet(ExtensionRestrictionError),
FeatureRestrictionNotMet(FeatureRestrictionError),
}
impl error::Error for DeviceCreationError {}
impl fmt::Display for DeviceCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::InitializationFailed => {
write!(
fmt,
"failed to create the device for an implementation-specific reason"
)
}
Self::OutOfHostMemory => write!(fmt, "no memory available on the host"),
Self::OutOfDeviceMemory => {
write!(fmt, "no memory available on the graphical device")
}
Self::DeviceLost => write!(fmt, "failed to connect to the device"),
Self::TooManyQueuesForFamily => {
write!(fmt, "tried to create too many queues for a given family")
}
Self::FeatureNotPresent => {
write!(
fmt,
"some of the requested features are unsupported by the physical device"
)
}
Self::PriorityOutOfRange => {
write!(
fmt,
"the priority of one of the queues is out of the [0.0; 1.0] range"
)
}
Self::ExtensionNotPresent => {
write!(fmt,"some of the requested device extensions are not supported by the physical device")
}
Self::TooManyObjects => {
write!(fmt,"you have reached the limit to the number of devices that can be created from the same physical device")
}
Self::ExtensionRestrictionNotMet(err) => err.fmt(fmt),
Self::FeatureRestrictionNotMet(err) => err.fmt(fmt),
}
}
}
impl From<Error> for DeviceCreationError {
#[inline]
fn from(err: Error) -> Self {
match err {
Error::InitializationFailed => Self::InitializationFailed,
Error::OutOfHostMemory => Self::OutOfHostMemory,
Error::OutOfDeviceMemory => Self::OutOfDeviceMemory,
Error::DeviceLost => Self::DeviceLost,
Error::ExtensionNotPresent => Self::ExtensionNotPresent,
Error::FeatureNotPresent => Self::FeatureNotPresent,
Error::TooManyObjects => Self::TooManyObjects,
_ => panic!("Unexpected error value: {}", err as i32),
}
}
}
impl From<ExtensionRestrictionError> for DeviceCreationError {
#[inline]
fn from(err: ExtensionRestrictionError) -> Self {
Self::ExtensionRestrictionNotMet(err)
}
}
impl From<FeatureRestrictionError> for DeviceCreationError {
#[inline]
fn from(err: FeatureRestrictionError) -> Self {
Self::FeatureRestrictionNotMet(err)
}
}
#[derive(Clone, Debug)]
pub struct DeviceCreateInfo<'qf> {
pub enabled_extensions: DeviceExtensions,
pub enabled_features: Features,
pub queue_create_infos: Vec<QueueCreateInfo<'qf>>,
pub _ne: crate::NonExhaustive,
}
impl Default for DeviceCreateInfo<'static> {
#[inline]
fn default() -> Self {
Self {
enabled_extensions: DeviceExtensions::none(),
enabled_features: Features::none(),
queue_create_infos: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
pub struct QueueCreateInfo<'qf> {
pub family: QueueFamily<'qf>,
pub queues: Vec<f32>,
pub _ne: crate::NonExhaustive,
}
impl<'qf> QueueCreateInfo<'qf> {
#[inline]
pub fn family(family: QueueFamily) -> QueueCreateInfo {
QueueCreateInfo {
family,
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,
{
#[inline]
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,
InvalidExternalHandle,
InvalidExternalHandleType,
NotSupported,
}
impl error::Error for MemoryFdPropertiesError {}
impl fmt::Display for MemoryFdPropertiesError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::OutOfHostMemory => write!(fmt, "no memory available on the host"),
Self::InvalidExternalHandle => {
write!(fmt, "the provided external handle was not valid")
}
Self::InvalidExternalHandleType => {
write!(fmt, "the provided external handle type was not valid")
}
Self::NotSupported => write!(
fmt,
"the `khr_external_memory_fd` extension was not enabled on the device",
),
}
}
}
impl From<Error> for MemoryFdPropertiesError {
#[inline]
fn from(err: Error) -> Self {
match err {
Error::OutOfHostMemory => Self::OutOfHostMemory,
Error::InvalidExternalHandle => Self::InvalidExternalHandle,
_ => panic!("Unexpected error value: {}", err as i32),
}
}
}
#[derive(Debug)]
pub struct Queue {
handle: Mutex<ash::vk::Queue>,
device: Arc<Device>,
family: u32,
id: u32, }
impl Queue {
#[inline]
pub fn device(&self) -> &Arc<Device> {
&self.device
}
#[inline]
pub fn family(&self) -> QueueFamily {
self.device
.physical_device()
.queue_family_by_id(self.family)
.unwrap()
}
#[inline]
pub fn id_within_family(&self) -> u32 {
self.id
}
#[inline]
pub fn wait(&self) -> Result<(), OomError> {
unsafe {
let fns = self.device.fns();
let handle = self.handle.lock().unwrap();
check_errors(fns.v1_0.queue_wait_idle(*handle))?;
Ok(())
}
}
}
unsafe impl SynchronizedVulkanObject for Queue {
type Object = ash::vk::Queue;
#[inline]
fn internal_object_guard(&self) -> MutexGuard<Self::Object> {
self.handle.lock().unwrap()
}
}
unsafe impl DeviceOwned for Queue {
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for Queue {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.family == other.family && self.device == other.device
}
}
impl Eq for Queue {}
impl Hash for Queue {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.family.hash(state);
self.device.hash(state);
}
}
#[cfg(test)]
mod tests {
use crate::device::physical::PhysicalDevice;
use crate::device::{Device, DeviceCreateInfo, DeviceCreationError, QueueCreateInfo};
use crate::device::{FeatureRestriction, FeatureRestrictionError, Features};
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 = match PhysicalDevice::enumerate(&instance).next() {
Some(p) => p,
None => return,
};
let family = physical.queue_families().next().unwrap();
let queues = (0..family.queues_count() + 1).map(|_| (family, 1.0));
match Device::new(
physical,
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queues: (0..family.queues_count() + 1).map(|_| (0.5)).collect(),
..QueueCreateInfo::family(family)
}],
..Default::default()
},
) {
Err(DeviceCreationError::TooManyQueuesForFamily) => return, _ => panic!(),
};
}
#[test]
fn unsupposed_features() {
let instance = instance!();
let physical = match PhysicalDevice::enumerate(&instance).next() {
Some(p) => p,
None => return,
};
let family = physical.queue_families().next().unwrap();
let features = Features::all();
if physical.supported_features().is_superset_of(&features) {
return;
}
match Device::new(
physical,
DeviceCreateInfo {
enabled_features: features,
queue_create_infos: vec![QueueCreateInfo::family(family)],
..Default::default()
},
) {
Err(DeviceCreationError::FeatureRestrictionNotMet(FeatureRestrictionError {
restriction: FeatureRestriction::NotSupported,
..
})) => return, _ => panic!(),
};
}
#[test]
fn priority_out_of_range() {
let instance = instance!();
let physical = match PhysicalDevice::enumerate(&instance).next() {
Some(p) => p,
None => return,
};
let family = physical.queue_families().next().unwrap();
assert_should_panic!({
Device::new(
physical,
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queues: vec![1.4],
..QueueCreateInfo::family(family)
}],
..Default::default()
},
)
});
assert_should_panic!({
Device::new(
physical,
DeviceCreateInfo {
queue_create_infos: vec![QueueCreateInfo {
queues: vec![-0.2],
..QueueCreateInfo::family(family)
}],
..Default::default()
},
)
});
}
}