blob: af5354d3c80f419b76e209fdc05bd18238fb903c [file] [log] [blame]
// 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()
},
)
});
}
}