aboutsummaryrefslogtreecommitdiff
path: root/src/device/physical.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/device/physical.rs')
-rw-r--r--src/device/physical.rs936
1 files changed, 936 insertions, 0 deletions
diff --git a/src/device/physical.rs b/src/device/physical.rs
new file mode 100644
index 0000000..feaea4b
--- /dev/null
+++ b/src/device/physical.rs
@@ -0,0 +1,936 @@
+// Copyright (c) 2021 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.
+
+use crate::check_errors;
+use crate::device::{DeviceExtensions, Features, FeaturesFfi, Properties, PropertiesFfi};
+use crate::instance::{Instance, InstanceCreationError};
+use crate::sync::PipelineStage;
+use crate::DeviceSize;
+use crate::Version;
+use crate::VulkanObject;
+use std::convert::TryFrom;
+use std::ffi::CStr;
+use std::fmt;
+use std::hash::Hash;
+use std::mem::MaybeUninit;
+use std::ptr;
+use std::sync::Arc;
+
+#[derive(Clone, Debug)]
+pub(crate) struct PhysicalDeviceInfo {
+ handle: ash::vk::PhysicalDevice,
+ api_version: Version,
+ supported_extensions: DeviceExtensions,
+ required_extensions: DeviceExtensions,
+ supported_features: Features,
+ properties: Properties,
+ memory_properties: ash::vk::PhysicalDeviceMemoryProperties,
+ queue_families: Vec<ash::vk::QueueFamilyProperties>,
+}
+
+pub(crate) fn init_physical_devices(
+ instance: &Instance,
+) -> Result<Vec<PhysicalDeviceInfo>, InstanceCreationError> {
+ let fns = instance.fns();
+ let instance_extensions = instance.enabled_extensions();
+
+ let handles: Vec<ash::vk::PhysicalDevice> = unsafe {
+ let mut num = 0;
+ check_errors(fns.v1_0.enumerate_physical_devices(
+ instance.internal_object(),
+ &mut num,
+ ptr::null_mut(),
+ ))?;
+
+ let mut handles = Vec::with_capacity(num as usize);
+ check_errors(fns.v1_0.enumerate_physical_devices(
+ instance.internal_object(),
+ &mut num,
+ handles.as_mut_ptr(),
+ ))?;
+ handles.set_len(num as usize);
+ handles
+ };
+
+ let mut infos: Vec<PhysicalDeviceInfo> = handles
+ .into_iter()
+ .enumerate()
+ .map(|(index, handle)| -> Result<_, InstanceCreationError> {
+ let api_version = unsafe {
+ let mut output = MaybeUninit::uninit();
+ fns.v1_0
+ .get_physical_device_properties(handle, output.as_mut_ptr());
+ let api_version = Version::try_from(output.assume_init().api_version).unwrap();
+ std::cmp::min(instance.max_api_version(), api_version)
+ };
+
+ let extension_properties: Vec<ash::vk::ExtensionProperties> = unsafe {
+ let mut num = 0;
+ check_errors(fns.v1_0.enumerate_device_extension_properties(
+ handle,
+ ptr::null(),
+ &mut num,
+ ptr::null_mut(),
+ ))?;
+
+ let mut properties = Vec::with_capacity(num as usize);
+ check_errors(fns.v1_0.enumerate_device_extension_properties(
+ handle,
+ ptr::null(),
+ &mut num,
+ properties.as_mut_ptr(),
+ ))?;
+ properties.set_len(num as usize);
+ properties
+ };
+
+ let supported_extensions = DeviceExtensions::from(
+ extension_properties
+ .iter()
+ .map(|property| unsafe { CStr::from_ptr(property.extension_name.as_ptr()) }),
+ );
+
+ let required_extensions = supported_extensions
+ .intersection(&DeviceExtensions::required_if_supported_extensions());
+
+ Ok(PhysicalDeviceInfo {
+ handle,
+ api_version,
+ supported_extensions,
+ required_extensions,
+ supported_features: Default::default(),
+ properties: Default::default(),
+ memory_properties: Default::default(),
+ queue_families: Default::default(),
+ })
+ })
+ .collect::<Result<_, _>>()?;
+
+ // Getting the remaining infos.
+ // If possible, we use VK_KHR_get_physical_device_properties2.
+ if instance.api_version() >= Version::V1_1
+ || instance_extensions.khr_get_physical_device_properties2
+ {
+ init_physical_devices_inner2(instance, &mut infos)
+ } else {
+ init_physical_devices_inner(instance, &mut infos)
+ };
+
+ Ok(infos)
+}
+
+/// Initialize all physical devices
+fn init_physical_devices_inner(instance: &Instance, infos: &mut [PhysicalDeviceInfo]) {
+ let fns = instance.fns();
+
+ for info in infos.into_iter() {
+ info.supported_features = unsafe {
+ let mut output = FeaturesFfi::default();
+ fns.v1_0
+ .get_physical_device_features(info.handle, &mut output.head_as_mut().features);
+ Features::from(&output)
+ };
+
+ info.properties = unsafe {
+ let mut output = PropertiesFfi::default();
+ output.make_chain(
+ info.api_version,
+ &info.supported_extensions,
+ instance.enabled_extensions(),
+ );
+ fns.v1_0
+ .get_physical_device_properties(info.handle, &mut output.head_as_mut().properties);
+ Properties::from(&output)
+ };
+
+ info.memory_properties = unsafe {
+ let mut output = MaybeUninit::uninit();
+ fns.v1_0
+ .get_physical_device_memory_properties(info.handle, output.as_mut_ptr());
+ output.assume_init()
+ };
+
+ info.queue_families = unsafe {
+ let mut num = 0;
+ fns.v1_0.get_physical_device_queue_family_properties(
+ info.handle,
+ &mut num,
+ ptr::null_mut(),
+ );
+
+ let mut families = Vec::with_capacity(num as usize);
+ fns.v1_0.get_physical_device_queue_family_properties(
+ info.handle,
+ &mut num,
+ families.as_mut_ptr(),
+ );
+ families.set_len(num as usize);
+ families
+ };
+ }
+}
+
+/// Initialize all physical devices, but use VK_KHR_get_physical_device_properties2
+/// TODO: Query extension-specific physical device properties, once a new instance extension is supported.
+fn init_physical_devices_inner2(instance: &Instance, infos: &mut [PhysicalDeviceInfo]) {
+ let fns = instance.fns();
+
+ for info in infos.into_iter() {
+ info.supported_features = unsafe {
+ let mut output = FeaturesFfi::default();
+ output.make_chain(
+ info.api_version,
+ &info.supported_extensions,
+ instance.enabled_extensions(),
+ );
+
+ if instance.api_version() >= Version::V1_1 {
+ fns.v1_1
+ .get_physical_device_features2(info.handle, output.head_as_mut());
+ } else {
+ fns.khr_get_physical_device_properties2
+ .get_physical_device_features2_khr(info.handle, output.head_as_mut());
+ }
+
+ Features::from(&output)
+ };
+
+ info.properties = unsafe {
+ let mut output = PropertiesFfi::default();
+ output.make_chain(
+ info.api_version,
+ &info.supported_extensions,
+ instance.enabled_extensions(),
+ );
+
+ if instance.api_version() >= Version::V1_1 {
+ fns.v1_1
+ .get_physical_device_properties2(info.handle, output.head_as_mut());
+ } else {
+ fns.khr_get_physical_device_properties2
+ .get_physical_device_properties2_khr(info.handle, output.head_as_mut());
+ }
+
+ Properties::from(&output)
+ };
+
+ info.memory_properties = unsafe {
+ let mut output = ash::vk::PhysicalDeviceMemoryProperties2KHR::default();
+
+ if instance.api_version() >= Version::V1_1 {
+ fns.v1_1
+ .get_physical_device_memory_properties2(info.handle, &mut output);
+ } else {
+ fns.khr_get_physical_device_properties2
+ .get_physical_device_memory_properties2_khr(info.handle, &mut output);
+ }
+
+ output.memory_properties
+ };
+
+ info.queue_families = unsafe {
+ let mut num = 0;
+
+ if instance.api_version() >= Version::V1_1 {
+ fns.v1_1.get_physical_device_queue_family_properties2(
+ info.handle,
+ &mut num,
+ ptr::null_mut(),
+ );
+ } else {
+ fns.khr_get_physical_device_properties2
+ .get_physical_device_queue_family_properties2_khr(
+ info.handle,
+ &mut num,
+ ptr::null_mut(),
+ );
+ }
+
+ let mut families = vec![ash::vk::QueueFamilyProperties2::default(); num as usize];
+
+ if instance.api_version() >= Version::V1_1 {
+ fns.v1_1.get_physical_device_queue_family_properties2(
+ info.handle,
+ &mut num,
+ families.as_mut_ptr(),
+ );
+ } else {
+ fns.khr_get_physical_device_properties2
+ .get_physical_device_queue_family_properties2_khr(
+ info.handle,
+ &mut num,
+ families.as_mut_ptr(),
+ );
+ }
+
+ families
+ .into_iter()
+ .map(|family| family.queue_family_properties)
+ .collect()
+ };
+ }
+}
+
+/// Represents one of the available devices on this machine.
+///
+/// This struct simply contains a pointer to an instance and a number representing the physical
+/// device. You are therefore encouraged to pass this around by value instead of by reference.
+///
+/// # Example
+///
+/// ```no_run
+/// # use vulkano::instance::Instance;
+/// # use vulkano::instance::InstanceExtensions;
+/// # use vulkano::Version;
+/// use vulkano::device::physical::PhysicalDevice;
+///
+/// # let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None).unwrap();
+/// for physical_device in PhysicalDevice::enumerate(&instance) {
+/// print_infos(physical_device);
+/// }
+///
+/// fn print_infos(dev: PhysicalDevice) {
+/// println!("Name: {}", dev.properties().device_name);
+/// }
+/// ```
+#[derive(Clone, Copy, Debug)]
+pub struct PhysicalDevice<'a> {
+ instance: &'a Arc<Instance>,
+ index: usize,
+ info: &'a PhysicalDeviceInfo,
+}
+
+impl<'a> PhysicalDevice<'a> {
+ /// Returns an iterator that enumerates the physical devices available.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// # use vulkano::instance::Instance;
+ /// # use vulkano::instance::InstanceExtensions;
+ /// # use vulkano::Version;
+ /// use vulkano::device::physical::PhysicalDevice;
+ ///
+ /// # let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None).unwrap();
+ /// for physical_device in PhysicalDevice::enumerate(&instance) {
+ /// println!("Available device: {}", physical_device.properties().device_name);
+ /// }
+ /// ```
+ #[inline]
+ pub fn enumerate(
+ instance: &'a Arc<Instance>,
+ ) -> impl ExactSizeIterator<Item = PhysicalDevice<'a>> {
+ instance
+ .physical_device_infos
+ .iter()
+ .enumerate()
+ .map(move |(index, info)| PhysicalDevice {
+ instance,
+ index,
+ info,
+ })
+ }
+
+ /// Returns a physical device from its index. Returns `None` if out of range.
+ ///
+ /// Indices range from 0 to the number of devices.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use vulkano::instance::Instance;
+ /// use vulkano::instance::InstanceExtensions;
+ /// use vulkano::device::physical::PhysicalDevice;
+ /// use vulkano::Version;
+ ///
+ /// let instance = Instance::new(None, Version::V1_1, &InstanceExtensions::none(), None).unwrap();
+ /// let first_physical_device = PhysicalDevice::from_index(&instance, 0).unwrap();
+ /// ```
+ #[inline]
+ pub fn from_index(instance: &'a Arc<Instance>, index: usize) -> Option<PhysicalDevice<'a>> {
+ instance
+ .physical_device_infos
+ .get(index)
+ .map(|info| PhysicalDevice {
+ instance,
+ index,
+ info,
+ })
+ }
+
+ /// Returns the instance corresponding to this physical device.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// use vulkano::device::physical::PhysicalDevice;
+ ///
+ /// fn do_something(physical_device: PhysicalDevice) {
+ /// let _loaded_extensions = physical_device.instance().enabled_extensions();
+ /// // ...
+ /// }
+ /// ```
+ #[inline]
+ pub fn instance(&self) -> &'a Arc<Instance> {
+ &self.instance
+ }
+
+ /// Returns the index of the physical device in the physical devices list.
+ ///
+ /// This index never changes and can be used later to retrieve a `PhysicalDevice` from an
+ /// instance and an index.
+ #[inline]
+ pub fn index(&self) -> usize {
+ self.index
+ }
+
+ /// Returns the version of Vulkan supported by this device.
+ ///
+ /// Unlike the `api_version` property, which is the version reported by the device directly,
+ /// this function returns the version the device can actually support, based on the instance's,
+ /// `max_api_version`.
+ #[inline]
+ pub fn api_version(&self) -> Version {
+ self.info.api_version
+ }
+
+ /// Returns the extensions that are supported by this physical device.
+ #[inline]
+ pub fn supported_extensions(&self) -> &'a DeviceExtensions {
+ &self.info.supported_extensions
+ }
+
+ /// Returns the extensions that must be enabled as a minimum when creating a `Device` from this
+ /// physical device.
+ pub fn required_extensions(&self) -> &'a DeviceExtensions {
+ &self.info.required_extensions
+ }
+
+ /// Returns the properties reported by the device.
+ #[inline]
+ pub fn properties(&self) -> &'a Properties {
+ &self.info.properties
+ }
+
+ /// Returns the features that are supported by this physical device.
+ #[inline]
+ pub fn supported_features(&self) -> &'a Features {
+ &self.info.supported_features
+ }
+
+ /// Builds an iterator that enumerates all the memory types on this physical device.
+ #[inline]
+ pub fn memory_types(&self) -> impl ExactSizeIterator<Item = MemoryType<'a>> {
+ let physical_device = *self;
+ self.info.memory_properties.memory_types
+ [0..self.info.memory_properties.memory_type_count as usize]
+ .iter()
+ .enumerate()
+ .map(move |(id, info)| MemoryType {
+ physical_device,
+ id: id as u32,
+ info,
+ })
+ }
+
+ /// Returns the memory type with the given index, or `None` if out of range.
+ #[inline]
+ pub fn memory_type_by_id(&self, id: u32) -> Option<MemoryType<'a>> {
+ if id < self.info.memory_properties.memory_type_count {
+ Some(MemoryType {
+ physical_device: *self,
+ id,
+ info: &self.info.memory_properties.memory_types[id as usize],
+ })
+ } else {
+ None
+ }
+ }
+
+ /// Builds an iterator that enumerates all the memory heaps on this physical device.
+ #[inline]
+ pub fn memory_heaps(&self) -> impl ExactSizeIterator<Item = MemoryHeap<'a>> {
+ let physical_device = *self;
+ self.info.memory_properties.memory_heaps
+ [0..self.info.memory_properties.memory_heap_count as usize]
+ .iter()
+ .enumerate()
+ .map(move |(id, info)| MemoryHeap {
+ physical_device,
+ id: id as u32,
+ info,
+ })
+ }
+
+ /// Returns the memory heap with the given index, or `None` if out of range.
+ #[inline]
+ pub fn memory_heap_by_id(&self, id: u32) -> Option<MemoryHeap<'a>> {
+ if id < self.info.memory_properties.memory_heap_count {
+ Some(MemoryHeap {
+ physical_device: *self,
+ id,
+ info: &self.info.memory_properties.memory_heaps[id as usize],
+ })
+ } else {
+ None
+ }
+ }
+
+ /// Builds an iterator that enumerates all the queue families on this physical device.
+ #[inline]
+ pub fn queue_families(&self) -> impl ExactSizeIterator<Item = QueueFamily<'a>> {
+ let physical_device = *self;
+ self.info
+ .queue_families
+ .iter()
+ .enumerate()
+ .map(move |(id, properties)| QueueFamily {
+ physical_device,
+ id: id as u32,
+ properties,
+ })
+ }
+
+ /// Returns the queue family with the given index, or `None` if out of range.
+ #[inline]
+ pub fn queue_family_by_id(&self, id: u32) -> Option<QueueFamily<'a>> {
+ if (id as usize) < self.info.queue_families.len() {
+ Some(QueueFamily {
+ physical_device: *self,
+ id,
+ properties: &self.info.queue_families[id as usize],
+ })
+ } else {
+ None
+ }
+ }
+}
+
+unsafe impl<'a> VulkanObject for PhysicalDevice<'a> {
+ type Object = ash::vk::PhysicalDevice;
+
+ #[inline]
+ fn internal_object(&self) -> ash::vk::PhysicalDevice {
+ self.info.handle
+ }
+}
+
+/// Type of a physical device.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
+#[repr(i32)]
+pub enum PhysicalDeviceType {
+ /// The device is an integrated GPU.
+ IntegratedGpu = ash::vk::PhysicalDeviceType::INTEGRATED_GPU.as_raw(),
+ /// The device is a discrete GPU.
+ DiscreteGpu = ash::vk::PhysicalDeviceType::DISCRETE_GPU.as_raw(),
+ /// The device is a virtual GPU.
+ VirtualGpu = ash::vk::PhysicalDeviceType::VIRTUAL_GPU.as_raw(),
+ /// The device is a CPU.
+ Cpu = ash::vk::PhysicalDeviceType::CPU.as_raw(),
+ /// The device is something else.
+ Other = ash::vk::PhysicalDeviceType::OTHER.as_raw(),
+}
+
+/// VkPhysicalDeviceType::Other is represented as 0
+impl Default for PhysicalDeviceType {
+ fn default() -> Self {
+ PhysicalDeviceType::Other
+ }
+}
+
+impl TryFrom<ash::vk::PhysicalDeviceType> for PhysicalDeviceType {
+ type Error = ();
+
+ #[inline]
+ fn try_from(val: ash::vk::PhysicalDeviceType) -> Result<Self, Self::Error> {
+ match val {
+ ash::vk::PhysicalDeviceType::INTEGRATED_GPU => Ok(Self::IntegratedGpu),
+ ash::vk::PhysicalDeviceType::DISCRETE_GPU => Ok(Self::DiscreteGpu),
+ ash::vk::PhysicalDeviceType::VIRTUAL_GPU => Ok(Self::VirtualGpu),
+ ash::vk::PhysicalDeviceType::CPU => Ok(Self::Cpu),
+ ash::vk::PhysicalDeviceType::OTHER => Ok(Self::Other),
+ _ => Err(()),
+ }
+ }
+}
+
+/// Represents a memory type in a physical device.
+#[derive(Debug, Copy, Clone)]
+pub struct MemoryType<'a> {
+ physical_device: PhysicalDevice<'a>,
+ id: u32,
+ info: &'a ash::vk::MemoryType,
+}
+
+impl<'a> MemoryType<'a> {
+ /// Returns the physical device associated to this memory type.
+ #[inline]
+ pub fn physical_device(&self) -> PhysicalDevice<'a> {
+ self.physical_device
+ }
+
+ /// Returns the identifier of this memory type within the physical device.
+ #[inline]
+ pub fn id(&self) -> u32 {
+ self.id
+ }
+
+ /// Returns the heap that corresponds to this memory type.
+ #[inline]
+ pub fn heap(&self) -> MemoryHeap<'a> {
+ self.physical_device
+ .memory_heap_by_id(self.info.heap_index)
+ .unwrap()
+ }
+
+ /// Returns true if the memory type is located on the device, which means that it's the most
+ /// efficient for GPU accesses.
+ #[inline]
+ pub fn is_device_local(&self) -> bool {
+ !(self.info.property_flags & ash::vk::MemoryPropertyFlags::DEVICE_LOCAL).is_empty()
+ }
+
+ /// Returns true if the memory type can be accessed by the host.
+ #[inline]
+ pub fn is_host_visible(&self) -> bool {
+ !(self.info.property_flags & ash::vk::MemoryPropertyFlags::HOST_VISIBLE).is_empty()
+ }
+
+ /// Returns true if modifications made by the host or the GPU on this memory type are
+ /// instantaneously visible to the other party. False means that changes have to be flushed.
+ ///
+ /// You don't need to worry about this, as this library handles that for you.
+ #[inline]
+ pub fn is_host_coherent(&self) -> bool {
+ !(self.info.property_flags & ash::vk::MemoryPropertyFlags::HOST_COHERENT).is_empty()
+ }
+
+ /// Returns true if memory of this memory type is cached by the host. Host memory accesses to
+ /// cached memory is faster than for uncached memory. However you are not guaranteed that it
+ /// is coherent.
+ #[inline]
+ pub fn is_host_cached(&self) -> bool {
+ !(self.info.property_flags & ash::vk::MemoryPropertyFlags::HOST_CACHED).is_empty()
+ }
+
+ /// Returns true if allocations made to this memory type is lazy.
+ ///
+ /// This means that no actual allocation is performed. Instead memory is automatically
+ /// allocated by the Vulkan implementation.
+ ///
+ /// Memory of this type can only be used on images created with a certain flag. Memory of this
+ /// type is never host-visible.
+ #[inline]
+ pub fn is_lazily_allocated(&self) -> bool {
+ !(self.info.property_flags & ash::vk::MemoryPropertyFlags::LAZILY_ALLOCATED).is_empty()
+ }
+}
+
+/// Represents a memory heap in a physical device.
+#[derive(Debug, Copy, Clone)]
+pub struct MemoryHeap<'a> {
+ physical_device: PhysicalDevice<'a>,
+ id: u32,
+ info: &'a ash::vk::MemoryHeap,
+}
+
+impl<'a> MemoryHeap<'a> {
+ /// Returns the physical device associated to this memory heap.
+ #[inline]
+ pub fn physical_device(&self) -> PhysicalDevice<'a> {
+ self.physical_device
+ }
+
+ /// Returns the identifier of this memory heap within the physical device.
+ #[inline]
+ pub fn id(&self) -> u32 {
+ self.id
+ }
+
+ /// Returns the size in bytes on this heap.
+ #[inline]
+ pub fn size(&self) -> DeviceSize {
+ self.info.size
+ }
+
+ /// Returns true if the heap is local to the GPU.
+ #[inline]
+ pub fn is_device_local(&self) -> bool {
+ !(self.info.flags & ash::vk::MemoryHeapFlags::DEVICE_LOCAL).is_empty()
+ }
+
+ /// Returns true if the heap is multi-instance enabled, that is allocation from such
+ /// heap will replicate to each physical-device's instance of heap.
+ #[inline]
+ pub fn is_multi_instance(&self) -> bool {
+ !(self.info.flags & ash::vk::MemoryHeapFlags::MULTI_INSTANCE).is_empty()
+ }
+}
+
+/// Represents a queue family in a physical device.
+///
+/// A queue family is group of one or multiple queues. All queues of one family have the same
+/// characteristics.
+#[derive(Debug, Copy, Clone)]
+pub struct QueueFamily<'a> {
+ physical_device: PhysicalDevice<'a>,
+ id: u32,
+ properties: &'a ash::vk::QueueFamilyProperties,
+}
+
+impl<'a> QueueFamily<'a> {
+ /// Returns the physical device associated to this queue family.
+ #[inline]
+ pub fn physical_device(&self) -> PhysicalDevice<'a> {
+ self.physical_device
+ }
+
+ /// Returns the identifier of this queue family within the physical device.
+ #[inline]
+ pub fn id(&self) -> u32 {
+ self.id
+ }
+
+ /// Returns the number of queues that belong to this family.
+ ///
+ /// Guaranteed to be at least 1 (or else that family wouldn't exist).
+ #[inline]
+ pub fn queues_count(&self) -> usize {
+ self.properties.queue_count as usize
+ }
+
+ /// If timestamps are supported, returns the number of bits supported by timestamp operations.
+ /// The returned value will be in the range 36..64.
+ /// If timestamps are not supported, returns None.
+ #[inline]
+ pub fn timestamp_valid_bits(&self) -> Option<u32> {
+ let value = self.properties.timestamp_valid_bits;
+ if value == 0 {
+ None
+ } else {
+ Some(value)
+ }
+ }
+
+ /// Returns the minimum granularity supported for image transfers in terms
+ /// of `[width, height, depth]`
+ #[inline]
+ pub fn min_image_transfer_granularity(&self) -> [u32; 3] {
+ let ref granularity = self.properties.min_image_transfer_granularity;
+ [granularity.width, granularity.height, granularity.depth]
+ }
+
+ /// Returns `true` if queues of this family can execute graphics operations.
+ #[inline]
+ pub fn supports_graphics(&self) -> bool {
+ !(self.properties.queue_flags & ash::vk::QueueFlags::GRAPHICS).is_empty()
+ }
+
+ /// Returns `true` if queues of this family can execute compute operations.
+ #[inline]
+ pub fn supports_compute(&self) -> bool {
+ !(self.properties.queue_flags & ash::vk::QueueFlags::COMPUTE).is_empty()
+ }
+
+ /// Returns `true` if queues of this family can execute transfer operations.
+ /// > **Note**: While all queues that can perform graphics or compute operations can implicitly perform
+ /// > transfer operations, graphics & compute queues only optionally indicate support for tranfers.
+ /// > Many discrete cards will have one queue family that exclusively sets the VK_QUEUE_TRANSFER_BIT
+ /// > to indicate a special relationship with the DMA module and more efficient transfers.
+ #[inline]
+ pub fn explicitly_supports_transfers(&self) -> bool {
+ !(self.properties.queue_flags & ash::vk::QueueFlags::TRANSFER).is_empty()
+ }
+
+ /// Returns `true` if queues of this family can execute sparse resources binding operations.
+ #[inline]
+ pub fn supports_sparse_binding(&self) -> bool {
+ !(self.properties.queue_flags & ash::vk::QueueFlags::SPARSE_BINDING).is_empty()
+ }
+
+ /// Returns `true` if the queues of this family support a particular pipeline stage.
+ #[inline]
+ pub fn supports_stage(&self, stage: PipelineStage) -> bool {
+ !(self.properties.queue_flags & stage.required_queue_flags()).is_empty()
+ }
+}
+
+impl<'a> PartialEq for QueueFamily<'a> {
+ fn eq(&self, other: &Self) -> bool {
+ self.id == other.id
+ && self.physical_device.internal_object() == other.physical_device.internal_object()
+ }
+}
+
+impl<'a> Eq for QueueFamily<'a> {}
+
+/// The version of the Vulkan conformance test that a driver is conformant against.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct ConformanceVersion {
+ pub major: u8,
+ pub minor: u8,
+ pub subminor: u8,
+ pub patch: u8,
+}
+
+impl From<ash::vk::ConformanceVersion> for ConformanceVersion {
+ #[inline]
+ fn from(val: ash::vk::ConformanceVersion) -> Self {
+ ConformanceVersion {
+ major: val.major,
+ minor: val.minor,
+ subminor: val.subminor,
+ patch: val.patch,
+ }
+ }
+}
+
+impl fmt::Debug for ConformanceVersion {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
+ }
+}
+
+impl fmt::Display for ConformanceVersion {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(self, formatter)
+ }
+}
+
+/// An identifier for the driver of a physical device.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(i32)]
+pub enum DriverId {
+ AMDProprietary = ash::vk::DriverId::AMD_PROPRIETARY.as_raw(),
+ AMDOpenSource = ash::vk::DriverId::AMD_OPEN_SOURCE.as_raw(),
+ MesaRADV = ash::vk::DriverId::MESA_RADV.as_raw(),
+ NvidiaProprietary = ash::vk::DriverId::NVIDIA_PROPRIETARY.as_raw(),
+ IntelProprietaryWindows = ash::vk::DriverId::INTEL_PROPRIETARY_WINDOWS.as_raw(),
+ IntelOpenSourceMesa = ash::vk::DriverId::INTEL_OPEN_SOURCE_MESA.as_raw(),
+ ImaginationProprietary = ash::vk::DriverId::IMAGINATION_PROPRIETARY.as_raw(),
+ QualcommProprietary = ash::vk::DriverId::QUALCOMM_PROPRIETARY.as_raw(),
+ ARMProprietary = ash::vk::DriverId::ARM_PROPRIETARY.as_raw(),
+ GoogleSwiftshader = ash::vk::DriverId::GOOGLE_SWIFTSHADER.as_raw(),
+ GGPProprietary = ash::vk::DriverId::GGP_PROPRIETARY.as_raw(),
+ BroadcomProprietary = ash::vk::DriverId::BROADCOM_PROPRIETARY.as_raw(),
+ MesaLLVMpipe = ash::vk::DriverId::MESA_LLVMPIPE.as_raw(),
+ MoltenVK = ash::vk::DriverId::MOLTENVK.as_raw(),
+}
+
+impl TryFrom<ash::vk::DriverId> for DriverId {
+ type Error = ();
+
+ #[inline]
+ fn try_from(val: ash::vk::DriverId) -> Result<Self, Self::Error> {
+ match val {
+ ash::vk::DriverId::AMD_PROPRIETARY => Ok(Self::AMDProprietary),
+ ash::vk::DriverId::AMD_OPEN_SOURCE => Ok(Self::AMDOpenSource),
+ ash::vk::DriverId::MESA_RADV => Ok(Self::MesaRADV),
+ ash::vk::DriverId::NVIDIA_PROPRIETARY => Ok(Self::NvidiaProprietary),
+ ash::vk::DriverId::INTEL_PROPRIETARY_WINDOWS => Ok(Self::IntelProprietaryWindows),
+ ash::vk::DriverId::INTEL_OPEN_SOURCE_MESA => Ok(Self::IntelOpenSourceMesa),
+ ash::vk::DriverId::IMAGINATION_PROPRIETARY => Ok(Self::ImaginationProprietary),
+ ash::vk::DriverId::QUALCOMM_PROPRIETARY => Ok(Self::QualcommProprietary),
+ ash::vk::DriverId::ARM_PROPRIETARY => Ok(Self::ARMProprietary),
+ ash::vk::DriverId::GOOGLE_SWIFTSHADER => Ok(Self::GoogleSwiftshader),
+ ash::vk::DriverId::GGP_PROPRIETARY => Ok(Self::GGPProprietary),
+ ash::vk::DriverId::BROADCOM_PROPRIETARY => Ok(Self::BroadcomProprietary),
+ ash::vk::DriverId::MESA_LLVMPIPE => Ok(Self::MesaLLVMpipe),
+ ash::vk::DriverId::MOLTENVK => Ok(Self::MoltenVK),
+ _ => Err(()),
+ }
+ }
+}
+
+/// Specifies which subgroup operations are supported.
+#[derive(Clone, Copy, Debug)]
+pub struct SubgroupFeatures {
+ pub basic: bool,
+ pub vote: bool,
+ pub arithmetic: bool,
+ pub ballot: bool,
+ pub shuffle: bool,
+ pub shuffle_relative: bool,
+ pub clustered: bool,
+ pub quad: bool,
+}
+
+impl From<ash::vk::SubgroupFeatureFlags> for SubgroupFeatures {
+ #[inline]
+ fn from(val: ash::vk::SubgroupFeatureFlags) -> Self {
+ Self {
+ basic: val.intersects(ash::vk::SubgroupFeatureFlags::BASIC),
+ vote: val.intersects(ash::vk::SubgroupFeatureFlags::VOTE),
+ arithmetic: val.intersects(ash::vk::SubgroupFeatureFlags::ARITHMETIC),
+ ballot: val.intersects(ash::vk::SubgroupFeatureFlags::BALLOT),
+ shuffle: val.intersects(ash::vk::SubgroupFeatureFlags::SHUFFLE),
+ shuffle_relative: val.intersects(ash::vk::SubgroupFeatureFlags::SHUFFLE_RELATIVE),
+ clustered: val.intersects(ash::vk::SubgroupFeatureFlags::CLUSTERED),
+ quad: val.intersects(ash::vk::SubgroupFeatureFlags::QUAD),
+ }
+ }
+}
+
+/// Specifies how the device clips single point primitives.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(i32)]
+pub enum PointClippingBehavior {
+ /// Points are clipped if they lie outside any clip plane, both those bounding the view volume
+ /// and user-defined clip planes.
+ AllClipPlanes = ash::vk::PointClippingBehavior::ALL_CLIP_PLANES.as_raw(),
+ /// Points are clipped only if they lie outside a user-defined clip plane.
+ UserClipPlanesOnly = ash::vk::PointClippingBehavior::USER_CLIP_PLANES_ONLY.as_raw(),
+}
+
+impl TryFrom<ash::vk::PointClippingBehavior> for PointClippingBehavior {
+ type Error = ();
+
+ #[inline]
+ fn try_from(val: ash::vk::PointClippingBehavior) -> Result<Self, Self::Error> {
+ match val {
+ ash::vk::PointClippingBehavior::ALL_CLIP_PLANES => Ok(Self::AllClipPlanes),
+ ash::vk::PointClippingBehavior::USER_CLIP_PLANES_ONLY => Ok(Self::UserClipPlanesOnly),
+ _ => Err(()),
+ }
+ }
+}
+
+/// Specifies whether, and how, shader float controls can be set independently.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[repr(i32)]
+pub enum ShaderFloatControlsIndependence {
+ Float32Only = ash::vk::ShaderFloatControlsIndependence::TYPE_32_ONLY.as_raw(),
+ All = ash::vk::ShaderFloatControlsIndependence::ALL.as_raw(),
+ None = ash::vk::ShaderFloatControlsIndependence::NONE.as_raw(),
+}
+
+impl TryFrom<ash::vk::ShaderFloatControlsIndependence> for ShaderFloatControlsIndependence {
+ type Error = ();
+
+ #[inline]
+ fn try_from(val: ash::vk::ShaderFloatControlsIndependence) -> Result<Self, Self::Error> {
+ match val {
+ ash::vk::ShaderFloatControlsIndependence::TYPE_32_ONLY => Ok(Self::Float32Only),
+ ash::vk::ShaderFloatControlsIndependence::ALL => Ok(Self::All),
+ ash::vk::ShaderFloatControlsIndependence::NONE => Ok(Self::None),
+ _ => Err(()),
+ }
+ }
+}
+
+/// Specifies shader core properties.
+#[derive(Clone, Copy, Debug)]
+pub struct ShaderCoreProperties {}
+
+impl From<ash::vk::ShaderCorePropertiesFlagsAMD> for ShaderCoreProperties {
+ #[inline]
+ fn from(val: ash::vk::ShaderCorePropertiesFlagsAMD) -> Self {
+ Self {}
+ }
+}