| // Copyright (c) 2016 The vulkano developers |
| // Licensed under the Apache License, Version 2.0 |
| // <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT |
| // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, |
| // 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<Instance>, |
| 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<Weak<StdMemoryPool>>, |
| standard_descriptor_pool: Mutex<Weak<StdDescriptorPool>>, |
| standard_command_pools: |
| Mutex<HashMap<u32, Weak<StandardCommandPool>, BuildHasherDefault<FnvHasher>>>, |
| features: Features, |
| extensions: DeviceExtensions, |
| active_queue_families: SmallVec<[u32; 8]>, |
| 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>>, |
| } |
| |
| // 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<Queue> 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<Device>, QueuesIter), DeviceCreationError> |
| where |
| I: IntoIterator<Item = (QueueFamily<'a>, 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<f32>)> = |
| 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::<SmallVec<[_; 16]>>(); |
| |
| 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::<SmallVec<[_; 16]>>(); |
| |
| let extensions_strings: Vec<CString> = requested_extensions.into(); |
| let extensions_ptrs = extensions_strings |
| .iter() |
| .map(|extension| extension.as_ptr()) |
| .collect::<SmallVec<[_; 16]>>(); |
| |
| 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<Instance> { |
| &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<Item = QueueFamily>` in the future. |
| // TODO: ^ |
| #[inline] |
| pub fn active_queue_families<'a>( |
| &'a self, |
| ) -> Box<dyn ExactSizeIterator<Item = QueueFamily<'a>> + '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<Self>) -> Arc<StdMemoryPool> { |
| 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<Self>) -> Arc<StdDescriptorPool> { |
| 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<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 |
| } |
| } |
| } |
| |
| /// 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<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 |
| } |
| |
| /// Assigns a human-readable name to `object` for debugging purposes. |
| /// |
| /// # Panics |
| /// * If `object` is not owned by this device. |
| 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) |
| } |
| } |
| |
| /// 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<ImageFormatProperties, String> { |
| 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, "<Vulkan device {:?}>", 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<H: Hasher>(&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<Device>; |
| } |
| |
| unsafe impl<T> DeviceOwned for T |
| where |
| T: Deref, |
| T::Target: DeviceOwned, |
| { |
| #[inline] |
| fn device(&self) -> &Arc<Device> { |
| (**self).device() |
| } |
| } |
| |
| /// Iterator that returns the queues produced when creating a device. |
| pub struct QueuesIter { |
| next_queue: usize, |
| device: Arc<Device>, |
| families_and_ids: SmallVec<[(u32, u32); 8]>, |
| } |
| |
| unsafe impl DeviceOwned for QueuesIter { |
| fn device(&self) -> &Arc<Device> { |
| &self.device |
| } |
| } |
| |
| impl Iterator for QueuesIter { |
| type Item = Arc<Queue>; |
| |
| fn next(&mut self) -> Option<Arc<Queue>> { |
| 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<usize>) { |
| 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<Error> 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<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) |
| } |
| } |
| |
| /// Represents a queue where commands can be submitted. |
| // TODO: should use internal synchronization? |
| #[derive(Debug)] |
| pub struct Queue { |
| queue: Mutex<ash::vk::Queue>, |
| device: Arc<Device>, |
| family: u32, |
| id: u32, // id within family |
| } |
| |
| impl Queue { |
| /// Returns the device this queue belongs to. |
| #[inline] |
| pub fn device(&self) -> &Arc<Device> { |
| &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<Device> { |
| &self.device |
| } |
| } |
| |
| unsafe impl SynchronizedVulkanObject for Queue { |
| type Object = ash::vk::Queue; |
| |
| #[inline] |
| fn internal_object_guard(&self) -> MutexGuard<ash::vk::Queue> { |
| 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!(), |
| }; |
| } |
| } |