use super::instance::{ApiVersion, ExtensionProperties, InstanceInner};
use super::{Device, DeviceCreateInfo, Error, Result, check};
use crate::raw::bindings::*;
use std::ffi::CStr;
use std::sync::Arc;
#[derive(Clone)]
pub struct PhysicalDevice {
pub(crate) instance: Arc<InstanceInner>,
pub(crate) handle: VkPhysicalDevice,
}
unsafe impl Send for PhysicalDevice {}
unsafe impl Sync for PhysicalDevice {}
impl PhysicalDevice {
pub(crate) fn new(instance: Arc<InstanceInner>, handle: VkPhysicalDevice) -> Self {
Self { instance, handle }
}
pub fn raw(&self) -> VkPhysicalDevice {
self.handle
}
#[doc(hidden)]
pub fn instance(&self) -> &VkInstanceDispatchTable {
&self.instance.dispatch
}
pub fn supported_features(&self) -> VkPhysicalDeviceFeatures {
let get = self
.instance
.dispatch
.vkGetPhysicalDeviceFeatures
.expect("vkGetPhysicalDeviceFeatures is required by Vulkan 1.0");
let mut feats: VkPhysicalDeviceFeatures = unsafe { std::mem::zeroed() };
unsafe { get(self.handle, &mut feats) };
feats
}
pub fn properties(&self) -> PhysicalDeviceProperties {
let get = self
.instance
.dispatch
.vkGetPhysicalDeviceProperties
.expect("vkGetPhysicalDeviceProperties is required by Vulkan 1.0");
let mut raw: VkPhysicalDeviceProperties = unsafe { std::mem::zeroed() };
unsafe { get(self.handle, &mut raw) };
PhysicalDeviceProperties { raw }
}
pub fn queue_family_properties(&self) -> Vec<QueueFamilyProperties> {
let get = self
.instance
.dispatch
.vkGetPhysicalDeviceQueueFamilyProperties
.expect("vkGetPhysicalDeviceQueueFamilyProperties is required by Vulkan 1.0");
let mut count: u32 = 0;
unsafe { get(self.handle, &mut count, std::ptr::null_mut()) };
let mut raw: Vec<VkQueueFamilyProperties> =
vec![unsafe { std::mem::zeroed() }; count as usize];
unsafe { get(self.handle, &mut count, raw.as_mut_ptr()) };
raw.into_iter()
.map(|r| QueueFamilyProperties { raw: r })
.collect()
}
pub fn memory_properties(&self) -> MemoryProperties {
let get = self
.instance
.dispatch
.vkGetPhysicalDeviceMemoryProperties
.expect("vkGetPhysicalDeviceMemoryProperties is required by Vulkan 1.0");
let mut raw: VkPhysicalDeviceMemoryProperties = unsafe { std::mem::zeroed() };
unsafe { get(self.handle, &mut raw) };
MemoryProperties { raw }
}
pub fn create_device(&self, info: DeviceCreateInfo<'_>) -> Result<Device> {
Device::new(self, info)
}
pub fn enumerate_extension_properties(&self) -> Result<Vec<ExtensionProperties>> {
let enumerate = self
.instance
.dispatch
.vkEnumerateDeviceExtensionProperties
.ok_or(Error::MissingFunction(
"vkEnumerateDeviceExtensionProperties",
))?;
let mut count: u32 = 0;
check(unsafe {
enumerate(
self.handle,
std::ptr::null(),
&mut count,
std::ptr::null_mut(),
)
})?;
let mut raw: Vec<VkExtensionProperties> =
vec![unsafe { std::mem::zeroed() }; count as usize];
check(unsafe { enumerate(self.handle, std::ptr::null(), &mut count, raw.as_mut_ptr()) })?;
Ok(raw.into_iter().map(ExtensionProperties::from_raw).collect())
}
pub fn find_queue_family(&self, required: QueueFlags) -> Option<u32> {
self.queue_family_properties()
.iter()
.enumerate()
.find(|(_, qf)| qf.queue_flags().contains(required))
.map(|(i, _)| i as u32)
}
pub fn find_dedicated_compute_queue(&self) -> Option<u32> {
let families = self.queue_family_properties();
for (i, qf) in families.iter().enumerate() {
let flags = qf.queue_flags();
if flags.contains(QueueFlags::COMPUTE) && !flags.contains(QueueFlags::GRAPHICS) {
return Some(i as u32);
}
}
for (i, qf) in families.iter().enumerate() {
if qf.queue_flags().contains(QueueFlags::COMPUTE) {
return Some(i as u32);
}
}
None
}
pub fn find_dedicated_transfer_queue(&self) -> Option<u32> {
let families = self.queue_family_properties();
for (i, qf) in families.iter().enumerate() {
let flags = qf.queue_flags();
if flags.contains(QueueFlags::TRANSFER)
&& !flags.contains(QueueFlags::GRAPHICS)
&& !flags.contains(QueueFlags::COMPUTE)
{
return Some(i as u32);
}
}
for (i, qf) in families.iter().enumerate() {
if qf.queue_flags().contains(QueueFlags::TRANSFER) {
return Some(i as u32);
}
}
None
}
pub unsafe fn cooperative_matrix_properties(&self) -> Vec<CooperativeMatrixProperties> {
let Some(get) = self
.instance
.dispatch
.vkGetPhysicalDeviceCooperativeMatrixPropertiesKHR
else {
return Vec::new();
};
let mut count: u32 = 0;
if unsafe { get(self.handle, &mut count, std::ptr::null_mut()) } != VkResult::SUCCESS {
return Vec::new();
}
let mut raw: Vec<VkCooperativeMatrixPropertiesKHR> = (0..count as usize)
.map(|_| VkCooperativeMatrixPropertiesKHR {
sType: VkStructureType::STRUCTURE_TYPE_COOPERATIVE_MATRIX_PROPERTIES_KHR,
..Default::default()
})
.collect();
if unsafe { get(self.handle, &mut count, raw.as_mut_ptr()) } != VkResult::SUCCESS {
return Vec::new();
}
raw.into_iter()
.map(|r| CooperativeMatrixProperties { raw: r })
.collect()
}
pub fn memory_budget(&self) -> Option<MemoryBudget> {
let get2 = self
.instance
.dispatch
.vkGetPhysicalDeviceMemoryProperties2?;
let mut budget = VkPhysicalDeviceMemoryBudgetPropertiesEXT {
sType: VkStructureType::STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT,
..Default::default()
};
let mut props2 = VkPhysicalDeviceMemoryProperties2 {
sType: VkStructureType::STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2,
pNext: &mut budget as *mut _ as *mut _,
..Default::default()
};
unsafe { get2(self.handle, &mut props2) };
Some(MemoryBudget {
heap_count: props2.memoryProperties.memoryHeapCount,
budget: budget.heapBudget,
usage: budget.heapUsage,
})
}
pub fn timestamp_period(&self) -> f32 {
self.properties().timestamp_period()
}
pub fn find_memory_type(
&self,
memory_type_bits: u32,
required: super::MemoryPropertyFlags,
) -> Option<u32> {
let props = self.memory_properties();
for i in 0..props.type_count() {
let allowed = (memory_type_bits & (1 << i)) != 0;
if allowed && props.memory_type(i).property_flags().contains(required) {
return Some(i);
}
}
None
}
}
#[derive(Clone)]
pub struct PhysicalDeviceProperties {
raw: VkPhysicalDeviceProperties,
}
impl PhysicalDeviceProperties {
pub fn api_version(&self) -> ApiVersion {
ApiVersion(self.raw.apiVersion)
}
pub fn driver_version(&self) -> u32 {
self.raw.driverVersion
}
pub fn vendor_id(&self) -> u32 {
self.raw.vendorID
}
pub fn device_id(&self) -> u32 {
self.raw.deviceID
}
pub fn device_type(&self) -> PhysicalDeviceType {
PhysicalDeviceType(self.raw.deviceType)
}
pub fn timestamp_period(&self) -> f32 {
self.raw.limits.timestampPeriod
}
pub fn max_push_constants_size(&self) -> u32 {
self.raw.limits.maxPushConstantsSize
}
pub fn device_name(&self) -> String {
unsafe {
CStr::from_ptr(self.raw.deviceName.as_ptr())
.to_string_lossy()
.into_owned()
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PhysicalDeviceType(pub VkPhysicalDeviceType);
impl PhysicalDeviceType {
pub const OTHER: Self = Self(VkPhysicalDeviceType::PHYSICAL_DEVICE_TYPE_OTHER);
pub const INTEGRATED_GPU: Self =
Self(VkPhysicalDeviceType::PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU);
pub const DISCRETE_GPU: Self = Self(VkPhysicalDeviceType::PHYSICAL_DEVICE_TYPE_DISCRETE_GPU);
pub const VIRTUAL_GPU: Self = Self(VkPhysicalDeviceType::PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU);
pub const CPU: Self = Self(VkPhysicalDeviceType::PHYSICAL_DEVICE_TYPE_CPU);
}
impl std::fmt::Debug for PhysicalDeviceProperties {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PhysicalDeviceProperties")
.field("device_name", &self.device_name())
.field("device_type", &self.device_type())
.field("api_version", &self.api_version())
.field("driver_version", &self.driver_version())
.field("vendor_id", &self.vendor_id())
.field("device_id", &self.device_id())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct QueueFlags(pub u32);
impl QueueFlags {
pub const GRAPHICS: Self = Self(0x1);
pub const COMPUTE: Self = Self(0x2);
pub const TRANSFER: Self = Self(0x4);
pub const SPARSE_BINDING: Self = Self(0x8);
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
}
impl std::ops::BitOr for QueueFlags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
#[derive(Clone)]
pub struct QueueFamilyProperties {
raw: VkQueueFamilyProperties,
}
impl QueueFamilyProperties {
pub fn queue_flags(&self) -> QueueFlags {
QueueFlags(self.raw.queueFlags)
}
pub fn queue_count(&self) -> u32 {
self.raw.queueCount
}
pub fn timestamp_valid_bits(&self) -> u32 {
self.raw.timestampValidBits
}
}
impl std::fmt::Debug for QueueFamilyProperties {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("QueueFamilyProperties")
.field("queue_flags", &self.queue_flags())
.field("queue_count", &self.queue_count())
.finish()
}
}
#[derive(Clone)]
pub struct MemoryProperties {
raw: VkPhysicalDeviceMemoryProperties,
}
impl MemoryProperties {
pub fn type_count(&self) -> u32 {
self.raw.memoryTypeCount
}
pub fn heap_count(&self) -> u32 {
self.raw.memoryHeapCount
}
pub fn memory_type(&self, index: u32) -> MemoryType {
MemoryType {
raw: self.raw.memoryTypes[index as usize],
}
}
pub fn memory_heap(&self, index: u32) -> MemoryHeap {
MemoryHeap {
raw: self.raw.memoryHeaps[index as usize],
}
}
}
#[derive(Clone)]
pub struct MemoryType {
raw: VkMemoryType,
}
impl MemoryType {
pub fn property_flags(&self) -> super::MemoryPropertyFlags {
super::MemoryPropertyFlags(self.raw.propertyFlags)
}
pub fn heap_index(&self) -> u32 {
self.raw.heapIndex
}
}
#[derive(Clone)]
pub struct MemoryHeap {
raw: VkMemoryHeap,
}
impl MemoryHeap {
pub fn size(&self) -> u64 {
self.raw.size
}
pub fn flags(&self) -> MemoryHeapFlags {
MemoryHeapFlags(self.raw.flags)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MemoryHeapFlags(pub u32);
impl MemoryHeapFlags {
pub const DEVICE_LOCAL: Self = Self(0x1);
pub const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
}
#[derive(Debug, Clone)]
pub struct MemoryBudget {
pub heap_count: u32,
pub budget: [u64; 16],
pub usage: [u64; 16],
}
impl MemoryBudget {
pub fn total_budget(&self) -> u64 {
self.budget[..self.heap_count as usize].iter().sum()
}
pub fn total_usage(&self) -> u64 {
self.usage[..self.heap_count as usize].iter().sum()
}
}
#[derive(Clone)]
#[allow(dead_code)] pub struct PhysicalDeviceGroup {
pub(crate) instance: Arc<InstanceInner>,
pub(crate) physical_devices: Vec<PhysicalDevice>,
pub(crate) subset_allocation: bool,
}
impl PhysicalDeviceGroup {
pub fn physical_devices(&self) -> &[PhysicalDevice] {
&self.physical_devices
}
pub fn count(&self) -> u32 {
self.physical_devices.len() as u32
}
pub fn supports_subset_allocation(&self) -> bool {
self.subset_allocation
}
pub fn create_device(&self, info: DeviceCreateInfo<'_>) -> Result<Device> {
Device::new_group(self, info)
}
}
impl std::fmt::Debug for PhysicalDeviceGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PhysicalDeviceGroup")
.field("count", &self.count())
.field("subset_allocation", &self.subset_allocation)
.finish()
}
}
#[derive(Clone)]
pub struct CooperativeMatrixProperties {
raw: VkCooperativeMatrixPropertiesKHR,
}
impl CooperativeMatrixProperties {
pub fn m_size(&self) -> u32 {
self.raw.MSize
}
pub fn n_size(&self) -> u32 {
self.raw.NSize
}
pub fn k_size(&self) -> u32 {
self.raw.KSize
}
pub fn a_type(&self) -> VkComponentTypeKHR {
self.raw.AType
}
pub fn b_type(&self) -> VkComponentTypeKHR {
self.raw.BType
}
pub fn c_type(&self) -> VkComponentTypeKHR {
self.raw.CType
}
pub fn result_type(&self) -> VkComponentTypeKHR {
self.raw.ResultType
}
pub fn saturating_accumulation(&self) -> bool {
self.raw.saturatingAccumulation != 0
}
pub fn scope(&self) -> VkScopeKHR {
self.raw.scope
}
}
impl std::fmt::Debug for CooperativeMatrixProperties {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CooperativeMatrixProperties")
.field("M", &self.m_size())
.field("N", &self.n_size())
.field("K", &self.k_size())
.field("AType", &self.a_type())
.field("BType", &self.b_type())
.field("CType", &self.c_type())
.field("ResultType", &self.result_type())
.finish()
}
}
#[allow(dead_code)]
fn _ensure_error_is_used(_: Error) {}