// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. //! Communication channel with a physical device. //! //! The `Device` is one of the most important objects of Vulkan. Creating a `Device` is required //! before you can create buffers, textures, shaders, etc. //! //! Basic example: //! //! ```no_run //! use vulkano::device::Device; //! use vulkano::device::DeviceExtensions; //! use vulkano::device::Features; //! use vulkano::instance::Instance; //! use vulkano::instance::InstanceExtensions; //! use vulkano::device::physical::PhysicalDevice; //! use vulkano::Version; //! //! // Creating the instance. See the documentation of the `instance` module. //! let instance = match Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None) { //! Ok(i) => i, //! Err(err) => panic!("Couldn't build instance: {:?}", err) //! }; //! //! // We just choose the first physical device. In a real application you would choose depending //! // on the capabilities of the physical device and the user's preferences. //! let physical_device = PhysicalDevice::enumerate(&instance).next().expect("No physical device"); //! //! // Here is the device-creating code. //! let device = { //! let queue_family = physical_device.queue_families().next().unwrap(); //! let features = Features::none(); //! let ext = DeviceExtensions::none(); //! //! match Device::new(physical_device, &features, &ext, Some((queue_family, 1.0))) { //! Ok(d) => d, //! Err(err) => panic!("Couldn't build device: {:?}", err) //! } //! }; //! ``` //! //! # Features and extensions //! //! Two of the parameters that you pass to `Device::new` are the list of the features and the list //! of extensions to enable on the newly-created device. //! //! > **Note**: Device extensions are the same as instance extensions, except for the device. //! > Features are similar to extensions, except that they are part of the core Vulkan //! > specifications instead of being separate documents. //! //! Some Vulkan capabilities, such as swapchains (that allow you to render on the screen) or //! geometry shaders for example, require that you enable a certain feature or extension when you //! create the device. Contrary to OpenGL, you can't use the functions provided by a feature or an //! extension if you didn't explicitly enable it when creating the device. //! //! Not all physical devices support all possible features and extensions. For example mobile //! devices tend to not support geometry shaders, because their hardware is not capable of it. You //! can query what is supported with respectively `PhysicalDevice::supported_features` and //! `DeviceExtensions::supported_by_device`. //! //! > **Note**: The fact that you need to manually enable features at initialization also means //! > that you don't need to worry about a capability not being supported later on in your code. //! //! # Queues //! //! Each physical device proposes one or more *queues* that are divided in *queue families*. A //! queue is a thread of execution to which you can submit commands that the GPU will execute. //! //! > **Note**: You can think of a queue like a CPU thread. Each queue executes its commands one //! > after the other, and queues run concurrently. A GPU behaves similarly to the hyper-threading //! > technology, in the sense that queues will only run partially in parallel. //! //! The Vulkan API requires that you specify the list of queues that you are going to use at the //! same time as when you create the device. This is done in vulkano by passing an iterator where //! each element is a tuple containing a queue family and a number between 0.0 and 1.0 indicating //! the priority of execution of the queue relative to the others. //! //! TODO: write better doc here //! //! The `Device::new` function returns the newly-created device, but also the list of queues. //! //! # Extended example //! //! TODO: write pub(crate) use self::features::FeaturesFfi; pub use self::features::{FeatureRestriction, FeatureRestrictionError, Features}; pub use self::properties::Properties; pub(crate) use self::properties::PropertiesFfi; pub use crate::autogen::DeviceExtensions; use crate::check_errors; use crate::command_buffer::pool::StandardCommandPool; use crate::descriptor_set::pool::StdDescriptorPool; use crate::device::physical::PhysicalDevice; use crate::device::physical::QueueFamily; pub use crate::extensions::{ ExtensionRestriction, ExtensionRestrictionError, SupportedExtensionsError, }; use crate::fns::DeviceFunctions; use crate::format::Format; use crate::image::ImageCreateFlags; use crate::image::ImageFormatProperties; use crate::image::ImageTiling; use crate::image::ImageType; use crate::image::ImageUsage; use crate::instance::Instance; use crate::memory::pool::StdMemoryPool; use crate::Error; use crate::OomError; use crate::SynchronizedVulkanObject; use crate::Version; use crate::VulkanObject; use ash::vk::Handle; use fnv::FnvHasher; use smallvec::SmallVec; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::error; use std::ffi::CStr; use std::ffi::CString; use std::fmt; use std::hash::BuildHasherDefault; use std::hash::Hash; use std::hash::Hasher; use std::mem; use std::mem::MaybeUninit; use std::ops::Deref; use std::ptr; use std::sync::Arc; use std::sync::Mutex; use std::sync::MutexGuard; use std::sync::Weak; pub(crate) mod extensions; pub(crate) mod features; pub mod physical; pub(crate) mod properties; /// Represents a Vulkan context. pub struct Device { instance: Arc, physical_device: usize, device: ash::vk::Device, // The highest version that is supported for this device. // This is the minimum of Instance::max_api_version and PhysicalDevice::api_version. api_version: Version, fns: DeviceFunctions, standard_pool: Mutex>, standard_descriptor_pool: Mutex>, standard_command_pools: Mutex, BuildHasherDefault>>, features: Features, extensions: DeviceExtensions, active_queue_families: SmallVec<[u32; 8]>, allocation_count: Mutex, fence_pool: Mutex>, semaphore_pool: Mutex>, event_pool: Mutex>, } // The `StandardCommandPool` type doesn't implement Send/Sync, so we have to manually reimplement // them for the device itself. unsafe impl Send for Device {} unsafe impl Sync for Device {} impl Device { /// Builds a new Vulkan device for the given physical device. /// /// You must pass two things when creating a logical device: /// /// - A list of optional Vulkan features that must be enabled on the device. Note that if a /// feature is not enabled at device creation, you can't use it later even it it's supported /// by the physical device. /// /// - An iterator to a list of queues to create. Each element of the iterator must indicate /// the family whose queue belongs to and a priority between 0.0 and 1.0 to assign to it. /// A queue with a higher value indicates that the commands will execute faster than on a /// queue with a lower value. Note however that no guarantee can be made on the way the /// priority value is handled by the implementation. /// /// # Panic /// /// - Panics if one of the queue families doesn't belong to the given device. /// // TODO: return Arc and handle synchronization in the Queue // TODO: should take the PhysicalDevice by value pub fn new<'a, I>( physical_device: PhysicalDevice, requested_features: &Features, requested_extensions: &DeviceExtensions, queue_families: I, ) -> Result<(Arc, QueuesIter), DeviceCreationError> where I: IntoIterator, f32)>, { let instance = physical_device.instance(); let fns_i = instance.fns(); let api_version = physical_device.api_version(); // Check if the extensions are correct requested_extensions.check_requirements( physical_device.supported_extensions(), api_version, instance.enabled_extensions(), )?; let mut requested_features = requested_features.clone(); // TODO: The plan regarding `robust_buffer_access` is to check the shaders' code to see // if they can possibly perform out-of-bounds reads and writes. If the user tries // to use a shader that can perform out-of-bounds operations without having // `robust_buffer_access` enabled, an error is returned. // // However for the moment this verification isn't performed. In order to be safe, // we always enable the `robust_buffer_access` feature as it is guaranteed to be // supported everywhere. // // The only alternative (while waiting for shaders introspection to work) is to // make all shaders depend on `robust_buffer_access`. But since usually the // majority of shaders don't need this feature, it would be very annoying to have // to enable it manually when you don't need it. // // Note that if we ever remove this, don't forget to adjust the change in // `Device`'s construction below. requested_features.robust_buffer_access = true; // Check if the features are correct requested_features.check_requirements( physical_device.supported_features(), api_version, requested_extensions, )?; // device creation let (device, queues) = unsafe { // each element of `queues` is a `(queue_family, priorities)` // each queue family must only have one entry in `queues` let mut queues: Vec<(u32, Vec)> = Vec::with_capacity(physical_device.queue_families().len()); // this variable will contain the queue family ID and queue ID of each requested queue let mut output_queues: SmallVec<[(u32, u32); 8]> = SmallVec::new(); for (queue_family, priority) in queue_families { // checking the parameters assert_eq!( queue_family.physical_device().internal_object(), physical_device.internal_object() ); if priority < 0.0 || priority > 1.0 { return Err(DeviceCreationError::PriorityOutOfRange); } // adding to `queues` and `output_queues` if let Some(q) = queues.iter_mut().find(|q| q.0 == queue_family.id()) { output_queues.push((queue_family.id(), q.1.len() as u32)); q.1.push(priority); if q.1.len() > queue_family.queues_count() { return Err(DeviceCreationError::TooManyQueuesForFamily); } continue; } queues.push((queue_family.id(), vec![priority])); output_queues.push((queue_family.id(), 0)); } // turning `queues` into an array of `vkDeviceQueueCreateInfo` suitable for Vulkan let queues = queues .iter() .map( |&(queue_id, ref priorities)| ash::vk::DeviceQueueCreateInfo { flags: ash::vk::DeviceQueueCreateFlags::empty(), queue_family_index: queue_id, queue_count: priorities.len() as u32, p_queue_priorities: priorities.as_ptr(), ..Default::default() }, ) .collect::>(); let mut features_ffi = FeaturesFfi::default(); features_ffi.make_chain( api_version, requested_extensions, instance.enabled_extensions(), ); features_ffi.write(&requested_features); // Device layers were deprecated in Vulkan 1.0.13, and device layer requests should be // ignored by the driver. For backwards compatibility, the spec recommends passing the // exact instance layers to the device as well. There's no need to support separate // requests at device creation time for legacy drivers: the spec claims that "[at] the // time of deprecation there were no known device-only layers." // // Because there's no way to query the list of layers enabled for an instance, we need // to save it alongside the instance. (`vkEnumerateDeviceLayerProperties` should get // the right list post-1.0.13, but not pre-1.0.13, so we can't use it here.) let layers_ptrs = instance .enabled_layers() .map(|layer| layer.as_ptr()) .collect::>(); let extensions_strings: Vec = requested_extensions.into(); let extensions_ptrs = extensions_strings .iter() .map(|extension| extension.as_ptr()) .collect::>(); let has_khr_get_physical_device_properties2 = instance .enabled_extensions() .khr_get_physical_device_properties2; let infos = ash::vk::DeviceCreateInfo { p_next: if has_khr_get_physical_device_properties2 { features_ffi.head_as_ref() as *const _ as _ } else { ptr::null() }, flags: ash::vk::DeviceCreateFlags::empty(), queue_create_info_count: queues.len() as u32, p_queue_create_infos: queues.as_ptr(), 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(), p_enabled_features: if has_khr_get_physical_device_properties2 { ptr::null() } else { &features_ffi.head_as_ref().features }, ..Default::default() }; let mut output = MaybeUninit::uninit(); check_errors(fns_i.v1_0.create_device( physical_device.internal_object(), &infos, ptr::null(), output.as_mut_ptr(), ))?; (output.assume_init(), output_queues) }; // loading the function pointers of the newly-created device let fns = DeviceFunctions::load(|name| unsafe { mem::transmute(fns_i.v1_0.get_device_proc_addr(device, name.as_ptr())) }); let mut active_queue_families: SmallVec<[u32; 8]> = SmallVec::new(); for (queue_family, _) in queues.iter() { if let None = active_queue_families .iter() .find(|&&qf| qf == *queue_family) { active_queue_families.push(*queue_family); } } let device = Arc::new(Device { instance: physical_device.instance().clone(), physical_device: physical_device.index(), device: device, api_version, fns, standard_pool: Mutex::new(Weak::new()), standard_descriptor_pool: Mutex::new(Weak::new()), standard_command_pools: Mutex::new(Default::default()), features: Features { // Always enabled ; see above robust_buffer_access: true, ..requested_features.clone() }, extensions: requested_extensions.clone(), 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()), }); // Iterator for the produced queues. let queues = QueuesIter { next_queue: 0, device: device.clone(), families_and_ids: queues, }; Ok((device, queues)) } /// Returns the Vulkan version supported by the device. /// /// This is the lower of the /// [physical device's supported version](crate::instance::PhysicalDevice::api_version) and /// the instance's [`max_api_version`](crate::instance::Instance::max_api_version). #[inline] pub fn api_version(&self) -> Version { self.api_version } /// Grants access to the Vulkan functions of the device. #[inline] pub fn fns(&self) -> &DeviceFunctions { &self.fns } /// Waits until all work on this device has finished. You should never need to call /// this function, but it can be useful for debugging or benchmarking purposes. /// /// > **Note**: This is the Vulkan equivalent of OpenGL's `glFinish`. /// /// # Safety /// /// This function is not thread-safe. You must not submit anything to any of the queue /// of the device (either explicitly or implicitly, for example with a future's destructor) /// while this function is waiting. /// pub unsafe fn wait(&self) -> Result<(), OomError> { check_errors(self.fns.v1_0.device_wait_idle(self.device))?; Ok(()) } /// Returns the instance used to create this device. #[inline] pub fn instance(&self) -> &Arc { &self.instance } /// Returns the physical device that was used to create this device. #[inline] pub fn physical_device(&self) -> PhysicalDevice { PhysicalDevice::from_index(&self.instance, self.physical_device).unwrap() } /// Returns an iterator to the list of queues families that this device uses. /// /// > **Note**: Will return `-> impl ExactSizeIterator` in the future. // TODO: ^ #[inline] pub fn active_queue_families<'a>( &'a self, ) -> Box> + 'a> { let physical_device = self.physical_device(); Box::new( self.active_queue_families .iter() .map(move |&id| physical_device.queue_family_by_id(id).unwrap()), ) } /// Returns the features that have been enabled on the device. #[inline] pub fn enabled_features(&self) -> &Features { &self.features } /// Returns the extensions that have been enabled on the device. #[inline] pub fn enabled_extensions(&self) -> &DeviceExtensions { &self.extensions } /// Returns the standard memory pool used by default if you don't provide any other pool. pub fn standard_pool(me: &Arc) -> Arc { let mut pool = me.standard_pool.lock().unwrap(); if let Some(p) = pool.upgrade() { return p; } // The weak pointer is empty, so we create the pool. let new_pool = StdMemoryPool::new(me.clone()); *pool = Arc::downgrade(&new_pool); new_pool } /// Returns the standard descriptor pool used by default if you don't provide any other pool. pub fn standard_descriptor_pool(me: &Arc) -> Arc { let mut pool = me.standard_descriptor_pool.lock().unwrap(); if let Some(p) = pool.upgrade() { return p; } // The weak pointer is empty, so we create the pool. let new_pool = Arc::new(StdDescriptorPool::new(me.clone())); *pool = Arc::downgrade(&new_pool); new_pool } /// Returns the standard command buffer pool used by default if you don't provide any other /// pool. /// /// # Panic /// /// - Panics if the device and the queue family don't belong to the same physical device. /// pub fn standard_command_pool(me: &Arc, queue: QueueFamily) -> Arc { 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 } } } /// Used to track the number of allocations on this device. /// /// To ensure valid usage of the Vulkan API, we cannot call `vkAllocateMemory` when /// `maxMemoryAllocationCount` has been exceeded. See the Vulkan specs: /// https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#vkAllocateMemory /// /// Warning: You should never modify this value, except in `device_memory` module pub(crate) fn allocation_count(&self) -> &Mutex { &self.allocation_count } pub(crate) fn fence_pool(&self) -> &Mutex> { &self.fence_pool } pub(crate) fn semaphore_pool(&self) -> &Mutex> { &self.semaphore_pool } pub(crate) fn event_pool(&self) -> &Mutex> { &self.event_pool } /// Assigns a human-readable name to `object` for debugging purposes. /// /// # Panics /// * If `object` is not owned by this device. pub fn set_object_name( &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) } } /// Assigns a human-readable name to `object` for debugging purposes. /// /// # Safety /// `object` must be a Vulkan handle owned by this device, and its type must be accurately described by `ty`. 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.device, &info), )?; Ok(()) } /// Checks the given combination of image attributes/configuration for compatibility with the physical device. /// /// Returns a struct with additional capabilities available for this image configuration. pub fn image_format_properties( &self, format: Format, ty: ImageType, tiling: ImageTiling, usage: ImageUsage, create_flags: ImageCreateFlags, ) -> Result { let fns_i = self.instance().fns(); let mut output = MaybeUninit::uninit(); let physical_device = self.physical_device().internal_object(); unsafe { let r = fns_i.v1_0.get_physical_device_image_format_properties( physical_device, format.into(), ty.into(), tiling.into(), usage.into(), create_flags.into(), output.as_mut_ptr(), ); match check_errors(r) { Ok(_) => Ok(output.assume_init().into()), Err(e) => { return Err(String::from(format!( "Image properties not supported. {:#?}", e ))) } } } } } impl fmt::Debug for Device { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, "", self.device) } } unsafe impl VulkanObject for Device { type Object = ash::vk::Device; #[inline] fn internal_object(&self) -> ash::vk::Device { self.device } } 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.device, raw_fence, ptr::null()); } for &raw_sem in self.semaphore_pool.lock().unwrap().iter() { self.fns .v1_0 .destroy_semaphore(self.device, raw_sem, ptr::null()); } for &raw_event in self.event_pool.lock().unwrap().iter() { self.fns .v1_0 .destroy_event(self.device, raw_event, ptr::null()); } self.fns.v1_0.destroy_device(self.device, ptr::null()); } } } impl PartialEq for Device { #[inline] fn eq(&self, other: &Self) -> bool { self.device == other.device && self.instance == other.instance } } impl Eq for Device {} impl Hash for Device { #[inline] fn hash(&self, state: &mut H) { self.device.hash(state); self.instance.hash(state); } } /// Implemented on objects that belong to a Vulkan device. /// /// # Safety /// /// - `device()` must return the correct device. /// pub unsafe trait DeviceOwned { /// Returns the device that owns `Self`. fn device(&self) -> &Arc; } unsafe impl DeviceOwned for T where T: Deref, T::Target: DeviceOwned, { #[inline] fn device(&self) -> &Arc { (**self).device() } } /// Iterator that returns the queues produced when creating a device. pub struct QueuesIter { next_queue: usize, device: Arc, families_and_ids: SmallVec<[(u32, u32); 8]>, } unsafe impl DeviceOwned for QueuesIter { fn device(&self) -> &Arc { &self.device } } impl Iterator for QueuesIter { type Item = Arc; fn next(&mut self) -> Option> { unsafe { let &(family, id) = match self.families_and_ids.get(self.next_queue) { Some(a) => a, None => return None, }; self.next_queue += 1; let mut output = MaybeUninit::uninit(); self.device.fns.v1_0.get_device_queue( self.device.device, family, id, output.as_mut_ptr(), ); Some(Arc::new(Queue { queue: Mutex::new(output.assume_init()), device: self.device.clone(), family: family, id: id, })) } } #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.families_and_ids.len().saturating_sub(self.next_queue); (len, Some(len)) } } impl ExactSizeIterator for QueuesIter {} /// Error that can be returned when creating a device. #[derive(Copy, Clone, Debug)] pub enum DeviceCreationError { /// Failed to create the device for an implementation-specific reason. InitializationFailed, /// You have reached the limit to the number of devices that can be created from the same /// physical device. TooManyObjects, /// Failed to connect to the device. DeviceLost, /// Some of the requested features are unsupported by the physical device. FeatureNotPresent, /// Some of the requested device extensions are not supported by the physical device. ExtensionNotPresent, /// Tried to create too many queues for a given family. TooManyQueuesForFamily, /// The priority of one of the queues is out of the [0.0; 1.0] range. PriorityOutOfRange, /// There is no memory available on the host (ie. the CPU, RAM, etc.). OutOfHostMemory, /// There is no memory available on the device (ie. video memory). OutOfDeviceMemory, /// A restriction for an extension was not met. ExtensionRestrictionNotMet(ExtensionRestrictionError), /// A restriction for a feature was not met. 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 { DeviceCreationError::InitializationFailed => { write!( fmt, "failed to create the device for an implementation-specific reason" ) } DeviceCreationError::OutOfHostMemory => write!(fmt, "no memory available on the host"), DeviceCreationError::OutOfDeviceMemory => { write!(fmt, "no memory available on the graphical device") } DeviceCreationError::DeviceLost => write!(fmt, "failed to connect to the device"), DeviceCreationError::TooManyQueuesForFamily => { write!(fmt, "tried to create too many queues for a given family") } DeviceCreationError::FeatureNotPresent => { write!( fmt, "some of the requested features are unsupported by the physical device" ) } DeviceCreationError::PriorityOutOfRange => { write!( fmt, "the priority of one of the queues is out of the [0.0; 1.0] range" ) } DeviceCreationError::ExtensionNotPresent => { write!(fmt,"some of the requested device extensions are not supported by the physical device") } DeviceCreationError::TooManyObjects => { write!(fmt,"you have reached the limit to the number of devices that can be created from the same physical device") } DeviceCreationError::ExtensionRestrictionNotMet(err) => err.fmt(fmt), DeviceCreationError::FeatureRestrictionNotMet(err) => err.fmt(fmt), } } } impl From for DeviceCreationError { #[inline] fn from(err: Error) -> DeviceCreationError { match err { Error::InitializationFailed => DeviceCreationError::InitializationFailed, Error::OutOfHostMemory => DeviceCreationError::OutOfHostMemory, Error::OutOfDeviceMemory => DeviceCreationError::OutOfDeviceMemory, Error::DeviceLost => DeviceCreationError::DeviceLost, Error::ExtensionNotPresent => DeviceCreationError::ExtensionNotPresent, Error::FeatureNotPresent => DeviceCreationError::FeatureNotPresent, Error::TooManyObjects => DeviceCreationError::TooManyObjects, _ => panic!("Unexpected error value: {}", err as i32), } } } impl From for DeviceCreationError { #[inline] fn from(err: ExtensionRestrictionError) -> Self { Self::ExtensionRestrictionNotMet(err) } } impl From for DeviceCreationError { #[inline] fn from(err: FeatureRestrictionError) -> Self { Self::FeatureRestrictionNotMet(err) } } /// Represents a queue where commands can be submitted. // TODO: should use internal synchronization? #[derive(Debug)] pub struct Queue { queue: Mutex, device: Arc, family: u32, id: u32, // id within family } impl Queue { /// Returns the device this queue belongs to. #[inline] pub fn device(&self) -> &Arc { &self.device } /// Returns true if this is the same queue as another one. #[inline] pub fn is_same(&self, other: &Queue) -> bool { self.id == other.id && self.family == other.family && self.device.internal_object() == other.device.internal_object() } /// Returns the family this queue belongs to. #[inline] pub fn family(&self) -> QueueFamily { self.device .physical_device() .queue_family_by_id(self.family) .unwrap() } /// Returns the index of this queue within its family. #[inline] pub fn id_within_family(&self) -> u32 { self.id } /// Waits until all work on this queue has finished. /// /// Just like `Device::wait()`, you shouldn't have to call this function in a typical program. #[inline] pub fn wait(&self) -> Result<(), OomError> { unsafe { let fns = self.device.fns(); let queue = self.queue.lock().unwrap(); check_errors(fns.v1_0.queue_wait_idle(*queue))?; Ok(()) } } } impl PartialEq for Queue { fn eq(&self, other: &Self) -> bool { self.is_same(other) } } impl Eq for Queue {} unsafe impl DeviceOwned for Queue { fn device(&self) -> &Arc { &self.device } } unsafe impl SynchronizedVulkanObject for Queue { type Object = ash::vk::Queue; #[inline] fn internal_object_guard(&self) -> MutexGuard { self.queue.lock().unwrap() } } #[cfg(test)] mod tests { use crate::device::physical::PhysicalDevice; use crate::device::Device; use crate::device::DeviceCreationError; use crate::device::DeviceExtensions; 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, &Features::none(), &DeviceExtensions::none(), queues, ) { Err(DeviceCreationError::TooManyQueuesForFamily) => return, // Success _ => 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(); // In the unlikely situation where the device supports everything, we ignore the test. if physical.supported_features().is_superset_of(&features) { return; } match Device::new( physical, &features, &DeviceExtensions::none(), Some((family, 1.0)), ) { Err(DeviceCreationError::FeatureRestrictionNotMet(FeatureRestrictionError { restriction: FeatureRestriction::NotSupported, .. })) => return, // Success _ => 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(); match Device::new( physical, &Features::none(), &DeviceExtensions::none(), Some((family, 1.4)), ) { Err(DeviceCreationError::PriorityOutOfRange) => (), // Success _ => panic!(), }; match Device::new( physical, &Features::none(), &DeviceExtensions::none(), Some((family, -0.2)), ) { Err(DeviceCreationError::PriorityOutOfRange) => (), // Success _ => panic!(), }; } }