Add support for instance and device versions, check version in vulkano-shaders (#1569)

* Move physical device functions into their own module

* Add method to FunctionPointers to query Vulkan api version

* Store the versions of instances and devices, allow user to query

* Check Vulkan version in vulkano-shaders, changelog

* Small mistake in changelog
This commit is contained in:
Rua 2021-05-15 22:04:55 +02:00 committed by GitHub
parent 8c0630d3c8
commit 6f3deceb8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1064 additions and 961 deletions

View File

@ -3,7 +3,10 @@
Please add new changes at the bottom, preceded by a hyphen -.
Breaking changes should be listed first, before other changes, and should be preceded by - **Breaking**.
-->
- **Breaking** Vulkano-shaders now checks if the device supports the shader's SPIR-V version, when loading the shader.
- Added `DeviceExtensions::khr_spirv_1_4`, which allows SPIR-V 1.4 shaders in Vulkan versions below 1.2.
- Added `FunctionPointers::api_version` to query the highest supported instance version.
- Added `Instance::api_version` and `Device::api_version` to return the actual supported Vulkan version. These may differ between instance and device, and be lower than what `FunctionPointers::api_version` and `PhysicalDevice::api_version` return (currently never higher than 1.1, but this may change in the future).
# Version 0.23.0 (2021-04-10)

View File

@ -215,6 +215,33 @@ where
// checking whether each required capability is enabled in the Vulkan device
let mut cap_checks: Vec<TokenStream> = vec![];
match doc.version {
(1, 0) => {}
(1, 1) | (1, 2) | (1, 3) => {
cap_checks.push(quote! {
if device.api_version() < (Version { major: 1, minor: 1, patch: 0 }) {
panic!("Device API version 1.1 required");
}
});
}
(1, 4) => {
cap_checks.push(quote! {
if device.api_version() < (Version { major: 1, minor: 2, patch: 0 })
&& !device.loaded_extensions().khr_spirv_1_4 {
panic!("Device API version 1.2 or extension VK_KHR_spirv_1_4 required");
}
});
}
(1, 5) => {
cap_checks.push(quote! {
if device.api_version() < (Version { major: 1, minor: 2, patch: 0 }) {
panic!("Device API version 1.2 required");
}
});
}
_ => return Err(Error::UnsupportedSpirvVersion),
}
for i in doc.instructions.iter() {
let dev_req = {
match i {
@ -325,6 +352,8 @@ where
use vulkano::pipeline::shader::SpecializationConstants as SpecConstsTrait;
#[allow(unused_imports)]
use vulkano::pipeline::shader::SpecializationMapEntry;
#[allow(unused_imports)]
use vulkano::Version;
pub struct #struct_name {
shader: ::std::sync::Arc<::vulkano::pipeline::shader::ShaderModule>,
@ -379,6 +408,7 @@ where
#[derive(Debug)]
pub enum Error {
UnsupportedSpirvVersion,
IoError(IoError),
ParseError(ParseError),
}

View File

@ -128,23 +128,26 @@ device_extensions! {
// https://vulkan.lunarg.com/doc/view/1.2.162.1/mac/1.2-extensions/vkspec.html#VUID-VkDeviceCreateInfo-pProperties-04451
khr_portability_subset,
],
khr_swapchain => b"VK_KHR_swapchain",
khr_display_swapchain => b"VK_KHR_display_swapchain",
khr_sampler_mirror_clamp_to_edge => b"VK_KHR_sampler_mirror_clamp_to_edge",
khr_maintenance1 => b"VK_KHR_maintenance1",
khr_get_memory_requirements2 => b"VK_KHR_get_memory_requirements2",
khr_dedicated_allocation => b"VK_KHR_dedicated_allocation",
khr_incremental_present => b"VK_KHR_incremental_present",
// List in order: khr, ext, then alphabetical
khr_16bit_storage => b"VK_KHR_16bit_storage",
khr_8bit_storage => b"VK_KHR_8bit_storage",
khr_storage_buffer_storage_class => b"VK_KHR_storage_buffer_storage_class",
ext_debug_utils => b"VK_EXT_debug_utils",
khr_multiview => b"VK_KHR_multiview",
ext_full_screen_exclusive => b"VK_EXT_full_screen_exclusive",
khr_dedicated_allocation => b"VK_KHR_dedicated_allocation",
khr_display_swapchain => b"VK_KHR_display_swapchain",
khr_external_memory => b"VK_KHR_external_memory",
khr_external_memory_fd => b"VK_KHR_external_memory_fd",
ext_external_memory_dmabuf => b"VK_EXT_external_memory_dma_buf",
khr_get_memory_requirements2 => b"VK_KHR_get_memory_requirements2",
khr_incremental_present => b"VK_KHR_incremental_present",
khr_maintenance1 => b"VK_KHR_maintenance1",
khr_multiview => b"VK_KHR_multiview",
khr_portability_subset => b"VK_KHR_portability_subset",
khr_sampler_mirror_clamp_to_edge => b"VK_KHR_sampler_mirror_clamp_to_edge",
khr_spirv_1_4 => b"VK_KHR_spirv_1_4",
khr_storage_buffer_storage_class => b"VK_KHR_storage_buffer_storage_class",
khr_swapchain => b"VK_KHR_swapchain",
ext_debug_utils => b"VK_EXT_debug_utils",
ext_external_memory_dmabuf => b"VK_EXT_external_memory_dma_buf",
ext_full_screen_exclusive => b"VK_EXT_full_screen_exclusive",
}
/// This helper type can only be instantiated inside this module.

View File

@ -130,6 +130,7 @@ use crate::image::ImageFormatProperties;
use crate::image::ImageTiling;
use crate::image::ImageType;
use crate::image::ImageUsage;
use crate::Version;
use std::pin::Pin;
/// Represents a Vulkan context.
@ -137,6 +138,11 @@ pub struct Device {
instance: Arc<Instance>,
physical_device: usize,
device: vk::Device,
// The highest version that is supported for this device.
// This is the minimum of Instance::desired_version and PhysicalDevice::api_version.
api_version: Version,
vk: vk::DevicePointers,
standard_pool: Mutex<Weak<StdMemoryPool>>,
standard_descriptor_pool: Mutex<Weak<StdDescriptorPool>>,
@ -187,6 +193,9 @@ impl Device {
I: IntoIterator<Item = (QueueFamily<'a>, f32)>,
Ext: Into<RawDeviceExtensions>,
{
let desired_version = phys.instance().desired_version;
let api_version = std::cmp::min(desired_version, phys.api_version());
let queue_families = queue_families.into_iter();
if !phys.supported_features().superset_of(&requested_features) {
@ -329,6 +338,7 @@ impl Device {
instance: phys.instance().clone(),
physical_device: phys.index(),
device: device,
api_version,
vk: vk,
standard_pool: Mutex::new(Weak::new()),
standard_descriptor_pool: Mutex::new(Weak::new()),
@ -356,6 +366,12 @@ impl Device {
Ok((device, output_queues))
}
/// Returns the Vulkan version supported by this `Device`.
#[inline]
pub fn api_version(&self) -> Version {
self.api_version
}
/// Grants access to the pointers to the Vulkan functions of the device.
#[inline]
pub fn pointers(&self) -> &vk::DevicePointers {

View File

@ -145,30 +145,32 @@ macro_rules! instance_extensions {
instance_extensions! {
InstanceExtensions,
RawInstanceExtensions,
khr_surface => b"VK_KHR_surface",
khr_display => b"VK_KHR_display",
khr_xlib_surface => b"VK_KHR_xlib_surface",
khr_xcb_surface => b"VK_KHR_xcb_surface",
khr_wayland_surface => b"VK_KHR_wayland_surface",
// List in order: khr, ext, then alphabetical
khr_android_surface => b"VK_KHR_android_surface",
khr_win32_surface => b"VK_KHR_win32_surface",
ext_debug_utils => b"VK_EXT_debug_utils",
mvk_ios_surface => b"VK_MVK_ios_surface",
mvk_macos_surface => b"VK_MVK_macos_surface",
mvk_moltenvk => b"VK_MVK_moltenvk", // TODO: confirm that it's an instance extension
nn_vi_surface => b"VK_NN_vi_surface",
ext_swapchain_colorspace => b"VK_EXT_swapchain_colorspace",
khr_get_physical_device_properties2 => b"VK_KHR_get_physical_device_properties2",
khr_get_surface_capabilities2 => b"VK_KHR_get_surface_capabilities2",
khr_device_group_creation => b"VK_KHR_device_group_creation",
khr_display => b"VK_KHR_display",
khr_external_fence_capabilities => b"VK_KHR_external_fence_capabilities",
khr_external_memory_capabilities => b"VK_KHR_external_memory_capabilities",
khr_external_semaphore_capabilities => b"VK_KHR_external_semaphore_capabilities",
khr_get_display_properties2 => b"VK_KHR_get_display_properties2",
khr_get_physical_device_properties2 => b"VK_KHR_get_physical_device_properties2",
khr_get_surface_capabilities2 => b"VK_KHR_get_surface_capabilities2",
khr_surface => b"VK_KHR_surface",
khr_wayland_surface => b"VK_KHR_wayland_surface",
khr_win32_surface => b"VK_KHR_win32_surface",
khr_xcb_surface => b"VK_KHR_xcb_surface",
khr_xlib_surface => b"VK_KHR_xlib_surface",
ext_acquire_xlib_display => b"VK_EXT_acquire_xlib_display",
ext_debug_report => b"VK_EXT_debug_report",
ext_debug_utils => b"VK_EXT_debug_utils",
ext_direct_mode_display => b"VK_EXT_direct_mode_display",
ext_display_surface_counter => b"VK_EXT_display_surface_counter",
ext_swapchain_colorspace => b"VK_EXT_swapchain_colorspace",
mvk_ios_surface => b"VK_MVK_ios_surface",
mvk_macos_surface => b"VK_MVK_macos_surface",
mvk_moltenvk => b"VK_MVK_moltenvk", // TODO: confirm that it's an instance extension
nn_vi_surface => b"VK_NN_vi_surface",
}
/// This helper type can only be instantiated inside this module.

View File

@ -8,28 +8,24 @@
// according to those terms.
use crate::check_errors;
use crate::features::{Features, FeaturesFfi};
use crate::instance::limits::Limits;
use crate::instance::loader;
use crate::instance::loader::FunctionPointers;
use crate::instance::loader::Loader;
use crate::instance::loader::LoadingError;
use crate::instance::physical_device::{init_physical_devices, PhysicalDeviceInfos};
use crate::instance::{InstanceExtensions, RawInstanceExtensions};
use crate::sync::PipelineStage;
use crate::version::Version;
use crate::vk;
use crate::Error;
use crate::OomError;
use crate::Version;
use crate::VulkanObject;
use smallvec::SmallVec;
use std::borrow::Cow;
use std::error;
use std::ffi::CStr;
use std::ffi::CString;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::mem;
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::ptr;
@ -94,7 +90,16 @@ use std::sync::Arc;
pub struct Instance {
instance: vk::Instance,
//alloc: Option<Box<Alloc + Send + Sync>>,
physical_devices: Vec<PhysicalDeviceInfos>,
// The desired API version for instances and devices created from it.
// TODO: allow the user to specify this on construction.
pub(crate) desired_version: Version,
// The highest version that is supported for this instance.
// This is the minimum of Instance::desired_version and FunctionPointers::api_version.
api_version: Version,
pub(super) physical_devices: Vec<PhysicalDeviceInfos>,
vk: vk::InstancePointers,
extensions: RawInstanceExtensions,
layers: SmallVec<[CString; 16]>,
@ -208,6 +213,21 @@ impl Instance {
None
};
// TODO: allow the user to specify this.
// Vulkan 1.0 will return VK_ERROR_INCOMPATIBLE_DRIVER on instance creation if this isn't
// 1.0, but higher versions never return this error, and thus will allow a desired version
// beyond what they support. The actual support on that particular instance/device is the
// minimum of desired and supported.
// In other words, it's impossible to use a Vulkan 1.1 or 1.2 device with a 1.0 instance,
// because instance creation with a higher desired_version will fail. But it is possible to
// use a 1.0 or 1.2 device with a 1.1 instance.
let desired_version = Version {
major: 1,
minor: 1,
patch: 0,
};
let api_version = std::cmp::min(desired_version, function_pointers.api_version()?);
// Building the `vk::ApplicationInfo` if required.
let app_infos = if let Some(app_infos) = app_infos {
Some(vk::ApplicationInfo {
@ -235,12 +255,7 @@ impl Instance {
.engine_version
.map(|v| v.into_vulkan_version())
.unwrap_or(0),
apiVersion: Version {
major: 1,
minor: 1,
patch: 0,
}
.into_vulkan_version(), // TODO:
apiVersion: desired_version.into_vulkan_version(), // TODO:
})
} else {
None
@ -282,38 +297,18 @@ impl Instance {
// Loading the function pointers of the newly-created instance.
let vk = {
vk::InstancePointers::load(|name| unsafe {
mem::transmute(function_pointers.get_instance_proc_addr(instance, name.as_ptr()))
vk::InstancePointers::load(|name| {
function_pointers.get_instance_proc_addr(instance, name.as_ptr())
})
};
// Enumerating all physical devices.
let physical_devices: Vec<vk::PhysicalDevice> = unsafe {
let mut num = 0;
check_errors(vk.EnumeratePhysicalDevices(instance, &mut num, ptr::null_mut()))?;
let mut devices = Vec::with_capacity(num as usize);
check_errors(vk.EnumeratePhysicalDevices(instance, &mut num, devices.as_mut_ptr()))?;
devices.set_len(num as usize);
devices
};
let vk_khr_get_physical_device_properties2 =
CString::new(b"VK_KHR_get_physical_device_properties2".to_vec()).unwrap();
// Getting the properties of all physical devices.
// If possible, we use VK_KHR_get_physical_device_properties2.
let physical_devices = if extensions
.iter()
.any(|v| *v == vk_khr_get_physical_device_properties2)
{
Instance::init_physical_devices2(&vk, physical_devices, &extensions)
} else {
Instance::init_physical_devices(&vk, physical_devices)
};
let physical_devices = init_physical_devices(instance, &vk, &extensions)?;
Ok(Arc::new(Instance {
instance,
api_version,
desired_version,
//alloc: None,
physical_devices,
vk,
@ -323,145 +318,6 @@ impl Instance {
}))
}
/// Initialize all physical devices
fn init_physical_devices(
vk: &vk::InstancePointers,
physical_devices: Vec<vk::PhysicalDevice>,
) -> Vec<PhysicalDeviceInfos> {
let mut output = Vec::with_capacity(physical_devices.len());
for device in physical_devices.into_iter() {
let properties: vk::PhysicalDeviceProperties = unsafe {
let mut output = MaybeUninit::uninit();
vk.GetPhysicalDeviceProperties(device, output.as_mut_ptr());
output.assume_init()
};
let queue_families = unsafe {
let mut num = 0;
vk.GetPhysicalDeviceQueueFamilyProperties(device, &mut num, ptr::null_mut());
let mut families = Vec::with_capacity(num as usize);
vk.GetPhysicalDeviceQueueFamilyProperties(device, &mut num, families.as_mut_ptr());
families.set_len(num as usize);
families
};
let memory: vk::PhysicalDeviceMemoryProperties = unsafe {
let mut output = MaybeUninit::uninit();
vk.GetPhysicalDeviceMemoryProperties(device, output.as_mut_ptr());
output.assume_init()
};
let available_features: vk::PhysicalDeviceFeatures = unsafe {
let mut output = MaybeUninit::uninit();
vk.GetPhysicalDeviceFeatures(device, output.as_mut_ptr());
output.assume_init()
};
output.push(PhysicalDeviceInfos {
device,
properties,
extended_properties: PhysicalDeviceExtendedProperties::empty(),
memory,
queue_families,
available_features: Features::from(available_features),
});
}
output
}
/// 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_devices2(
vk: &vk::InstancePointers,
physical_devices: Vec<vk::PhysicalDevice>,
extensions: &RawInstanceExtensions,
) -> Vec<PhysicalDeviceInfos> {
let mut output = Vec::with_capacity(physical_devices.len());
for device in physical_devices.into_iter() {
let mut extended_properties = PhysicalDeviceExtendedProperties::empty();
let properties: vk::PhysicalDeviceProperties = unsafe {
let mut subgroup_properties = vk::PhysicalDeviceSubgroupProperties {
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES,
pNext: ptr::null_mut(),
subgroupSize: 0,
supportedStages: 0,
supportedOperations: 0,
quadOperationsInAllStages: 0,
};
let mut output = vk::PhysicalDeviceProperties2KHR {
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
pNext: &mut subgroup_properties,
properties: mem::zeroed(),
};
vk.GetPhysicalDeviceProperties2KHR(device, &mut output);
extended_properties = PhysicalDeviceExtendedProperties {
subgroup_size: Some(subgroup_properties.subgroupSize),
..extended_properties
};
output.properties
};
let queue_families = unsafe {
let mut num = 0;
vk.GetPhysicalDeviceQueueFamilyProperties2KHR(device, &mut num, ptr::null_mut());
let mut families = (0..num)
.map(|_| vk::QueueFamilyProperties2KHR {
sType: vk::STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR,
pNext: ptr::null_mut(),
queueFamilyProperties: mem::zeroed(),
})
.collect::<Vec<_>>();
vk.GetPhysicalDeviceQueueFamilyProperties2KHR(
device,
&mut num,
families.as_mut_ptr(),
);
families
.into_iter()
.map(|family| family.queueFamilyProperties)
.collect()
};
let memory: vk::PhysicalDeviceMemoryProperties = unsafe {
let mut output = vk::PhysicalDeviceMemoryProperties2KHR {
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR,
pNext: ptr::null_mut(),
memoryProperties: mem::zeroed(),
};
vk.GetPhysicalDeviceMemoryProperties2KHR(device, &mut output);
output.memoryProperties
};
let available_features: Features = unsafe {
let mut output = FeaturesFfi::new();
let ptr = FeaturesFfi::mut_base_ptr(&mut output) as *mut _;
vk.GetPhysicalDeviceFeatures2KHR(device, ptr);
Features::from(&output.main)
};
output.push(PhysicalDeviceInfos {
device,
properties,
extended_properties,
memory,
queue_families,
available_features,
});
}
output
}
/*/// Same as `new`, but provides an allocator that will be used by the Vulkan library whenever
/// it needs to allocate memory on the host.
///
@ -470,6 +326,12 @@ impl Instance {
unimplemented!()
}*/
/// Returns the Vulkan version supported by this `Instance`.
#[inline]
pub fn api_version(&self) -> Version {
self.api_version
}
/// Grants access to the Vulkan functions of the instance.
#[inline]
pub fn pointers(&self) -> &vk::InstancePointers {
@ -724,706 +586,6 @@ impl From<Error> for InstanceCreationError {
}
}
struct PhysicalDeviceInfos {
device: vk::PhysicalDevice,
properties: vk::PhysicalDeviceProperties,
extended_properties: PhysicalDeviceExtendedProperties,
queue_families: Vec<vk::QueueFamilyProperties>,
memory: vk::PhysicalDeviceMemoryProperties,
available_features: Features,
}
/// Represents additional information related to Physical Devices fetched from
/// `vkGetPhysicalDeviceProperties` call. Certain features available only when
/// appropriate `Instance` extensions enabled. The core extension required
/// for this features is `InstanceExtensions::khr_get_physical_device_properties2`
///
/// TODO: Only a small subset of available properties(https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceProperties2.html) is implemented at this moment.
pub struct PhysicalDeviceExtendedProperties {
subgroup_size: Option<u32>,
}
impl PhysicalDeviceExtendedProperties {
fn empty() -> Self {
Self {
subgroup_size: None,
}
}
/// The default number of invocations in each subgroup
///
/// See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceSubgroupProperties.html for details
#[inline]
pub fn subgroup_size(&self) -> &Option<u32> {
&self.subgroup_size
}
}
/// 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::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// print_infos(physical_device);
/// }
///
/// fn print_infos(dev: PhysicalDevice) {
/// println!("Name: {}", dev.name());
/// }
/// ```
#[derive(Debug, Copy, Clone)]
pub struct PhysicalDevice<'a> {
instance: &'a Arc<Instance>,
device: usize,
}
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::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// println!("Available device: {}", physical_device.name());
/// }
/// ```
#[inline]
pub fn enumerate(instance: &'a Arc<Instance>) -> PhysicalDevicesIter<'a> {
PhysicalDevicesIter {
instance,
current_id: 0,
}
}
/// 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::instance::PhysicalDevice;
///
/// let instance = Instance::new(None, &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>> {
if instance.physical_devices.len() > index {
Some(PhysicalDevice {
instance,
device: index,
})
} else {
None
}
}
/// Returns the instance corresponding to this physical device.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance::PhysicalDevice;
///
/// fn do_something(physical_device: PhysicalDevice) {
/// let _loaded_extensions = physical_device.instance().loaded_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.device
}
/// Returns the human-readable name of the device.
#[inline]
pub fn name(&self) -> &str {
unsafe {
let val = &self.infos().properties.deviceName;
let val = CStr::from_ptr(val.as_ptr());
val.to_str()
.expect("physical device name contained non-UTF8 characters")
}
}
/// Returns the type of the device.
///
/// # Example
///
/// ```no_run
/// # use vulkano::instance::Instance;
/// # use vulkano::instance::InstanceExtensions;
/// use vulkano::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// println!("Available device: {} (type: {:?})",
/// physical_device.name(), physical_device.ty());
/// }
/// ```
#[inline]
pub fn ty(&self) -> PhysicalDeviceType {
match self.instance.physical_devices[self.device]
.properties
.deviceType
{
vk::PHYSICAL_DEVICE_TYPE_OTHER => PhysicalDeviceType::Other,
vk::PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => PhysicalDeviceType::IntegratedGpu,
vk::PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => PhysicalDeviceType::DiscreteGpu,
vk::PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU => PhysicalDeviceType::VirtualGpu,
vk::PHYSICAL_DEVICE_TYPE_CPU => PhysicalDeviceType::Cpu,
_ => panic!("Unrecognized Vulkan device type"),
}
}
/// Returns the version of Vulkan supported by this device.
#[inline]
pub fn api_version(&self) -> Version {
let val = self.infos().properties.apiVersion;
Version::from_vulkan_version(val)
}
/// Returns the Vulkan features that are supported by this physical device.
#[inline]
pub fn supported_features(&self) -> &'a Features {
&self.infos().available_features
}
/// Builds an iterator that enumerates all the queue families on this physical device.
#[inline]
pub fn queue_families(&self) -> QueueFamiliesIter<'a> {
QueueFamiliesIter {
physical_device: *self,
current_id: 0,
}
}
/// 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.infos().queue_families.len() {
Some(QueueFamily {
physical_device: *self,
id,
})
} else {
None
}
}
/// Builds an iterator that enumerates all the memory types on this physical device.
#[inline]
pub fn memory_types(&self) -> MemoryTypesIter<'a> {
MemoryTypesIter {
physical_device: *self,
current_id: 0,
}
}
/// 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.infos().memory.memoryTypeCount {
Some(MemoryType {
physical_device: *self,
id,
})
} else {
None
}
}
/// Builds an iterator that enumerates all the memory heaps on this physical device.
#[inline]
pub fn memory_heaps(&self) -> MemoryHeapsIter<'a> {
MemoryHeapsIter {
physical_device: *self,
current_id: 0,
}
}
/// 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.infos().memory.memoryHeapCount {
Some(MemoryHeap {
physical_device: *self,
id,
})
} else {
None
}
}
/// Gives access to the limits of the physical device.
///
/// This function should be zero-cost in release mode. It only exists to not pollute the
/// namespace of `PhysicalDevice` with all the limits-related getters.
#[inline]
pub fn limits(&self) -> Limits<'a> {
Limits::from_vk_limits(&self.infos().properties.limits)
}
/// Returns an opaque number representing the version of the driver of this device.
///
/// The meaning of this number is implementation-specific. It can be used in bug reports, for
/// example.
#[inline]
pub fn driver_version(&self) -> u32 {
self.infos().properties.driverVersion
}
/// Returns the PCI ID of the device.
#[inline]
pub fn pci_device_id(&self) -> u32 {
self.infos().properties.deviceID
}
/// Returns the PCI ID of the vendor.
#[inline]
pub fn pci_vendor_id(&self) -> u32 {
self.infos().properties.vendorID
}
/// Returns a unique identifier for the device.
///
/// Can be stored in a configuration file, so that you can retrieve the device again the next
/// time the program is run.
#[inline]
pub fn uuid(&self) -> &[u8; 16] {
// must be equal to vk::UUID_SIZE
&self.infos().properties.pipelineCacheUUID
}
#[inline]
pub fn extended_properties(&self) -> &PhysicalDeviceExtendedProperties {
&self.infos().extended_properties
}
// Internal function to make it easier to get the infos of this device.
#[inline]
fn infos(&self) -> &'a PhysicalDeviceInfos {
&self.instance.physical_devices[self.device]
}
}
unsafe impl<'a> VulkanObject for PhysicalDevice<'a> {
type Object = vk::PhysicalDevice;
const TYPE: vk::ObjectType = vk::OBJECT_TYPE_PHYSICAL_DEVICE;
#[inline]
fn internal_object(&self) -> vk::PhysicalDevice {
self.infos().device
}
}
/// Iterator for all the physical devices available on hardware.
#[derive(Debug, Clone)]
pub struct PhysicalDevicesIter<'a> {
instance: &'a Arc<Instance>,
current_id: usize,
}
impl<'a> Iterator for PhysicalDevicesIter<'a> {
type Item = PhysicalDevice<'a>;
#[inline]
fn next(&mut self) -> Option<PhysicalDevice<'a>> {
if self.current_id >= self.instance.physical_devices.len() {
return None;
}
let dev = PhysicalDevice {
instance: self.instance,
device: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.instance.physical_devices.len() - self.current_id;
(len, Some(len))
}
}
impl<'a> ExactSizeIterator for PhysicalDevicesIter<'a> {}
/// Type of a physical device.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(u32)]
pub enum PhysicalDeviceType {
/// The device is an integrated GPU.
IntegratedGpu = 1,
/// The device is a discrete GPU.
DiscreteGpu = 2,
/// The device is a virtual GPU.
VirtualGpu = 3,
/// The device is a CPU.
Cpu = 4,
/// The device is something else.
Other = 0,
}
/// 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,
}
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.physical_device.infos().queue_families[self.id as usize].queueCount 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.physical_device.infos().queue_families[self.id as usize].timestampValidBits;
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.physical_device.infos().queue_families[self.id as usize]
.minImageTransferGranularity;
[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.flags() & vk::QUEUE_GRAPHICS_BIT) != 0
}
/// Returns `true` if queues of this family can execute compute operations.
#[inline]
pub fn supports_compute(&self) -> bool {
(self.flags() & vk::QUEUE_COMPUTE_BIT) != 0
}
/// 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.flags() & vk::QUEUE_TRANSFER_BIT) != 0
}
/// Returns `true` if queues of this family can execute sparse resources binding operations.
#[inline]
pub fn supports_sparse_binding(&self) -> bool {
(self.flags() & vk::QUEUE_SPARSE_BINDING_BIT) != 0
}
/// Returns `true` if the queues of this family support a particular pipeline stage.
#[inline]
pub fn supports_stage(&self, stage: PipelineStage) -> bool {
(self.flags() & stage.required_queue_flags()) != 0
}
/// Internal utility function that returns the flags of this queue family.
#[inline]
fn flags(&self) -> u32 {
self.physical_device.infos().queue_families[self.id as usize].queueFlags
}
}
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> {}
/// Iterator for all the queue families available on a physical device.
#[derive(Debug, Clone)]
pub struct QueueFamiliesIter<'a> {
physical_device: PhysicalDevice<'a>,
current_id: u32,
}
impl<'a> Iterator for QueueFamiliesIter<'a> {
type Item = QueueFamily<'a>;
#[inline]
fn next(&mut self) -> Option<QueueFamily<'a>> {
if self.current_id as usize >= self.physical_device.infos().queue_families.len() {
return None;
}
let dev = QueueFamily {
physical_device: self.physical_device,
id: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.physical_device.infos().queue_families.len();
let remain = len - self.current_id as usize;
(remain, Some(remain))
}
}
impl<'a> ExactSizeIterator for QueueFamiliesIter<'a> {}
/// Represents a memory type in a physical device.
#[derive(Debug, Copy, Clone)]
pub struct MemoryType<'a> {
physical_device: PhysicalDevice<'a>,
id: u32,
}
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> {
let heap_id = self.physical_device.infos().memory.memoryTypes[self.id as usize].heapIndex;
MemoryHeap {
physical_device: self.physical_device,
id: heap_id,
}
}
/// 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.flags() & vk::MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0
}
/// Returns true if the memory type can be accessed by the host.
#[inline]
pub fn is_host_visible(&self) -> bool {
(self.flags() & vk::MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0
}
/// 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.flags() & vk::MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0
}
/// 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.flags() & vk::MEMORY_PROPERTY_HOST_CACHED_BIT) != 0
}
/// 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.flags() & vk::MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0
}
/// Internal utility function that returns the flags of this queue family.
#[inline]
fn flags(&self) -> u32 {
self.physical_device.infos().memory.memoryTypes[self.id as usize].propertyFlags
}
}
/// Iterator for all the memory types available on a physical device.
#[derive(Debug, Clone)]
pub struct MemoryTypesIter<'a> {
physical_device: PhysicalDevice<'a>,
current_id: u32,
}
impl<'a> Iterator for MemoryTypesIter<'a> {
type Item = MemoryType<'a>;
#[inline]
fn next(&mut self) -> Option<MemoryType<'a>> {
if self.current_id >= self.physical_device.infos().memory.memoryTypeCount {
return None;
}
let dev = MemoryType {
physical_device: self.physical_device,
id: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.physical_device.infos().memory.memoryTypeCount;
let remain = (len - self.current_id) as usize;
(remain, Some(remain))
}
}
impl<'a> ExactSizeIterator for MemoryTypesIter<'a> {}
/// Represents a memory heap in a physical device.
#[derive(Debug, Copy, Clone)]
pub struct MemoryHeap<'a> {
physical_device: PhysicalDevice<'a>,
id: u32,
}
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) -> usize {
self.physical_device.infos().memory.memoryHeaps[self.id as usize].size as usize
}
/// Returns true if the heap is local to the GPU.
#[inline]
pub fn is_device_local(&self) -> bool {
let flags = self.physical_device.infos().memory.memoryHeaps[self.id as usize].flags;
(flags & vk::MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0
}
/// 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 {
let flags = self.physical_device.infos().memory.memoryHeaps[self.id as usize].flags;
(flags & vk::MEMORY_HEAP_MULTI_INSTANCE_BIT) != 0
}
}
/// Iterator for all the memory heaps available on a physical device.
#[derive(Debug, Clone)]
pub struct MemoryHeapsIter<'a> {
physical_device: PhysicalDevice<'a>,
current_id: u32,
}
impl<'a> Iterator for MemoryHeapsIter<'a> {
type Item = MemoryHeap<'a>;
#[inline]
fn next(&mut self) -> Option<MemoryHeap<'a>> {
if self.current_id >= self.physical_device.infos().memory.memoryHeapCount {
return None;
}
let dev = MemoryHeap {
physical_device: self.physical_device,
id: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.physical_device.infos().memory.memoryHeapCount;
let remain = (len - self.current_id) as usize;
(remain, Some(remain))
}
}
impl<'a> ExactSizeIterator for MemoryHeapsIter<'a> {}
#[cfg(test)]
mod tests {
use crate::instance;

View File

@ -16,10 +16,10 @@ use std::vec::IntoIter;
use crate::check_errors;
use crate::instance::loader;
use crate::instance::loader::LoadingError;
use crate::version::Version;
use crate::vk;
use crate::Error;
use crate::OomError;
use crate::Version;
/// Queries the list of layers that are available when creating an instance.
///

View File

@ -21,11 +21,15 @@
//! By default vulkano will use the `auto_loader()` function, which tries to automatically load
//! a Vulkan implementation from the system.
use crate::check_errors;
use crate::vk;
use crate::OomError;
use crate::SafeDeref;
use crate::Version;
use lazy_static::lazy_static;
use shared_library;
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::mem;
use std::ops::Deref;
@ -38,11 +42,7 @@ pub unsafe trait Loader {
/// Calls the `vkGetInstanceProcAddr` function. The parameters are the same.
///
/// The returned function must stay valid for as long as `self` is alive.
fn get_instance_proc_addr(
&self,
instance: vk::Instance,
name: *const c_char,
) -> extern "system" fn() -> ();
fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char) -> *const c_void;
}
unsafe impl<T> Loader for T
@ -51,11 +51,7 @@ where
T::Target: Loader,
{
#[inline]
fn get_instance_proc_addr(
&self,
instance: vk::Instance,
name: *const c_char,
) -> extern "system" fn() -> () {
fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char) -> *const c_void {
(**self).get_instance_proc_addr(instance, name)
}
}
@ -63,10 +59,8 @@ where
/// Implementation of `Loader` that loads Vulkan from a dynamic library.
pub struct DynamicLibraryLoader {
vk_lib: shared_library::dynamic_library::DynamicLibrary,
get_proc_addr: extern "system" fn(
instance: vk::Instance,
pName: *const c_char,
) -> extern "system" fn() -> (),
get_proc_addr:
extern "system" fn(instance: vk::Instance, pName: *const c_char) -> *const c_void,
}
impl DynamicLibraryLoader {
@ -100,11 +94,7 @@ impl DynamicLibraryLoader {
unsafe impl Loader for DynamicLibraryLoader {
#[inline]
fn get_instance_proc_addr(
&self,
instance: vk::Instance,
name: *const c_char,
) -> extern "system" fn() -> () {
fn get_instance_proc_addr(&self, instance: vk::Instance, name: *const c_char) -> *const c_void {
(self.get_proc_addr)(instance, name)
}
}
@ -137,13 +127,42 @@ impl<L> FunctionPointers<L> {
&self.entry_points
}
/// Returns the highest Vulkan version that is supported for instances.
pub fn api_version(&self) -> Result<Version, OomError>
where
L: Loader,
{
// Per the Vulkan spec:
// If the vkGetInstanceProcAddr returns NULL for vkEnumerateInstanceVersion, it is a
// Vulkan 1.0 implementation. Otherwise, the application can call vkEnumerateInstanceVersion
// to determine the version of Vulkan.
unsafe {
let name = CStr::from_bytes_with_nul_unchecked(b"vkEnumerateInstanceVersion\0");
let func = self.get_instance_proc_addr(0, name.as_ptr());
if func.is_null() {
Ok(Version {
major: 1,
minor: 0,
patch: 0,
})
} else {
type Pfn = extern "system" fn(pApiVersion: *mut u32) -> vk::Result;
let func: Pfn = mem::transmute(func);
let mut api_version = 0;
check_errors(func(&mut api_version))?;
Ok(Version::from_vulkan_version(api_version))
}
}
}
/// Calls `get_instance_proc_addr` on the underlying loader.
#[inline]
pub fn get_instance_proc_addr(
&self,
instance: vk::Instance,
name: *const c_char,
) -> extern "system" fn() -> ()
) -> *const c_void
where
L: Loader,
{

View File

@ -111,27 +111,27 @@ pub use self::extensions::RawInstanceExtensions;
pub use self::instance::ApplicationInfo;
pub use self::instance::Instance;
pub use self::instance::InstanceCreationError;
pub use self::instance::MemoryHeap;
pub use self::instance::MemoryHeapsIter;
pub use self::instance::MemoryType;
pub use self::instance::MemoryTypesIter;
pub use self::instance::PhysicalDevice;
pub use self::instance::PhysicalDeviceType;
pub use self::instance::PhysicalDevicesIter;
pub use self::instance::QueueFamiliesIter;
pub use self::instance::QueueFamily;
pub use self::layers::layers_list;
pub use self::layers::LayerProperties;
pub use self::layers::LayersIterator;
pub use self::layers::LayersListError;
pub use self::limits::Limits;
pub use self::loader::LoadingError;
pub use self::physical_device::MemoryHeap;
pub use self::physical_device::MemoryHeapsIter;
pub use self::physical_device::MemoryType;
pub use self::physical_device::MemoryTypesIter;
pub use self::physical_device::PhysicalDevice;
pub use self::physical_device::PhysicalDeviceType;
pub use self::physical_device::PhysicalDevicesIter;
pub use self::physical_device::QueueFamiliesIter;
pub use self::physical_device::QueueFamily;
pub use crate::version::Version;
pub mod debug;
pub mod loader;
mod extensions;
mod instance;
mod layers;
mod limits;
pub mod loader;
mod physical_device;

View File

@ -0,0 +1,891 @@
// 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::features::{Features, FeaturesFfi};
use crate::instance::limits::Limits;
use crate::instance::{Instance, InstanceCreationError, RawInstanceExtensions};
use crate::sync::PipelineStage;
use crate::vk;
use crate::Version;
use crate::VulkanObject;
use std::ffi::CStr;
use std::ffi::CString;
use std::hash::Hash;
use std::mem;
use std::mem::MaybeUninit;
use std::ptr;
use std::sync::Arc;
pub(super) fn init_physical_devices(
instance: vk::Instance,
vk: &vk::InstancePointers,
extensions: &RawInstanceExtensions,
) -> Result<Vec<PhysicalDeviceInfos>, InstanceCreationError> {
let physical_devices: Vec<vk::PhysicalDevice> = unsafe {
let mut num = 0;
check_errors(vk.EnumeratePhysicalDevices(instance, &mut num, ptr::null_mut()))?;
let mut devices = Vec::with_capacity(num as usize);
check_errors(vk.EnumeratePhysicalDevices(instance, &mut num, devices.as_mut_ptr()))?;
devices.set_len(num as usize);
devices
};
let vk_khr_get_physical_device_properties2 =
CString::new(b"VK_KHR_get_physical_device_properties2".to_vec()).unwrap();
// Getting the properties of all physical devices.
// If possible, we use VK_KHR_get_physical_device_properties2.
let physical_devices = if extensions
.iter()
.any(|v| *v == vk_khr_get_physical_device_properties2)
{
init_physical_devices_inner2(&vk, physical_devices, &extensions)
} else {
init_physical_devices_inner(&vk, physical_devices)
};
Ok(physical_devices)
}
/// Initialize all physical devices
fn init_physical_devices_inner(
vk: &vk::InstancePointers,
physical_devices: Vec<vk::PhysicalDevice>,
) -> Vec<PhysicalDeviceInfos> {
let mut output = Vec::with_capacity(physical_devices.len());
for device in physical_devices.into_iter() {
let properties: vk::PhysicalDeviceProperties = unsafe {
let mut output = MaybeUninit::uninit();
vk.GetPhysicalDeviceProperties(device, output.as_mut_ptr());
output.assume_init()
};
let queue_families = unsafe {
let mut num = 0;
vk.GetPhysicalDeviceQueueFamilyProperties(device, &mut num, ptr::null_mut());
let mut families = Vec::with_capacity(num as usize);
vk.GetPhysicalDeviceQueueFamilyProperties(device, &mut num, families.as_mut_ptr());
families.set_len(num as usize);
families
};
let memory: vk::PhysicalDeviceMemoryProperties = unsafe {
let mut output = MaybeUninit::uninit();
vk.GetPhysicalDeviceMemoryProperties(device, output.as_mut_ptr());
output.assume_init()
};
let available_features: vk::PhysicalDeviceFeatures = unsafe {
let mut output = MaybeUninit::uninit();
vk.GetPhysicalDeviceFeatures(device, output.as_mut_ptr());
output.assume_init()
};
output.push(PhysicalDeviceInfos {
device,
properties,
extended_properties: PhysicalDeviceExtendedProperties::empty(),
memory,
queue_families,
available_features: Features::from(available_features),
});
}
output
}
/// 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(
vk: &vk::InstancePointers,
physical_devices: Vec<vk::PhysicalDevice>,
extensions: &RawInstanceExtensions,
) -> Vec<PhysicalDeviceInfos> {
let mut output = Vec::with_capacity(physical_devices.len());
for device in physical_devices.into_iter() {
let mut extended_properties = PhysicalDeviceExtendedProperties::empty();
let properties: vk::PhysicalDeviceProperties = unsafe {
let mut subgroup_properties = vk::PhysicalDeviceSubgroupProperties {
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES,
pNext: ptr::null_mut(),
subgroupSize: 0,
supportedStages: 0,
supportedOperations: 0,
quadOperationsInAllStages: 0,
};
let mut output = vk::PhysicalDeviceProperties2KHR {
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
pNext: &mut subgroup_properties,
properties: mem::zeroed(),
};
vk.GetPhysicalDeviceProperties2KHR(device, &mut output);
extended_properties = PhysicalDeviceExtendedProperties {
subgroup_size: Some(subgroup_properties.subgroupSize),
..extended_properties
};
output.properties
};
let queue_families = unsafe {
let mut num = 0;
vk.GetPhysicalDeviceQueueFamilyProperties2KHR(device, &mut num, ptr::null_mut());
let mut families = (0..num)
.map(|_| vk::QueueFamilyProperties2KHR {
sType: vk::STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR,
pNext: ptr::null_mut(),
queueFamilyProperties: mem::zeroed(),
})
.collect::<Vec<_>>();
vk.GetPhysicalDeviceQueueFamilyProperties2KHR(device, &mut num, families.as_mut_ptr());
families
.into_iter()
.map(|family| family.queueFamilyProperties)
.collect()
};
let memory: vk::PhysicalDeviceMemoryProperties = unsafe {
let mut output = vk::PhysicalDeviceMemoryProperties2KHR {
sType: vk::STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR,
pNext: ptr::null_mut(),
memoryProperties: mem::zeroed(),
};
vk.GetPhysicalDeviceMemoryProperties2KHR(device, &mut output);
output.memoryProperties
};
let available_features: Features = unsafe {
let mut output = FeaturesFfi::new();
let ptr = FeaturesFfi::mut_base_ptr(&mut output) as *mut _;
vk.GetPhysicalDeviceFeatures2KHR(device, ptr);
Features::from(&output.main)
};
output.push(PhysicalDeviceInfos {
device,
properties,
extended_properties,
memory,
queue_families,
available_features,
});
}
output
}
pub(super) struct PhysicalDeviceInfos {
device: vk::PhysicalDevice,
properties: vk::PhysicalDeviceProperties,
extended_properties: PhysicalDeviceExtendedProperties,
queue_families: Vec<vk::QueueFamilyProperties>,
memory: vk::PhysicalDeviceMemoryProperties,
available_features: Features,
}
/// Represents additional information related to Physical Devices fetched from
/// `vkGetPhysicalDeviceProperties` call. Certain features available only when
/// appropriate `Instance` extensions enabled. The core extension required
/// for this features is `InstanceExtensions::khr_get_physical_device_properties2`
///
/// TODO: Only a small subset of available properties(https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceProperties2.html) is implemented at this moment.
pub struct PhysicalDeviceExtendedProperties {
subgroup_size: Option<u32>,
}
impl PhysicalDeviceExtendedProperties {
pub(super) fn empty() -> Self {
Self {
subgroup_size: None,
}
}
/// The default number of invocations in each subgroup
///
/// See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkPhysicalDeviceSubgroupProperties.html for details
#[inline]
pub fn subgroup_size(&self) -> &Option<u32> {
&self.subgroup_size
}
}
/// 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::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// print_infos(physical_device);
/// }
///
/// fn print_infos(dev: PhysicalDevice) {
/// println!("Name: {}", dev.name());
/// }
/// ```
#[derive(Debug, Copy, Clone)]
pub struct PhysicalDevice<'a> {
instance: &'a Arc<Instance>,
device: usize,
}
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::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// println!("Available device: {}", physical_device.name());
/// }
/// ```
#[inline]
pub fn enumerate(instance: &'a Arc<Instance>) -> PhysicalDevicesIter<'a> {
PhysicalDevicesIter {
instance,
current_id: 0,
}
}
/// 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::instance::PhysicalDevice;
///
/// let instance = Instance::new(None, &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>> {
if instance.physical_devices.len() > index {
Some(PhysicalDevice {
instance,
device: index,
})
} else {
None
}
}
/// Returns the instance corresponding to this physical device.
///
/// # Example
///
/// ```no_run
/// use vulkano::instance::PhysicalDevice;
///
/// fn do_something(physical_device: PhysicalDevice) {
/// let _loaded_extensions = physical_device.instance().loaded_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.device
}
/// Returns the human-readable name of the device.
#[inline]
pub fn name(&self) -> &str {
unsafe {
let val = &self.infos().properties.deviceName;
let val = CStr::from_ptr(val.as_ptr());
val.to_str()
.expect("physical device name contained non-UTF8 characters")
}
}
/// Returns the type of the device.
///
/// # Example
///
/// ```no_run
/// # use vulkano::instance::Instance;
/// # use vulkano::instance::InstanceExtensions;
/// use vulkano::instance::PhysicalDevice;
///
/// # let instance = Instance::new(None, &InstanceExtensions::none(), None).unwrap();
/// for physical_device in PhysicalDevice::enumerate(&instance) {
/// println!("Available device: {} (type: {:?})",
/// physical_device.name(), physical_device.ty());
/// }
/// ```
#[inline]
pub fn ty(&self) -> PhysicalDeviceType {
match self.instance.physical_devices[self.device]
.properties
.deviceType
{
vk::PHYSICAL_DEVICE_TYPE_OTHER => PhysicalDeviceType::Other,
vk::PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => PhysicalDeviceType::IntegratedGpu,
vk::PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => PhysicalDeviceType::DiscreteGpu,
vk::PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU => PhysicalDeviceType::VirtualGpu,
vk::PHYSICAL_DEVICE_TYPE_CPU => PhysicalDeviceType::Cpu,
_ => panic!("Unrecognized Vulkan device type"),
}
}
/// Returns the version of Vulkan supported by this device.
#[inline]
pub fn api_version(&self) -> Version {
let val = self.infos().properties.apiVersion;
Version::from_vulkan_version(val)
}
/// Returns the Vulkan features that are supported by this physical device.
#[inline]
pub fn supported_features(&self) -> &'a Features {
&self.infos().available_features
}
/// Builds an iterator that enumerates all the queue families on this physical device.
#[inline]
pub fn queue_families(&self) -> QueueFamiliesIter<'a> {
QueueFamiliesIter {
physical_device: *self,
current_id: 0,
}
}
/// 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.infos().queue_families.len() {
Some(QueueFamily {
physical_device: *self,
id,
})
} else {
None
}
}
/// Builds an iterator that enumerates all the memory types on this physical device.
#[inline]
pub fn memory_types(&self) -> MemoryTypesIter<'a> {
MemoryTypesIter {
physical_device: *self,
current_id: 0,
}
}
/// 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.infos().memory.memoryTypeCount {
Some(MemoryType {
physical_device: *self,
id,
})
} else {
None
}
}
/// Builds an iterator that enumerates all the memory heaps on this physical device.
#[inline]
pub fn memory_heaps(&self) -> MemoryHeapsIter<'a> {
MemoryHeapsIter {
physical_device: *self,
current_id: 0,
}
}
/// 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.infos().memory.memoryHeapCount {
Some(MemoryHeap {
physical_device: *self,
id,
})
} else {
None
}
}
/// Gives access to the limits of the physical device.
///
/// This function should be zero-cost in release mode. It only exists to not pollute the
/// namespace of `PhysicalDevice` with all the limits-related getters.
#[inline]
pub fn limits(&self) -> Limits<'a> {
Limits::from_vk_limits(&self.infos().properties.limits)
}
/// Returns an opaque number representing the version of the driver of this device.
///
/// The meaning of this number is implementation-specific. It can be used in bug reports, for
/// example.
#[inline]
pub fn driver_version(&self) -> u32 {
self.infos().properties.driverVersion
}
/// Returns the PCI ID of the device.
#[inline]
pub fn pci_device_id(&self) -> u32 {
self.infos().properties.deviceID
}
/// Returns the PCI ID of the vendor.
#[inline]
pub fn pci_vendor_id(&self) -> u32 {
self.infos().properties.vendorID
}
/// Returns a unique identifier for the device.
///
/// Can be stored in a configuration file, so that you can retrieve the device again the next
/// time the program is run.
#[inline]
pub fn uuid(&self) -> &[u8; 16] {
// must be equal to vk::UUID_SIZE
&self.infos().properties.pipelineCacheUUID
}
#[inline]
pub fn extended_properties(&self) -> &PhysicalDeviceExtendedProperties {
&self.infos().extended_properties
}
// Internal function to make it easier to get the infos of this device.
#[inline]
fn infos(&self) -> &'a PhysicalDeviceInfos {
&self.instance.physical_devices[self.device]
}
}
unsafe impl<'a> VulkanObject for PhysicalDevice<'a> {
type Object = vk::PhysicalDevice;
const TYPE: vk::ObjectType = vk::OBJECT_TYPE_PHYSICAL_DEVICE;
#[inline]
fn internal_object(&self) -> vk::PhysicalDevice {
self.infos().device
}
}
/// Iterator for all the physical devices available on hardware.
#[derive(Debug, Clone)]
pub struct PhysicalDevicesIter<'a> {
instance: &'a Arc<Instance>,
current_id: usize,
}
impl<'a> Iterator for PhysicalDevicesIter<'a> {
type Item = PhysicalDevice<'a>;
#[inline]
fn next(&mut self) -> Option<PhysicalDevice<'a>> {
if self.current_id >= self.instance.physical_devices.len() {
return None;
}
let dev = PhysicalDevice {
instance: self.instance,
device: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.instance.physical_devices.len() - self.current_id;
(len, Some(len))
}
}
impl<'a> ExactSizeIterator for PhysicalDevicesIter<'a> {}
/// Type of a physical device.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
#[repr(u32)]
pub enum PhysicalDeviceType {
/// The device is an integrated GPU.
IntegratedGpu = 1,
/// The device is a discrete GPU.
DiscreteGpu = 2,
/// The device is a virtual GPU.
VirtualGpu = 3,
/// The device is a CPU.
Cpu = 4,
/// The device is something else.
Other = 0,
}
/// 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,
}
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.physical_device.infos().queue_families[self.id as usize].queueCount 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.physical_device.infos().queue_families[self.id as usize].timestampValidBits;
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.physical_device.infos().queue_families[self.id as usize]
.minImageTransferGranularity;
[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.flags() & vk::QUEUE_GRAPHICS_BIT) != 0
}
/// Returns `true` if queues of this family can execute compute operations.
#[inline]
pub fn supports_compute(&self) -> bool {
(self.flags() & vk::QUEUE_COMPUTE_BIT) != 0
}
/// 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.flags() & vk::QUEUE_TRANSFER_BIT) != 0
}
/// Returns `true` if queues of this family can execute sparse resources binding operations.
#[inline]
pub fn supports_sparse_binding(&self) -> bool {
(self.flags() & vk::QUEUE_SPARSE_BINDING_BIT) != 0
}
/// Returns `true` if the queues of this family support a particular pipeline stage.
#[inline]
pub fn supports_stage(&self, stage: PipelineStage) -> bool {
(self.flags() & stage.required_queue_flags()) != 0
}
/// Internal utility function that returns the flags of this queue family.
#[inline]
fn flags(&self) -> u32 {
self.physical_device.infos().queue_families[self.id as usize].queueFlags
}
}
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> {}
/// Iterator for all the queue families available on a physical device.
#[derive(Debug, Clone)]
pub struct QueueFamiliesIter<'a> {
physical_device: PhysicalDevice<'a>,
current_id: u32,
}
impl<'a> Iterator for QueueFamiliesIter<'a> {
type Item = QueueFamily<'a>;
#[inline]
fn next(&mut self) -> Option<QueueFamily<'a>> {
if self.current_id as usize >= self.physical_device.infos().queue_families.len() {
return None;
}
let dev = QueueFamily {
physical_device: self.physical_device,
id: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.physical_device.infos().queue_families.len();
let remain = len - self.current_id as usize;
(remain, Some(remain))
}
}
impl<'a> ExactSizeIterator for QueueFamiliesIter<'a> {}
/// Represents a memory type in a physical device.
#[derive(Debug, Copy, Clone)]
pub struct MemoryType<'a> {
physical_device: PhysicalDevice<'a>,
id: u32,
}
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> {
let heap_id = self.physical_device.infos().memory.memoryTypes[self.id as usize].heapIndex;
MemoryHeap {
physical_device: self.physical_device,
id: heap_id,
}
}
/// 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.flags() & vk::MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0
}
/// Returns true if the memory type can be accessed by the host.
#[inline]
pub fn is_host_visible(&self) -> bool {
(self.flags() & vk::MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0
}
/// 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.flags() & vk::MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0
}
/// 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.flags() & vk::MEMORY_PROPERTY_HOST_CACHED_BIT) != 0
}
/// 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.flags() & vk::MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0
}
/// Internal utility function that returns the flags of this queue family.
#[inline]
fn flags(&self) -> u32 {
self.physical_device.infos().memory.memoryTypes[self.id as usize].propertyFlags
}
}
/// Iterator for all the memory types available on a physical device.
#[derive(Debug, Clone)]
pub struct MemoryTypesIter<'a> {
physical_device: PhysicalDevice<'a>,
current_id: u32,
}
impl<'a> Iterator for MemoryTypesIter<'a> {
type Item = MemoryType<'a>;
#[inline]
fn next(&mut self) -> Option<MemoryType<'a>> {
if self.current_id >= self.physical_device.infos().memory.memoryTypeCount {
return None;
}
let dev = MemoryType {
physical_device: self.physical_device,
id: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.physical_device.infos().memory.memoryTypeCount;
let remain = (len - self.current_id) as usize;
(remain, Some(remain))
}
}
impl<'a> ExactSizeIterator for MemoryTypesIter<'a> {}
/// Represents a memory heap in a physical device.
#[derive(Debug, Copy, Clone)]
pub struct MemoryHeap<'a> {
physical_device: PhysicalDevice<'a>,
id: u32,
}
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) -> usize {
self.physical_device.infos().memory.memoryHeaps[self.id as usize].size as usize
}
/// Returns true if the heap is local to the GPU.
#[inline]
pub fn is_device_local(&self) -> bool {
let flags = self.physical_device.infos().memory.memoryHeaps[self.id as usize].flags;
(flags & vk::MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0
}
/// 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 {
let flags = self.physical_device.infos().memory.memoryHeaps[self.id as usize].flags;
(flags & vk::MEMORY_HEAP_MULTI_INSTANCE_BIT) != 0
}
}
/// Iterator for all the memory heaps available on a physical device.
#[derive(Debug, Clone)]
pub struct MemoryHeapsIter<'a> {
physical_device: PhysicalDevice<'a>,
current_id: u32,
}
impl<'a> Iterator for MemoryHeapsIter<'a> {
type Item = MemoryHeap<'a>;
#[inline]
fn next(&mut self) -> Option<MemoryHeap<'a>> {
if self.current_id >= self.physical_device.infos().memory.memoryHeapCount {
return None;
}
let dev = MemoryHeap {
physical_device: self.physical_device,
id: self.current_id,
};
self.current_id += 1;
Some(dev)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.physical_device.infos().memory.memoryHeapCount;
let remain = (len - self.current_id) as usize;
(remain, Some(remain))
}
}
impl<'a> ExactSizeIterator for MemoryHeapsIter<'a> {}

View File

@ -70,6 +70,7 @@ use std::fmt;
use std::ops::Deref;
use std::sync::Arc;
use std::sync::MutexGuard;
pub use version::Version;
#[macro_use]
mod tests;

View File

@ -9,11 +9,10 @@
// The `Version` object is reexported from the `instance` module.
use std::cmp::Ordering;
use std::fmt;
/// Represents an API version of Vulkan.
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Version {
/// Major version number.
pub major: u16,
@ -35,29 +34,6 @@ impl fmt::Display for Version {
}
}
impl PartialOrd for Version {
#[inline]
fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Version {
fn cmp(&self, other: &Version) -> Ordering {
match self.major.cmp(&other.major) {
Ordering::Equal => (),
o => return o,
};
match self.minor.cmp(&other.minor) {
Ordering::Equal => (),
o => return o,
};
self.patch.cmp(&other.patch)
}
}
impl Version {
/// Turns a version number given by Vulkan into a `Version` struct.
#[inline]