| // 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::{physical::PhysicalDevice, Device, DeviceCreateInfo, DeviceExtensions, Features, QueueCreateInfo}, |
| //! instance::{Instance, InstanceExtensions}, |
| //! Version, VulkanLibrary, |
| //! }; |
| //! |
| //! // Creating the instance. See the documentation of the `instance` module. |
| //! let library = VulkanLibrary::new() |
| //! .unwrap_or_else(|err| panic!("Couldn't load Vulkan library: {:?}", err)); |
| //! let instance = Instance::new(library, Default::default()) |
| //! .unwrap_or_else(|err| panic!("Couldn't create 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 = instance |
| //! .enumerate_physical_devices() |
| //! .unwrap_or_else(|err| panic!("Couldn't enumerate physical devices: {:?}", err)) |
| //! .next().expect("No physical device"); |
| //! |
| //! // Here is the device-creating code. |
| //! let device = { |
| //! let features = Features::empty(); |
| //! let extensions = DeviceExtensions::empty(); |
| //! |
| //! match Device::new( |
| //! physical_device, |
| //! DeviceCreateInfo { |
| //! enabled_extensions: extensions, |
| //! enabled_features: features, |
| //! queue_create_infos: vec![QueueCreateInfo { |
| //! queue_family_index: 0, |
| //! ..Default::default() |
| //! }], |
| //! ..Default::default() |
| //! }, |
| //! ) { |
| //! 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 |
| |
| 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, macros::impl_id_counter, 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; |
| |
| /// Represents a Vulkan context. |
| #[derive(Debug)] |
| pub struct Device { |
| handle: ash::vk::Device, |
| physical_device: Arc<PhysicalDevice>, |
| id: NonZeroU64, |
| |
| // 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, |
| enabled_extensions: DeviceExtensions, |
| enabled_features: Features, |
| active_queue_family_indices: SmallVec<[u32; 2]>, |
| // This is required for validation in `memory::device_memory`, the count must only be modified |
| // in that module. |
| 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 { |
| /// Creates a new `Device`. |
| /// |
| /// # Panics |
| /// |
| /// - Panics if `create_info.queues` is empty. |
| /// - Panics if one of the queue families in `create_info.queues` doesn't belong to the given |
| /// physical device. |
| /// - Panics if `create_info.queues` contains multiple elements for the same queue family. |
| /// - Panics if `create_info.queues` contains an element where `queues` is empty. |
| /// - Panics if `create_info.queues` contains an element where `queues` contains a value that is |
| /// not between 0.0 and 1.0 inclusive. |
| 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(); |
| |
| /* |
| Queues |
| */ |
| |
| struct QueueToGet { |
| queue_family_index: u32, |
| id: u32, |
| } |
| |
| // VUID-VkDeviceCreateInfo-queueCreateInfoCount-arraylength |
| 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; |
| |
| // VUID-VkDeviceQueueCreateInfo-queueFamilyIndex-00381 |
| // TODO: return error instead of panicking? |
| let queue_family_properties = |
| &physical_device.queue_family_properties()[queue_family_index as usize]; |
| |
| // VUID-VkDeviceCreateInfo-queueFamilyIndex-02802 |
| assert!( |
| queue_create_infos |
| .iter() |
| .filter(|qc2| qc2.queue_family_index == queue_family_index) |
| .count() |
| == 1 |
| ); |
| |
| // VUID-VkDeviceQueueCreateInfo-queueCount-arraylength |
| assert!(!queues.is_empty()); |
| |
| // VUID-VkDeviceQueueCreateInfo-pQueuePriorities-00383 |
| 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(), // borrows from queue_create |
| ..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; |
| } |
| |
| /* |
| Extensions |
| */ |
| |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-01840 |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-03328 |
| // VUID-VkDeviceCreateInfo-pProperties-04451 |
| 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]>>(); |
| |
| /* |
| Features |
| */ |
| |
| // 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. |
| enabled_features.robust_buffer_access = true; |
| |
| // VUID-VkDeviceCreateInfo-pNext-04748 |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-04476 |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-02831 |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-02832 |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-02833 |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-02834 |
| // VUID-VkDeviceCreateInfo-ppEnabledExtensionNames-02835 |
| // VUID-VkDeviceCreateInfo-shadingRateImage-04478 |
| // VUID-VkDeviceCreateInfo-shadingRateImage-04479 |
| // VUID-VkDeviceCreateInfo-shadingRateImage-04480 |
| // VUID-VkDeviceCreateInfo-fragmentDensityMap-04481 |
| // VUID-VkDeviceCreateInfo-fragmentDensityMap-04482 |
| // VUID-VkDeviceCreateInfo-fragmentDensityMap-04483 |
| // VUID-VkDeviceCreateInfo-None-04896 |
| // VUID-VkDeviceCreateInfo-None-04897 |
| // VUID-VkDeviceCreateInfo-None-04898 |
| // VUID-VkDeviceCreateInfo-sparseImageFloat32AtomicMinMax-04975 |
| enabled_features.check_requirements( |
| physical_device.supported_features(), |
| api_version, |
| &enabled_extensions, |
| )?; |
| |
| // VUID-VkDeviceCreateInfo-pNext-02829 |
| // VUID-VkDeviceCreateInfo-pNext-02830 |
| // VUID-VkDeviceCreateInfo-pNext-06532 |
| let mut features_ffi = FeaturesFfi::default(); |
| features_ffi.make_chain( |
| api_version, |
| &enabled_extensions, |
| instance.enabled_extensions(), |
| ); |
| features_ffi.write(&enabled_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 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]>>(); |
| |
| /* |
| Create the device |
| */ |
| |
| 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() |
| }; |
| |
| // VUID-VkDeviceCreateInfo-pNext-00373 |
| 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() |
| }; |
| |
| // loading the function pointers of the newly-created device |
| 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()), |
| }); |
| |
| // Iterator to return the queues |
| 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)) |
| } |
| |
| /// Returns the Vulkan version supported by the device. |
| /// |
| /// This is the lower of the |
| /// [physical device's supported version](crate::device::physical::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 |
| } |
| |
| /// Returns pointers to the raw Vulkan functions of the device. |
| #[inline] |
| pub fn fns(&self) -> &DeviceFunctions { |
| &self.fns |
| } |
| |
| /// Returns the physical device that was used to create this device. |
| #[inline] |
| pub fn physical_device(&self) -> &Arc<PhysicalDevice> { |
| &self.physical_device |
| } |
| |
| /// Returns the instance used to create this device. |
| #[inline] |
| pub fn instance(&self) -> &Arc<Instance> { |
| self.physical_device.instance() |
| } |
| |
| /// Returns the queue family indices that this device uses. |
| #[inline] |
| pub fn active_queue_family_indices(&self) -> &[u32] { |
| &self.active_queue_family_indices |
| } |
| |
| /// Returns the extensions that have been enabled on the device. |
| #[inline] |
| pub fn enabled_extensions(&self) -> &DeviceExtensions { |
| &self.enabled_extensions |
| } |
| |
| /// Returns the features that have been enabled on the device. |
| #[inline] |
| pub fn enabled_features(&self) -> &Features { |
| &self.enabled_features |
| } |
| |
| /// Returns the current number of active [`DeviceMemory`] allocations the device has. |
| /// |
| /// [`DeviceMemory`]: crate::memory::DeviceMemory |
| #[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 |
| } |
| |
| /// Retrieves the properties of an external file descriptor when imported as a given external |
| /// handle type. |
| /// |
| /// An error will be returned if the |
| /// [`khr_external_memory_fd`](DeviceExtensions::khr_external_memory_fd) extension was not |
| /// enabled on the device, or if `handle_type` is [`ExternalMemoryHandleType::OpaqueFd`]. |
| /// |
| /// # Safety |
| /// |
| /// - `file` must be a handle to external memory that was created outside the Vulkan API. |
| #[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; |
| |
| // VUID-vkGetMemoryFdPropertiesKHR-handleType-parameter |
| handle_type.validate_device(self)?; |
| |
| // VUID-vkGetMemoryFdPropertiesKHR-handleType-00674 |
| 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, |
| }) |
| } |
| } |
| |
| /// Assigns a human-readable name to `object` for debugging purposes. |
| /// |
| /// If `object_name` is `None`, a previously set object name is removed. |
| /// |
| /// # Panics |
| /// - If `object` is not owned by this device. |
| 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(()) |
| } |
| |
| /// 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. |
| #[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 |
| } |
| } |
| |
| impl_id_counter!(Device); |
| |
| /// 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 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) |
| } |
| } |
| |
| /// Parameters to create a new `Device`. |
| #[derive(Clone, Debug)] |
| pub struct DeviceCreateInfo { |
| /// The extensions to enable on the device. |
| /// |
| /// If the [`khr_portability_subset`](DeviceExtensions::khr_portability_subset) extension is |
| /// available, it will be enabled automatically, so you do not have to do this yourself. |
| /// You are responsible for ensuring that your program can work correctly on such devices. |
| /// See [the documentation of the `instance` module](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag) |
| /// for more information. |
| /// |
| /// The default value is [`DeviceExtensions::empty()`]. |
| pub enabled_extensions: DeviceExtensions, |
| |
| /// The features to enable on the device. |
| /// |
| /// The default value is [`Features::empty()`]. |
| pub enabled_features: Features, |
| |
| /// The queues to create for the device. |
| /// |
| /// The default value is empty, which must be overridden. |
| 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(()), |
| } |
| } |
| } |
| |
| /// Parameters to create queues in a new `Device`. |
| #[derive(Clone, Debug)] |
| pub struct QueueCreateInfo { |
| /// The index of the queue family to create queues for. |
| /// |
| /// The default value is `0`. |
| pub queue_family_index: u32, |
| |
| /// The queues to create for the given queue family, each with a relative priority. |
| /// |
| /// The relative priority value is an arbitrary number between 0.0 and 1.0. Giving a queue a |
| /// higher priority is a hint to the driver that the queue should be given more processing time. |
| /// As this is only a hint, different drivers may handle this value differently and there are no |
| /// guarantees about its behavior. |
| /// |
| /// The default value is a single queue with a priority of 0.5. |
| 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(()), |
| } |
| } |
| } |
| |
| /// 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, |
| { |
| fn device(&self) -> &Arc<Device> { |
| (**self).device() |
| } |
| } |
| |
| /// The properties of a Unix file descriptor when it is imported. |
| #[derive(Clone, Debug)] |
| #[non_exhaustive] |
| pub struct MemoryFdProperties { |
| /// A bitmask of the indices of memory types that can be used with the file. |
| pub memory_type_bits: u32, |
| } |
| |
| /// Error that can happen when calling `memory_fd_properties`. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| pub enum MemoryFdPropertiesError { |
| /// No memory available on the host. |
| OutOfHostMemory, |
| |
| RequirementNotMet { |
| required_for: &'static str, |
| requires_one_of: RequiresOneOf, |
| }, |
| |
| /// The provided external handle was not valid. |
| InvalidExternalHandle, |
| |
| /// The provided external handle type was not valid. |
| InvalidExternalHandleType, |
| |
| /// The `khr_external_memory_fd` extension was not enabled on the device. |
| 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) => (), // Success |
| _ => 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(); |
| // In the unlikely situation where the device supports everything, we ignore the test. |
| 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, |
| .. |
| })) => (), // Success |
| _ => 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() |
| }, |
| ) |
| }); |
| } |
| } |