diff --git a/frameworks/vulkan/src/alloc.rs b/frameworks/vulkan/src/alloc.rs new file mode 100644 index 0000000..e02d48c --- /dev/null +++ b/frameworks/vulkan/src/alloc.rs @@ -0,0 +1,181 @@ +//! This alloc provides base definitions for allocator functionality. +use std::alloc::Layout; +use std::ffi; +use std::ptr::NonNull; + +use crate::c; + +/// Represents an allocation Scope that may be requested, so that data can appear +/// on separate heaps. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Scope { + /// The allocation is for Command objects + Command, + /// The allocation is for Objects + Object, + /// The allocation is for Catch objects + Cache, + /// The allocation is for Device objects + Device, + /// The allocation is for Instance objects + Instance, +} + +impl Scope { + fn from_c(scope: c::VkSystemAllocationScope) -> Scope { + match scope { + c::VkSystemAllocationScope_VK_SYSTEM_ALLOCATION_SCOPE_COMMAND => Scope::Command, + c::VkSystemAllocationScope_VK_SYSTEM_ALLOCATION_SCOPE_OBJECT => Scope::Object, + c::VkSystemAllocationScope_VK_SYSTEM_ALLOCATION_SCOPE_CACHE => Scope::Cache, + c::VkSystemAllocationScope_VK_SYSTEM_ALLOCATION_SCOPE_DEVICE => Scope::Device, + c::VkSystemAllocationScope_VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE => Scope::Instance, + _ => unreachable!(), + } + } +} + +/// The primary trait to define the allocation mechanisms in the Vulkan API. +/// +/// Types implementing this can be converted easily into [`VkAllocationCallbacks`] +/// +/// # Safety +/// +/// This trait requires the semantics of an allocator, in that an allocation +/// must always be freeable by a call to deallocate. +/// +/// [`VkAllocationCallbacks`] c::VkAllocationCallbacks +pub unsafe trait Allocator { + /// The error type that may be returned + type Error; + + /// Requests Layout memory from the underlying system to be used with the + /// given allocation scope + /// + /// # Arguments + /// + /// * `layout` - the layout to allocate + /// * `scope` - the amount requested to allocate + fn allocate(&mut self, layout: Layout, scope: Scope) -> Result, Self::Error>; + + /// Requests to deallocate the bytes at the given address + /// + /// # Arguments + /// + /// * `data` - the bytes to deallocate + /// + /// # Safety + /// + /// * `data` must denote a block of memory currently allocated via this allocator + unsafe fn deallocate(&mut self, data: *mut u8); + + /// Requests that a given pointer be reallocated to align to the new layout + /// and within the given scope. + /// + /// # Arguments + /// + /// * `ptr` - the address to reallocate + /// * `new_layout` - the new layout to allocate at this pointer + /// * `scope` - the new scope this allocation should be allocated to + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result if the + /// caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// * `new_layout` must be greater than zero + unsafe fn reallocate( + &mut self, + ptr: *mut u8, + new_layout: Layout, + new_scope: Scope, + ) -> Result, Self::Error>; +} + +/// A collection of allocation callbacks that can be used within Vulkan. +#[derive(Clone)] +pub struct AllocationCallbacks(c::VkAllocationCallbacks); + +impl AllocationCallbacks { + /// Constructs a new set of [`AllocationCallbacks`] from an underlying + /// `allocator` object. + pub fn new(allocator: &mut T) -> Self { + Self(c::VkAllocationCallbacks { + pUserData: allocator as *mut _ as *mut ffi::c_void, + pfnAllocation: Some(vulkan_alloc_fn::), + pfnReallocation: Some(vulkan_realloc_fn::), + pfnFree: Some(vulkan_dealloc_fn::), + pfnInternalAllocation: None, + pfnInternalFree: None, + }) + } + + /// Constructs the [`AllocationCallbacks`] object from the raw underlying C + /// API. + /// + /// # Arguments + /// + /// * `callbacks` - the callbacks to user internally. + pub const fn from_raw(callbacks: c::VkAllocationCallbacks) -> Self { + Self(callbacks) + } + + /// Returns a pointer to the internal [`VkAllocationCallbacks`] + /// + /// [`VkAllocationCallbacks`]: c::VkAllocationCallbacks + pub fn as_ptr(&self) -> *const c::VkAllocationCallbacks { + &self.0 as *const c::VkAllocationCallbacks + } + + /// Returns a mutable pointer to the internal [`VkAllocationCallbacks`] + /// + /// [`VkAllocationCallbacks`]: c::VkAllocationCallbacks + pub fn as_ptr_mut(&self) -> *const c::VkAllocationCallbacks { + &self.0 as *const c::VkAllocationCallbacks + } +} + +unsafe extern "C" fn vulkan_alloc_fn( + data: *mut ffi::c_void, + size: usize, + alignment: usize, + scope: c::VkSystemAllocationScope, +) -> *mut ffi::c_void { + let allocator: &mut T = unsafe { &mut *(data as *mut T) }; + let maybe_layout = Layout::from_size_align(size, alignment); + + let layout = match maybe_layout { + Ok(layout) => layout, + Err(_) => return std::ptr::null_mut(), + }; + + match allocator.allocate(layout, Scope::from_c(scope)) { + Ok(ptr) => ptr.as_ptr() as *mut ffi::c_void, + Err(_) => std::ptr::null_mut(), + } +} + +unsafe extern "C" fn vulkan_dealloc_fn( + data: *mut ffi::c_void, + memory: *mut ffi::c_void, +) { + let allocator: &mut T = unsafe { &mut *(data as *mut T) }; + + allocator.deallocate(memory as *mut u8); +} + +unsafe extern "C" fn vulkan_realloc_fn( + data: *mut ffi::c_void, + ptr: *mut ffi::c_void, + size: usize, + alignment: usize, + scope: c::VkSystemAllocationScope, +) -> *mut ffi::c_void { + let allocator: &mut T = unsafe { &mut *(data as *mut T) }; + let layout = Layout::from_size_align(size, alignment).expect("Invalid alignment or size"); + + match allocator.reallocate(ptr as *mut u8, layout, Scope::from_c(scope)) { + Ok(ptr) => ptr.as_ptr() as *mut ffi::c_void, + Err(_) => std::ptr::null_mut(), + } +} diff --git a/frameworks/vulkan/src/debug.rs b/frameworks/vulkan/src/debug.rs new file mode 100644 index 0000000..33988f6 --- /dev/null +++ b/frameworks/vulkan/src/debug.rs @@ -0,0 +1,19 @@ +//! The [`debug`] module provides utilities for working with the Vulkan +//! debugging layer extensions +//! +//! [`debug`]: crate::debug + +// use crate::c; + +// #[repr(transparent)] +// struct CallbackData(c::VkDebugUtilsMessengerCallbackDataEXT); + +// unsafe impl foundation::Transparent for CallbackData { +// type Wrapped = CallbackData; +// } + +// pub trait DebugHook {} + +// pub trait PerformanceHook {} + +// pub trait GeneralHook {} diff --git a/frameworks/vulkan/src/device.rs b/frameworks/vulkan/src/device.rs new file mode 100644 index 0000000..07af674 --- /dev/null +++ b/frameworks/vulkan/src/device.rs @@ -0,0 +1,7 @@ +//! This module provides wrappers for both logical and physical Vulkan devices +//! (e.g. [`VkPhysicalDevice`]) +//! +//! [`VkPhysicalDevice`]: crate::c::VkPhysicalDevice + +pub mod logical; +pub mod physical; diff --git a/frameworks/vulkan/src/device/logical.rs b/frameworks/vulkan/src/device/logical.rs new file mode 100644 index 0000000..3c4f45c --- /dev/null +++ b/frameworks/vulkan/src/device/logical.rs @@ -0,0 +1,272 @@ +//! This module provides wrappers for logical Vulkan devices + +use crate::c; +use crate::queue::Queue; +use std::ffi::{c_char, CStr, CString}; + +use crate::device::physical::PhysicalDeviceFeatures; +use crate::{Error, Result}; + +use super::physical::PhysicalDevice; + +struct FamilyInfo { + queue_family_index: u32, + priorities: Vec, +} + +/// A builder for logical devices +pub struct DeviceBuilder { + extensions: Vec, + layers: Vec, + queue_families: Vec, + features: PhysicalDeviceFeatures, +} + +impl DeviceBuilder { + /// Constructs a builder object. + pub fn new() -> Self { + Self { + extensions: Default::default(), + layers: Default::default(), + queue_families: Default::default(), + features: unsafe { std::mem::zeroed::() }, + } + } + + /// Adds a queue family to the logical device being created. + /// + /// # Arguments + /// + /// * `queue_family_index` - the queue family index to request + /// * `priorities` - the list of priorities per queue. + pub fn queue_family(mut self, queue_family_index: u32, priorities: &[f32]) -> Self { + self.queue_families.push(FamilyInfo { + queue_family_index, + priorities: priorities.iter().copied().collect(), + }); + self + } + + /// Adds a queue family to the logical device being created. + /// + /// # Arguments + /// + /// * `queue_family_index` - the queue family index to request + pub fn single_queue_family(mut self, queue_family_index: u32) -> Self { + self.queue_families.push(FamilyInfo { + queue_family_index, + priorities: vec![1.0], + }); + self + } + + /// Adds a single extension to the current instance being built. + /// + /// # Arguments + /// + /// * `extension` - the extension str to add. + pub fn extension(mut self, extension: &str) -> Self { + self + .extensions + .push(foundation::cstring_from_str(extension)); + self + } + + /// Adds multiple extensions to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of str extension names. + pub fn extensions<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self + .extensions + .extend(it.map(|s| foundation::cstring_from_str(s))); + self + } + + /// Adds a single extension to the current instance being built. + /// + /// # Arguments + /// + /// * `extension` - the extension cstr to add. + pub fn extension_cstr(mut self, extension: &CStr) -> Self { + self.extensions.push(extension.to_owned()); + self + } + + /// Adds multiple extensions to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of cstr extension names. + pub fn extensions_cstr<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self.extensions.extend(it.map(|s| (*s).to_owned())); + self + } + + /// Adds a single layer to the current instance being built. + /// + /// # Arguments + /// + /// * `layer` - the layer str to add. + pub fn layer(mut self, layer: &str) -> Self { + self.layers.push(foundation::cstring_from_str(layer)); + self + } + + /// Adds a multiple layers to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of str layer names. + pub fn layers<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self + .layers + .extend(it.map(|s| foundation::cstring_from_str(s))); + self + } + + /// Adds a single layer to the current instance being built. + /// + /// # Arguments + /// + /// * `layer` - the layer cstr to add. + pub fn layer_cstr(mut self, layer: &CStr) -> Self { + self.layers.push(layer.to_owned()); + self + } + + /// Adds a multiple layers to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of cstr layer names. + pub fn layers_cstr<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self.layers.extend(it.map(|s| s.to_owned())); + self + } + + /// Sets the enabled features + /// + /// # Arguments + /// + /// * `features` - the features to enable + pub fn enabled_features(mut self, features: PhysicalDeviceFeatures) -> Self { + self.features = features; + self + } + + /// Builds this + pub fn build<'a, 'b>(self, physical_device: &'a PhysicalDevice<'b>) -> Result> { + let mut queues: Vec = Default::default(); + for info in self.queue_families.iter() { + let create_info = c::VkDeviceQueueCreateInfo { + sType: c::VkStructureType_VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + flags: 0, + pNext: std::ptr::null(), + queueCount: info.priorities.len() as u32, + pQueuePriorities: info.priorities.as_ptr(), + queueFamilyIndex: info.queue_family_index, + }; + queues.push(create_info) + } + + use foundation::Transparent; + + let extensions: Vec<*const c_char> = self.extensions.iter().map(|s| s.as_ptr()).collect(); + let layers: Vec<*const c_char> = self.layers.iter().map(|s| s.as_ptr()).collect(); + let info = c::VkDeviceCreateInfo { + sType: c::VkStructureType_VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + pNext: std::ptr::null(), + flags: 0, + enabledExtensionCount: extensions.len() as u32, + ppEnabledExtensionNames: extensions.as_ptr(), + enabledLayerCount: layers.len() as u32, + ppEnabledLayerNames: layers.as_ptr(), + pQueueCreateInfos: queues.as_ptr() as *const c::VkDeviceQueueCreateInfo, + queueCreateInfoCount: queues.len() as u32, + pEnabledFeatures: self.features.as_ref() as *const c::VkPhysicalDeviceFeatures, + }; + unsafe { + let mut result = std::mem::zeroed::(); + + Error::check(c::vkCreateDevice( + *physical_device.as_ref(), + &info as *const c::VkDeviceCreateInfo, + physical_device.instance().allocation_callbacks(), + &mut result as *mut c::VkDevice, + )) + .map(|_| Device::from_c(physical_device, result)) + } + } +} + +/// A representation of a logical [`VkDevice`] +/// +/// [`VkDevice`]: c::VkDevice +pub struct Device<'a> { + physical_device: &'a PhysicalDevice<'a>, + device: c::VkDevice, +} + +impl<'a> Device<'a> { + /// Constructs this Device from the underlying [`VkDevice`] + /// + /// [`VkDevice`]: c::VkDevice + pub fn from_c(physical_device: &'a PhysicalDevice<'a>, device: c::VkDevice) -> Self { + Self { + physical_device, + device, + } + } + + /// Gets a reference to the underlying [`VkDevice`] + /// + /// [`VkDevice`]: c::VkDevice + pub fn as_ref(&self) -> &c::VkDevice { + &self.device + } + + /// Gets a mutable reference to the underlying [`VkDevice`] + /// + /// [`VkDevice`]: c::VkDevice + pub fn as_ref_mut(&mut self) -> &mut c::VkDevice { + &mut self.device + } + + /// + pub unsafe fn queue(&self, queue_family_index: u32, index: u32) -> Queue { + let mut out = std::mem::zeroed::(); + c::vkGetDeviceQueue( + self.device, + queue_family_index, + index, + &mut out as *mut c::VkQueue, + ); + + Queue::from_c(out) + } +} + +impl<'a> Drop for Device<'a> { + fn drop(&mut self) { + unsafe { + c::vkDestroyDevice( + self.device, + self.physical_device.instance().allocation_callbacks(), + ) + } + } +} diff --git a/frameworks/vulkan/src/device/physical.rs b/frameworks/vulkan/src/device/physical.rs new file mode 100644 index 0000000..b3590a4 --- /dev/null +++ b/frameworks/vulkan/src/device/physical.rs @@ -0,0 +1,2306 @@ +//! This module provides wrappers for both logical and physical Vulkan devices +//! (e.g. [`VkPhysicalDevice`]) +//! +//! [`VkPhysicalDevice`]: crate::c::VkPhysicalDevice + +use crate::{ + c, extension::ExtensionProperties, instance::Instance, queue::QueueFamilyProperties, + surface::Surface, DeviceSize, Version, +}; +use std::ffi::CStr; + +/// A wrapper around [`VkPhysicalDeviceType`] +/// +/// [`VkPhysicalDeviceType`]: c::VkPhysicalDeviceType +#[derive(Copy, Clone)] +pub enum PhysicalDeviceType { + /// The device is a standard CPU + CPU, + /// The device is a discrete GPU + DiscreteGPU, + /// The device is an integrated GPU + IntegratedGPU, + /// The device is a virtual or emulated GPU + VirtualGPU, + /// The device is some other type of device. + Other, +} + +impl std::fmt::Display for PhysicalDeviceType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::CPU => "CPU", + Self::DiscreteGPU => "Discrete GPU", + Self::IntegratedGPU => "Integrated GPU", + Self::VirtualGPU => "Virtual GPU", + Self::Other => "Other", + } + .fmt(f) + } +} + +impl PhysicalDeviceType { + /// Constructs this [`PhysicalDeviceType`] from an instance of [`VkPhysicalDeviceType`]. + /// + /// # Arguments + /// + /// * `t` - the underlying type to use + pub fn from_c(t: c::VkPhysicalDeviceType) -> Self { + match t { + c::VkPhysicalDeviceType_VK_PHYSICAL_DEVICE_TYPE_CPU => Self::CPU, + c::VkPhysicalDeviceType_VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => Self::DiscreteGPU, + c::VkPhysicalDeviceType_VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => Self::IntegratedGPU, + c::VkPhysicalDeviceType_VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU => Self::VirtualGPU, + _ => Self::Other, + } + } +} + +/// A wrapper around [`VkPhysicalDevice`] objects. +/// +/// [`VkPhysicalDevice`]: c::VkPhysicalDevice +pub struct PhysicalDevice<'a> { + device: c::VkPhysicalDevice, + instance: &'a Instance, +} + +impl<'a> PhysicalDevice<'a> { + /// Constructs this [`PhysicalDevice`] from the underlying [`VkPhysicalDevice`] + /// + /// # Panics + /// + /// This function will [`panic`] if the `device` is null. + /// + /// # Arguments + /// + /// * `device` - the physical device + /// + /// [`VkPhysicalDevice`]: c::VkPhysicalDevice + pub fn from_c(instance: &'a Instance, device: c::VkPhysicalDevice) -> Self { + debug_assert!(device != std::ptr::null_mut()); + Self { device, instance } + } + + /// Returns the underlying [`PhysicalDeviceProperties`] of this device. + pub fn properties(&self) -> PhysicalDeviceProperties { + let mut out = unsafe { std::mem::zeroed::() }; + unsafe { + c::vkGetPhysicalDeviceProperties(self.device, &mut out as *mut c::VkPhysicalDeviceProperties) + } + + PhysicalDeviceProperties::from_c(out) + } + + /// Returns the underlying [`PhysicalDeviceFeatures`] of this device. + pub fn features(&self) -> PhysicalDeviceFeatures { + let mut out = unsafe { std::mem::zeroed::() }; + unsafe { + c::vkGetPhysicalDeviceFeatures(self.device, &mut out as *mut c::VkPhysicalDeviceFeatures) + } + + PhysicalDeviceFeatures::from_c(out) + } + + /// Returns the list of queue family properties for this device. + pub fn queue_family_properties(&self) -> Vec { + let mut count: u32 = 0; + unsafe { + c::vkGetPhysicalDeviceQueueFamilyProperties( + self.device, + &mut count as *mut u32, + std::ptr::null_mut(), + ); + + foundation::read_transparent_out_vec(count as usize, |p| { + let mut count = count; + c::vkGetPhysicalDeviceQueueFamilyProperties(self.device, &mut count as *mut u32, p); + p + }) + } + } + + /// Checks for surface support with the given queue family index. + pub fn surface_support(&self, queue_family_index: usize, surface: &Surface<'_>) -> bool { + let mut out = crate::Bool32::new(false); + use foundation::Transparent; + + unsafe { + c::vkGetPhysicalDeviceSurfaceSupportKHR( + self.device, + queue_family_index as u32, + *surface.as_ref(), + out.as_ref_mut() as *mut c::VkBool32, + ) + }; + out == true + } + + /// Queries available device extensions. + pub fn device_extensions(&self) -> Vec { + let mut count: u32 = 0; + unsafe { + c::vkEnumerateDeviceExtensionProperties( + self.device, + std::ptr::null(), + &mut count as *mut u32, + std::ptr::null_mut(), + ); + let out = foundation::read_transparent_out_vec(count as usize, |v| { + let mut count = count; + c::vkEnumerateDeviceExtensionProperties( + self.device, + std::ptr::null(), + &mut count as *mut u32, + v, + ); + v + }); + + out + } + } + + /// Gets a reference to the underlying [`VkPhysicalDevice`]. + /// + /// [`VkPhysicalDevice`]: c::VkPhysicalDevice + pub fn as_ref(&self) -> &c::VkPhysicalDevice { + &self.device + } + + /// Gets a mutable reference to the underlying [`VkPhysicalDevice`]. + /// + /// [`VkPhysicalDevice`]: c::VkPhysicalDevice + pub fn as_ref_mut(&mut self) -> &c::VkPhysicalDevice { + &mut self.device + } + + /// Gets the underlying Vulkan instance from the associated device. + pub fn instance(&self) -> &Instance { + self.instance + } +} + +/// A wrapper around [`VkPhysicalDeviceProperties`] objects. +/// +/// [`VkPhysicalDeviceProperties`]: c::VkPhysicalDeviceProperties +#[repr(transparent)] +pub struct PhysicalDeviceProperties(c::VkPhysicalDeviceProperties); + +unsafe impl foundation::Transparent for PhysicalDeviceProperties { + type Wrapped = c::VkPhysicalDeviceProperties; +} + +impl PhysicalDeviceProperties { + /// Constructs this [`PhysicalDeviceProperties`] from the underlying [`VkPhysicalDeviceProperties`] + /// + /// # Arguments + /// + /// * `device` - the physical device + /// + /// [`VkPhysicalDeviceProperties`]: c::VkPhysicalDeviceProperties + #[inline] + pub fn from_c(properties: c::VkPhysicalDeviceProperties) -> Self { + Self(properties) + } + + /// Gets the UUID of the pipeline cache. + #[inline] + pub fn pipeline_cache_uuid(&self) -> foundation::Uuid { + foundation::Uuid::from_bytes(self.0.pipelineCacheUUID) + } + + /// Gets the name of this device as a [`str`]. + #[inline] + pub fn name(&self) -> &str { + // SAFETY: Vulkan strings are defined in terms of UTF-8 strings. + unsafe { self.name_cstr().to_str().unwrap_unchecked() } + } + + /// Gets the name of this device as a [`CStr`]. + #[inline] + pub fn name_cstr(&self) -> &CStr { + // SAFETY: Vulkan API is required to provide null-terminated strings. + unsafe { foundation::cstr_from_char_slice(&self.0.deviceName) } + } + + /// Gets the API version of this device. + #[inline] + pub fn api_version(&self) -> Version { + Version::from_c(self.0.apiVersion) + } + + /// Gets the driver version of this device. + #[inline] + pub fn driver_version(&self) -> Version { + Version::from_c(self.0.driverVersion) + } + + /// Gets the vendor ID of this device. + #[inline] + pub fn vendor_id(&self) -> u32 { + self.0.vendorID + } + + /// Gets the device ID of this device. + #[inline] + pub fn device_id(&self) -> u32 { + self.0.deviceID + } + + /// Gets the underlying device type. + #[inline] + pub fn device_type(&self) -> PhysicalDeviceType { + PhysicalDeviceType::from_c(self.0.deviceType) + } + + /// Gets the underlying device limits. + #[inline] + pub fn limits(&self) -> &PhysicalDeviceLimits { + ::from_ref(&self.0.limits) + } + + /// Gets the underlying sparse properties. + #[inline] + pub fn sparse_properties(&self) -> &PhysicalDeviceSparseProperties { + ::from_ref(&self.0.sparseProperties) + } +} + +/// A wrapper around [`VkPhysicalDeviceFeatures`] objects. +/// +/// [`VkPhysicalDeviceFeatures`]: c::VkPhysicalDeviceFeatures +#[repr(transparent)] +pub struct PhysicalDeviceFeatures(c::VkPhysicalDeviceFeatures); + +unsafe impl foundation::Transparent for PhysicalDeviceFeatures { + type Wrapped = c::VkPhysicalDeviceFeatures; +} + +impl PhysicalDeviceFeatures { + /// Constructs this [`VkPhysicalDeviceFeatures`] from the underlying [`VkPhysicalDeviceFeatures`] + /// + /// # Arguments + /// + /// * `device` - the physical device + /// + /// [`VkPhysicalDeviceFeatures`]: c::VkPhysicalDeviceFeatures + #[inline] + pub fn from_c(properties: c::VkPhysicalDeviceFeatures) -> Self { + Self(properties) + } + + /// Checks whether robust buffer access is supported on this device. + #[inline] + pub fn robust_buffer_access(&self) -> bool { + self.0.robustBufferAccess != 0 + } + + /// Checks whether full draw index with uint32 is supported on this device. + #[inline] + pub fn full_draw_index_uint32(&self) -> bool { + self.0.fullDrawIndexUint32 != 0 + } + + /// Checks whether image cube array is supported on this device. + #[inline] + pub fn image_cube_array(&self) -> bool { + self.0.imageCubeArray != 0 + } + + /// Checks whether independent blend is supported on this device. + #[inline] + pub fn independent_blend(&self) -> bool { + self.0.independentBlend != 0 + } + + /// Checks whether geometry shader is supported on this device. + #[inline] + pub fn geometry_shader(&self) -> bool { + self.0.geometryShader != 0 + } + + /// Checks whether tessellation shader is supported on this device. + #[inline] + pub fn tessellation_shader(&self) -> bool { + self.0.tessellationShader != 0 + } + + /// Checks whether sample rate shading is supported on this device. + #[inline] + pub fn sample_rate_shading(&self) -> bool { + self.0.sampleRateShading != 0 + } + + /// Checks whether dual src blend is supported on this device. + #[inline] + pub fn dual_src_blend(&self) -> bool { + self.0.dualSrcBlend != 0 + } + + /// Checks whether logic op is supported on this device. + #[inline] + pub fn logic_op(&self) -> bool { + self.0.logicOp != 0 + } + + /// Checks whether multi draw indirect is supported on this device. + #[inline] + pub fn multi_draw_indirect(&self) -> bool { + self.0.multiDrawIndirect != 0 + } + + /// Checks whether draw indirect first instance is supported on this device. + #[inline] + pub fn draw_indirect_first_instance(&self) -> bool { + self.0.drawIndirectFirstInstance != 0 + } + + /// Checks whether depth clamp is supported on this device. + #[inline] + pub fn depth_clamp(&self) -> bool { + self.0.depthClamp != 0 + } + + /// Checks whether depth bias clamp is supported on this device. + #[inline] + pub fn depth_bias_clamp(&self) -> bool { + self.0.depthBiasClamp != 0 + } + + /// Checks whether fill mode non-solid is supported on this device. + #[inline] + pub fn fill_mode_non_solid(&self) -> bool { + self.0.fillModeNonSolid != 0 + } + + /// Checks whether depth bounds is supported on this device. + #[inline] + pub fn depth_bounds(&self) -> bool { + self.0.depthBounds != 0 + } + + /// Checks whether wide lines is supported on this device. + #[inline] + pub fn wide_lines(&self) -> bool { + self.0.wideLines != 0 + } + + /// Checks whether large points is supported on this device. + #[inline] + pub fn large_points(&self) -> bool { + self.0.largePoints != 0 + } + + /// Checks whether alpha to one is supported on this device. + #[inline] + pub fn alpha_to_one(&self) -> bool { + self.0.alphaToOne != 0 + } + + /// Checks whether multi viewport is supported on this device. + #[inline] + pub fn multi_viewport(&self) -> bool { + self.0.multiViewport != 0 + } + + /// Checks whether sampler anisotropy is supported on this device. + #[inline] + pub fn sampler_anisotropy(&self) -> bool { + self.0.samplerAnisotropy != 0 + } + + /// Checks whether texture compression 'etc2' is supported on this device. + #[inline] + pub fn texture_compression_etc2(&self) -> bool { + self.0.textureCompressionETC2 != 0 + } + + /// Checks whether texture compression 'astc ldr' is supported on this device. + #[inline] + pub fn texture_compression_astc_ldr(&self) -> bool { + self.0.textureCompressionASTC_LDR != 0 + } + + /// Checks whether texture compression 'bc' is supported on this device. + #[inline] + pub fn texture_compression_bc(&self) -> bool { + self.0.textureCompressionBC != 0 + } + + /// Checks whether occlusion query precise is supported on this device. + #[inline] + pub fn occlusion_query_precise(&self) -> bool { + self.0.occlusionQueryPrecise != 0 + } + + /// Checks whether pipeline statistics query is supported on this device. + #[inline] + pub fn pipeline_statistics_query(&self) -> bool { + self.0.pipelineStatisticsQuery != 0 + } + + /// Checks whether vertexpipeline stores and atomics is supported on this device. + #[inline] + pub fn vertex_pipeline_stores_and_atomics(&self) -> bool { + self.0.vertexPipelineStoresAndAtomics != 0 + } + + /// Checks whether fragment stores and atomics is supported on this device. + #[inline] + pub fn fragment_stores_and_atomics(&self) -> bool { + self.0.fragmentStoresAndAtomics != 0 + } + + /// Checks whether shader tessellation and geometry point size is supported on this device. + #[inline] + pub fn shader_tessellation_and_geometry_point_size(&self) -> bool { + self.0.shaderTessellationAndGeometryPointSize != 0 + } + + /// Checks whether shader image gather extended is supported on this device. + #[inline] + pub fn shader_image_gather_extended(&self) -> bool { + self.0.shaderImageGatherExtended != 0 + } + + /// Checks whether shader storage image extended formats is supported on this device. + #[inline] + pub fn shader_storage_image_extended_formats(&self) -> bool { + self.0.shaderStorageImageExtendedFormats != 0 + } + + /// Checks whether shader_storage_image_multisample is supported on this device. + #[inline] + pub fn shader_storage_image_multisample(&self) -> bool { + self.0.shaderStorageImageMultisample != 0 + } + + /// Checks whether shader_storage_image_read_without_format is supported on this device. + #[inline] + pub fn shader_storage_image_read_without_format(&self) -> bool { + self.0.shaderStorageImageReadWithoutFormat != 0 + } + + /// Checks whether shader_storage_image_write_without_format is supported on this device. + #[inline] + pub fn shader_storage_image_write_without_format(&self) -> bool { + self.0.shaderStorageImageWriteWithoutFormat != 0 + } + + /// Checks whether shader_uniform_buffer_array_dynamic_indexing is supported on this device. + #[inline] + pub fn shader_uniform_buffer_array_dynamic_indexing(&self) -> bool { + self.0.shaderUniformBufferArrayDynamicIndexing != 0 + } + + /// Checks whether shader_sampled_image_array_dynamic_indexing is supported on this device. + #[inline] + pub fn shader_sampled_image_array_dynamic_indexing(&self) -> bool { + self.0.shaderSampledImageArrayDynamicIndexing != 0 + } + + /// Checks whether shader_storage_buffer_array_dynamic_indexing is supported on this device. + #[inline] + pub fn shader_storage_buffer_array_dynamic_indexing(&self) -> bool { + self.0.shaderStorageBufferArrayDynamicIndexing != 0 + } + + /// Checks whether shader_storage_image_array_dynamic_indexing is supported on this device. + #[inline] + pub fn shader_storage_image_array_dynamic_indexing(&self) -> bool { + self.0.shaderStorageImageArrayDynamicIndexing != 0 + } + + /// Checks whether shader_clip_distance is supported on this device. + #[inline] + pub fn shader_clip_distance(&self) -> bool { + self.0.shaderClipDistance != 0 + } + + /// Checks whether shader_cull_distance is supported on this device. + #[inline] + pub fn shader_cull_distance(&self) -> bool { + self.0.shaderCullDistance != 0 + } + + /// Checks whether shader_float64 is supported on this device. + #[inline] + pub fn shader_float64(&self) -> bool { + self.0.shaderFloat64 != 0 + } + + /// Checks whether shader_int64 is supported on this device. + #[inline] + pub fn shader_int64(&self) -> bool { + self.0.shaderInt64 != 0 + } + + /// Checks whether shader_int16 is supported on this device. + #[inline] + pub fn shader_int16(&self) -> bool { + self.0.shaderInt16 != 0 + } + + /// Checks whether shader_resource_residency is supported on this device. + #[inline] + pub fn shader_resource_residency(&self) -> bool { + self.0.shaderResourceResidency != 0 + } + + /// Checks whether shader_resource_min_lod is supported on this device. + #[inline] + pub fn shader_resource_min_lod(&self) -> bool { + self.0.shaderResourceMinLod != 0 + } + + /// Checks whether sparse_binding is supported on this device. + #[inline] + pub fn sparse_binding(&self) -> bool { + self.0.sparseBinding != 0 + } + + /// Checks whether sparse_residency_buffer is supported on this device. + #[inline] + pub fn sparse_residency_buffer(&self) -> bool { + self.0.sparseResidencyBuffer != 0 + } + + /// Checks whether sparse_residency_image2d is supported on this device. + #[inline] + pub fn sparse_residency_image2d(&self) -> bool { + self.0.sparseResidencyImage2D != 0 + } + + /// Checks whether sparse_residency_image3d is supported on this device. + #[inline] + pub fn sparse_residency_image3d(&self) -> bool { + self.0.sparseResidencyImage3D != 0 + } + + /// Checks whether sparse_residency2_samples is supported on this device. + #[inline] + pub fn sparse_residency2_samples(&self) -> bool { + self.0.sparseResidency2Samples != 0 + } + + /// Checks whether sparse_residency4_samples is supported on this device. + #[inline] + pub fn sparse_residency4_samples(&self) -> bool { + self.0.sparseResidency4Samples != 0 + } + + /// Checks whether sparse_residency8_samples is supported on this device. + #[inline] + pub fn sparse_residency8_samples(&self) -> bool { + self.0.sparseResidency8Samples != 0 + } + + /// Checks whether sparse_residency16_samples is supported on this device. + #[inline] + pub fn sparse_residency16_samples(&self) -> bool { + self.0.sparseResidency16Samples != 0 + } + + /// Checks whether sparse_residency_aliased is supported on this device. + #[inline] + pub fn sparse_residency_aliased(&self) -> bool { + self.0.sparseResidencyAliased != 0 + } + + /// Checks whether variable_multisample_rate is supported on this device. + #[inline] + pub fn variable_multisample_rate(&self) -> bool { + self.0.variableMultisampleRate != 0 + } + + /// Checks whether inherited_queries is supported on this device. + #[inline] + pub fn inherited_queries(&self) -> bool { + self.0.inheritedQueries != 0 + } +} + +/// +#[repr(transparent)] +pub struct PhysicalDeviceLimits(c::VkPhysicalDeviceLimits); + +unsafe impl foundation::Transparent for PhysicalDeviceLimits { + type Wrapped = c::VkPhysicalDeviceLimits; +} + +impl PhysicalDeviceLimits { + /// Constructs this [`PhysicalDeviceFeatures`] from the underlying + /// [`VkPhysicalDeviceLimits`] + /// + /// # Arguments + /// + /// * `device` - the physical device + /// + /// [`VkPhysicalDeviceLimits`]: c::VkPhysicalDeviceLimits + #[inline] + pub fn from_c(limits: c::VkPhysicalDeviceLimits) -> Self { + Self(limits) + } + + /// Returns the device's maxImageDimension1D + #[inline] + pub fn max_image_dimension_1d(&self) -> u32 { + self.0.maxImageDimension1D + } + + /// Returns the device's maxImageDimension2D + #[inline] + pub fn max_image_dimension_2d(&self) -> u32 { + self.0.maxImageDimension2D + } + + /// Returns the device's maxImageDimension3D + #[inline] + pub fn max_image_dimension_3d(&self) -> u32 { + self.0.maxImageDimension3D + } + /// Returns the device's maxImageDimensionCube + #[inline] + pub fn max_image_dimension_cube(&self) -> u32 { + self.0.maxImageDimensionCube + } + + /// Returns the device's maxImageArrayLayers + #[inline] + pub fn max_image_array_layers(&self) -> u32 { + self.0.maxImageArrayLayers + } + + /// Returns the device's maxTexelBufferElements + #[inline] + pub fn max_texel_buffer_elements(&self) -> u32 { + self.0.maxTexelBufferElements + } + + /// Returns the device's maxUniformBufferRange + #[inline] + pub fn max_uniform_buffer_range(&self) -> u32 { + self.0.maxUniformBufferRange + } + + /// Returns the device's maxStorageBufferRange + #[inline] + pub fn max_storage_buffer_range(&self) -> u32 { + self.0.maxStorageBufferRange + } + + /// Returns the device's maxPushConstantsSize + #[inline] + pub fn max_push_constants_size(&self) -> u32 { + self.0.maxPushConstantsSize + } + + /// Returns the device's maxMemoryAllocationCount + #[inline] + pub fn max_memory_allocation_count(&self) -> u32 { + self.0.maxMemoryAllocationCount + } + + /// Returns the device's maxSamplerAllocationCount + #[inline] + pub fn max_sampler_allocation_count(&self) -> u32 { + self.0.maxSamplerAllocationCount + } + + /// Returns the device's bufferImageGranularity + #[inline] + pub fn buffer_image_granularity(&self) -> DeviceSize { + self.0.bufferImageGranularity + } + + /// Returns the device's sparseAddressSpaceSize + #[inline] + pub fn sparse_address_space_size(&self) -> DeviceSize { + self.0.sparseAddressSpaceSize + } + + /// Returns the device's maxBoundDescriptorSets + #[inline] + pub fn max_bound_descriptor_sets(&self) -> u32 { + self.0.maxBoundDescriptorSets + } + + /// Returns the device's maxPerStageDescriptorSamplers + #[inline] + pub fn max_per_stage_descriptor_samplers(&self) -> u32 { + self.0.maxPerStageDescriptorSamplers + } + + /// Returns the device's maxPerStageDescriptorUniformBuffers + #[inline] + pub fn max_per_stage_descriptor_uniform_buffers(&self) -> u32 { + self.0.maxPerStageDescriptorUniformBuffers + } + + /// Returns the device's maxPerStageDescriptorStorageBuffers + #[inline] + pub fn max_per_stage_descriptor_storage_buffers(&self) -> u32 { + self.0.maxPerStageDescriptorStorageBuffers + } + + /// Returns the device's maxPerStageDescriptorSampledImages + #[inline] + pub fn max_per_stage_descriptor_sampled_images(&self) -> u32 { + self.0.maxPerStageDescriptorSampledImages + } + + /// Returns the device's maxPerStageDescriptorStorageImages + #[inline] + pub fn max_per_stage_descriptor_storage_images(&self) -> u32 { + self.0.maxPerStageDescriptorStorageImages + } + + /// Returns the device's maxPerStageDescriptorInputAttachments + #[inline] + pub fn max_per_stage_descriptor_input_attachments(&self) -> u32 { + self.0.maxPerStageDescriptorInputAttachments + } + + /// Returns the device's maxPerStageResources + #[inline] + pub fn max_per_stage_resources(&self) -> u32 { + self.0.maxPerStageResources + } + + /// Returns the device's maxDescriptorSetSamplers + #[inline] + pub fn max_descriptor_set_samplers(&self) -> u32 { + self.0.maxDescriptorSetSamplers + } + + /// Returns the device's maxDescriptorSetUniformBuffers + #[inline] + pub fn max_descriptor_set_uniform_buffers(&self) -> u32 { + self.0.maxDescriptorSetUniformBuffers + } + + /// Returns the device's maxDescriptorSetUniformBuffersDynamic + #[inline] + pub fn max_descriptor_set_uniform_buffers_dynamic(&self) -> u32 { + self.0.maxDescriptorSetUniformBuffersDynamic + } + + /// Returns the device's maxDescriptorSetStorageBuffers + #[inline] + pub fn max_descriptor_set_storage_buffers(&self) -> u32 { + self.0.maxDescriptorSetStorageBuffers + } + + /// Returns the device's maxDescriptorSetStorageBuffersDynamic + #[inline] + pub fn max_descriptor_set_storage_buffers_dynamic(&self) -> u32 { + self.0.maxDescriptorSetStorageBuffersDynamic + } + + /// Returns the device's maxDescriptorSetSampledImages + #[inline] + pub fn max_descriptor_set_sampled_images(&self) -> u32 { + self.0.maxDescriptorSetSampledImages + } + + /// Returns the device's maxDescriptorSetStorageImages + #[inline] + pub fn max_descriptor_set_storage_images(&self) -> u32 { + self.0.maxDescriptorSetStorageImages + } + + /// Returns the device's maxDescriptorSetInputAttachments + #[inline] + pub fn max_descriptor_set_input_attachments(&self) -> u32 { + self.0.maxDescriptorSetInputAttachments + } + + /// Returns the device's maxVertexInputAttributes + #[inline] + pub fn max_vertex_input_attributes(&self) -> u32 { + self.0.maxVertexInputAttributes + } + + /// Returns the device's maxVertexInputBindings + #[inline] + pub fn max_vertex_input_bindings(&self) -> u32 { + self.0.maxVertexInputBindings + } + + /// Returns the device's maxVertexInputAttributeOffset + #[inline] + pub fn max_vertex_input_attribute_offset(&self) -> u32 { + self.0.maxVertexInputAttributeOffset + } + + /// Returns the device's maxVertexInputBindingStride + #[inline] + pub fn max_vertex_input_binding_stride(&self) -> u32 { + self.0.maxVertexInputBindingStride + } + + /// Returns the device's maxVertexOutputComponents + #[inline] + pub fn max_vertex_output_components(&self) -> u32 { + self.0.maxVertexOutputComponents + } + + /// Returns the device's maxTessellationGenerationLevel + #[inline] + pub fn max_tessellation_generation_level(&self) -> u32 { + self.0.maxTessellationGenerationLevel + } + + /// Returns the device's maxTessellationPatchSize + #[inline] + pub fn max_tessellation_patch_size(&self) -> u32 { + self.0.maxTessellationPatchSize + } + + /// Returns the device's maxTessellationControlPerVertexInputComponents + #[inline] + pub fn max_tessellation_control_per_vertex_input_components(&self) -> u32 { + self.0.maxTessellationControlPerVertexInputComponents + } + + /// Returns the device's maxTessellationControlPerVertexOutputComponents + #[inline] + pub fn max_tessellation_control_per_vertex_output_components(&self) -> u32 { + self.0.maxTessellationControlPerVertexOutputComponents + } + + /// Returns the device's maxTessellationControlPerPatchOutputComponents + #[inline] + pub fn max_tessellation_control_per_patch_output_components(&self) -> u32 { + self.0.maxTessellationControlPerPatchOutputComponents + } + + /// Returns the device's maxTessellationControlTotalOutputComponents + #[inline] + pub fn max_tessellation_control_total_output_components(&self) -> u32 { + self.0.maxTessellationControlTotalOutputComponents + } + + /// Returns the device's maxTessellationEvaluationInputComponents + #[inline] + pub fn max_tessellation_evaluation_input_components(&self) -> u32 { + self.0.maxTessellationEvaluationInputComponents + } + + /// Returns the device's maxTessellationEvaluationOutputComponents + #[inline] + pub fn max_tessellation_evaluation_output_components(&self) -> u32 { + self.0.maxTessellationEvaluationOutputComponents + } + + /// Returns the device's maxGeometryShaderInvocations + #[inline] + pub fn max_geometry_shader_invocations(&self) -> u32 { + self.0.maxGeometryShaderInvocations + } + + /// Returns the device's maxGeometryInputComponents + #[inline] + pub fn max_geometry_input_components(&self) -> u32 { + self.0.maxGeometryInputComponents + } + + /// Returns the device's maxGeometryOutputComponents + #[inline] + pub fn max_geometry_output_components(&self) -> u32 { + self.0.maxGeometryOutputComponents + } + + /// Returns the device's maxGeometryOutputVertices + #[inline] + pub fn max_geometry_output_vertices(&self) -> u32 { + self.0.maxGeometryOutputVertices + } + + /// Returns the device's maxGeometryTotalOutputComponents + #[inline] + pub fn max_geometry_total_output_components(&self) -> u32 { + self.0.maxGeometryTotalOutputComponents + } + + /// Returns the device's maxFragmentInputComponents + #[inline] + pub fn max_fragment_input_components(&self) -> u32 { + self.0.maxFragmentInputComponents + } + + /// Returns the device's maxFragmentOutputAttachments + #[inline] + pub fn max_fragment_output_attachments(&self) -> u32 { + self.0.maxFragmentOutputAttachments + } + + /// Returns the device's maxFragmentDualSrcAttachments + #[inline] + pub fn max_fragment_dual_src_attachments(&self) -> u32 { + self.0.maxFragmentDualSrcAttachments + } + + /// Returns the device's maxFragmentCombinedOutputResources + #[inline] + pub fn max_fragment_combined_output_resources(&self) -> u32 { + self.0.maxFragmentCombinedOutputResources + } + + /// Returns the device's maxComputeSharedMemorySize + #[inline] + pub fn max_compute_shared_memory_size(&self) -> u32 { + self.0.maxComputeSharedMemorySize + } + + /// Returns the device's maxComputeWorkGroupCount + #[inline] + pub fn max_compute_work_group_count(&self) -> [u32; 3usize] { + self.0.maxComputeWorkGroupCount + } + + /// Returns the device's maxComputeWorkGroupInvocations + #[inline] + pub fn max_compute_work_group_invocations(&self) -> u32 { + self.0.maxComputeWorkGroupInvocations + } + + /// Returns the device's maxComputeWorkGroupSize + #[inline] + pub fn max_compute_work_group_size(&self) -> [u32; 3usize] { + self.0.maxComputeWorkGroupSize + } + + /// Returns the device's subPixelPrecisionBits + #[inline] + pub fn sub_pixel_precision_bits(&self) -> u32 { + self.0.subPixelPrecisionBits + } + + /// Returns the device's subTexelPrecisionBits + #[inline] + pub fn sub_texel_precision_bits(&self) -> u32 { + self.0.subTexelPrecisionBits + } + + /// Returns the device's mipmapPrecisionBits + #[inline] + pub fn mipmap_precision_bits(&self) -> u32 { + self.0.mipmapPrecisionBits + } + + /// Returns the device's maxDrawIndexedIndexValue + #[inline] + pub fn max_draw_indexed_index_value(&self) -> u32 { + self.0.maxDrawIndexedIndexValue + } + + /// Returns the device's maxDrawIndirectCount + #[inline] + pub fn max_draw_indirect_count(&self) -> u32 { + self.0.maxDrawIndirectCount + } + + /// Returns the device's maxSamplerLodBias + #[inline] + pub fn max_sampler_lod_bias(&self) -> f32 { + self.0.maxSamplerLodBias + } + + /// Returns the device's maxSamplerAnisotropy + #[inline] + pub fn max_sampler_anisotropy(&self) -> f32 { + self.0.maxSamplerAnisotropy + } + + /// Returns the device's maxViewports + #[inline] + pub fn max_viewports(&self) -> u32 { + self.0.maxViewports + } + + /// Returns the device's maxViewportDimensions + #[inline] + pub fn max_viewport_dimensions(&self) -> [u32; 2usize] { + self.0.maxViewportDimensions + } + + /// Returns the device's viewportBoundsRange + #[inline] + pub fn viewport_bounds_range(&self) -> [f32; 2usize] { + self.0.viewportBoundsRange + } + + /// Returns the device's viewportSubPixelBits + #[inline] + pub fn viewport_sub_pixel_bits(&self) -> u32 { + self.0.viewportSubPixelBits + } + + /// Returns the device's minMemoryMapAlignment + #[inline] + pub fn min_memory_map_alignment(&self) -> usize { + self.0.minMemoryMapAlignment + } + + /// Returns the device's minTexelBufferOffsetAlignment + #[inline] + pub fn min_texel_buffer_offset_alignment(&self) -> DeviceSize { + self.0.minTexelBufferOffsetAlignment + } + + /// Returns the device's minUniformBufferOffsetAlignment + #[inline] + pub fn min_uniform_buffer_offset_alignment(&self) -> DeviceSize { + self.0.minUniformBufferOffsetAlignment + } + + /// Returns the device's minStorageBufferOffsetAlignment + #[inline] + pub fn min_storage_buffer_offset_alignment(&self) -> DeviceSize { + self.0.minStorageBufferOffsetAlignment + } + + /// Returns the device's minTexelOffset + #[inline] + pub fn min_texel_offset(&self) -> i32 { + self.0.minTexelOffset + } + + /// Returns the device's maxTexelOffset + #[inline] + pub fn max_texel_offset(&self) -> u32 { + self.0.maxTexelOffset + } + + /// Returns the device's minTexelGatherOffset + #[inline] + pub fn min_texel_gather_offset(&self) -> i32 { + self.0.minTexelGatherOffset + } + + /// Returns the device's maxTexelGatherOffset + #[inline] + pub fn max_texel_gather_offset(&self) -> u32 { + self.0.maxTexelGatherOffset + } + + /// Returns the device's minInterpolationOffset + #[inline] + pub fn min_interpolation_offset(&self) -> f32 { + self.0.minInterpolationOffset + } + + /// Returns the device's maxInterpolationOffset + #[inline] + pub fn max_interpolation_offset(&self) -> f32 { + self.0.maxInterpolationOffset + } + + /// Returns the device's subPixelInterpolationOffsetBits + #[inline] + pub fn sub_pixel_interpolation_offset_bits(&self) -> u32 { + self.0.subPixelInterpolationOffsetBits + } + + /// Returns the device's maxFramebufferWidth + #[inline] + pub fn max_framebuffer_width(&self) -> u32 { + self.0.maxFramebufferWidth + } + + /// Returns the device's maxFramebufferHeight + #[inline] + pub fn max_framebuffer_height(&self) -> u32 { + self.0.maxFramebufferHeight + } + + /// Returns the device's maxFramebufferLayers + #[inline] + pub fn max_framebuffer_layers(&self) -> u32 { + self.0.maxFramebufferLayers + } + + /// Returns the device's framebufferColorSampleCounts + #[inline] + pub fn framebuffer_color_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.framebufferColorSampleCounts) + } + + /// Returns the device's framebufferDepthSampleCounts + #[inline] + pub fn framebuffer_depth_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.framebufferDepthSampleCounts) + } + + /// Returns the device's framebufferStencilSampleCounts + #[inline] + pub fn framebuffer_stencil_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.framebufferStencilSampleCounts) + } + + /// Returns the device's framebufferNoAttachmentsSampleCounts + #[inline] + pub fn framebuffer_no_attachments_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.framebufferNoAttachmentsSampleCounts) + } + + /// Returns the device's maxColorAttachments + #[inline] + pub fn max_color_attachments(&self) -> u32 { + self.0.maxColorAttachments + } + + /// Returns the device's sampledImageColorSampleCounts + #[inline] + pub fn sampled_image_color_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.sampledImageColorSampleCounts) + } + + /// Returns the device's sampledImageIntegerSampleCounts + #[inline] + pub fn sampled_image_integer_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.sampledImageIntegerSampleCounts) + } + + /// Returns the device's sampledImageDepthSampleCounts + #[inline] + pub fn sampled_image_depth_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.sampledImageDepthSampleCounts) + } + + /// Returns the device's sampledImageStencilSampleCounts + #[inline] + pub fn sampled_image_stencil_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.sampledImageStencilSampleCounts) + } + + /// Returns the device's storageImageSampleCounts + #[inline] + pub fn storage_image_sample_counts(&self) -> SampleCountFlags { + SampleCountFlags::from_c(self.0.storageImageSampleCounts) + } + + /// Returns the device's maxSampleMaskWords + #[inline] + pub fn max_sample_mask_words(&self) -> u32 { + self.0.maxSampleMaskWords + } + + /// Returns the device's timestampComputeAndGraphics + #[inline] + pub fn timestamp_compute_and_graphics(&self) -> bool { + self.0.timestampComputeAndGraphics != 0 + } + + /// Returns the device's timestampPeriod + #[inline] + pub fn timestamp_period(&self) -> f32 { + self.0.timestampPeriod + } + + /// Returns the device's maxClipDistances + #[inline] + pub fn max_clip_distances(&self) -> u32 { + self.0.maxClipDistances + } + + /// Returns the device's maxCullDistances + #[inline] + pub fn max_cull_distances(&self) -> u32 { + self.0.maxCullDistances + } + + /// Returns the device's maxCombinedClipAndCullDistances + #[inline] + pub fn max_combined_clip_and_cull_distances(&self) -> u32 { + self.0.maxCombinedClipAndCullDistances + } + + /// Returns the device's discreteQueuePriorities + #[inline] + pub fn discrete_queue_priorities(&self) -> u32 { + self.0.discreteQueuePriorities + } + + /// Returns the device's pointSizeRange + #[inline] + pub fn point_size_range(&self) -> [f32; 2usize] { + self.0.pointSizeRange + } + + /// Returns the device's lineWidthRange + #[inline] + pub fn line_width_range(&self) -> [f32; 2usize] { + self.0.lineWidthRange + } + + /// Returns the device's pointSizeGranularity + #[inline] + pub fn point_size_granularity(&self) -> f32 { + self.0.pointSizeGranularity + } + + /// Returns the device's lineWidthGranularity + #[inline] + pub fn line_width_granularity(&self) -> f32 { + self.0.lineWidthGranularity + } + + /// Returns the device's strictLines + #[inline] + pub fn strict_lines(&self) -> bool { + self.0.strictLines != 0 + } + + /// Returns the device's standardSampleLocations + #[inline] + pub fn standard_sample_locations(&self) -> bool { + self.0.standardSampleLocations != 0 + } + + /// Returns the device's optimalBufferCopyOffsetAlignment + #[inline] + pub fn optimal_buffer_copy_offset_alignment(&self) -> DeviceSize { + self.0.optimalBufferCopyOffsetAlignment + } + + /// Returns the device's optimalBufferCopyRowPitchAlignment + #[inline] + pub fn optimal_buffer_copy_row_pitch_alignment(&self) -> DeviceSize { + self.0.optimalBufferCopyRowPitchAlignment + } + + /// Returns the device's nonCoherentAtomSize + #[inline] + pub fn non_coherent_atom_size(&self) -> DeviceSize { + self.0.nonCoherentAtomSize + } +} + +/// +pub struct SampleCountFlags(c::VkSampleCountFlags); + +impl SampleCountFlags { + /// Constructs this [`SampleCountFlags`] from the underlying [`VkSampleCountFlags`] + /// + /// # Arguments + /// + /// * `value` - the underlying value to construct from + /// + /// [`VkSampleCountFlags`]: c::VkSampleCountFlags + #[inline] + pub fn from_c(value: c::VkSampleCountFlags) -> Self { + Self(value) + } + + /// Checks whether the different sample count bits are set. + #[inline] + pub fn sample_count_1_bit(&self) -> bool { + self.0 & c::VkSampleCountFlagBits_VK_SAMPLE_COUNT_1_BIT != 0 + } + + /// Checks whether the different sample count bits are set. + #[inline] + pub fn sample_count_2_bit(&self) -> bool { + self.0 & c::VkSampleCountFlagBits_VK_SAMPLE_COUNT_2_BIT != 0 + } + + /// Checks whether the different sample count bits are set. + #[inline] + pub fn sample_count_4_bit(&self) -> bool { + self.0 & c::VkSampleCountFlagBits_VK_SAMPLE_COUNT_4_BIT != 0 + } + + /// Checks whether the different sample count bits are set. + #[inline] + pub fn sample_count_8_bit(&self) -> bool { + self.0 & c::VkSampleCountFlagBits_VK_SAMPLE_COUNT_8_BIT != 0 + } + + /// Checks whether the different sample count bits are set. + #[inline] + pub fn sample_count_16_bit(&self) -> bool { + self.0 & c::VkSampleCountFlagBits_VK_SAMPLE_COUNT_16_BIT != 0 + } + + /// Checks whether the different sample count bits are set. + #[inline] + pub fn sample_count_32_bit(&self) -> bool { + self.0 & c::VkSampleCountFlagBits_VK_SAMPLE_COUNT_32_BIT != 0 + } + + /// Checks whether the different sample count bits are set. + #[inline] + pub fn sample_count_64_bit(&self) -> bool { + self.0 & c::VkSampleCountFlagBits_VK_SAMPLE_COUNT_64_BIT != 0 + } +} + +/// A trait for defining suitability testing for devices, for selecting the best +/// [`PhysicalDevice`] object when presented for multiple. +/// +/// This may be used to define algorithmic decisions for determining which device +/// to use, whether this is based on "best match", exact ID match, etc. +pub trait Suitable { + /// The type of the score value to return. + /// This will typically be an integral value, so that scores can easily be + /// computed and compared. + type Type: Ord; + + /// Computes a [`Score`] value for a given [`PhysicalDevice`] object. + /// + /// Implementers are encouraged, but not required, to make the score value + /// idempotent so that score values are consistent and reproducible. + /// + /// # Arguments + /// + /// * `device` - the device to score + /// + /// [`Score`]: DeviceScore::Score + fn score(&self, device: &PhysicalDevice<'_>) -> Self::Type; + + /// Checks whether a given device satisfies the bare minimum requirements for + /// suitability. + /// + /// The default implementation of this indicates that all devices are suitable. + /// + /// # Arguments + /// + /// * `device` - the device to check for suitability. + fn is_suitable(&self, device: &PhysicalDevice<'_>) -> bool { + let _ = device; + true + } +} + +/// Selects the best device given a device scoring mechanism. +/// +/// The device with the highest computed score will be returned. +/// +/// # Arguments +/// +/// * `devices` - the devices to score +/// * `scorer` - the scoring mechansim +pub fn select_best<'a, 'b, DS: Suitable>( + devices: &'a [PhysicalDevice<'b>], + scorer: &DS, +) -> Option<&'a PhysicalDevice<'b>> { + devices + .iter() + .filter(|d| scorer.is_suitable(d)) + .map(|d| (scorer.score(d), d)) + .max_by(|(s1, _), (s2, _)| s1.cmp(s2)) + .map(|(_, d)| d) +} + +/// A wrapped representation of the [`VkPhysicalDeviceSparseProperties`]. +/// +/// [`VkPhysicalDeviceSparseProperties`]: c::VkPhysicalDeviceSparseProperties +#[repr(transparent)] +pub struct PhysicalDeviceSparseProperties(c::VkPhysicalDeviceSparseProperties); + +unsafe impl foundation::Transparent for PhysicalDeviceSparseProperties { + type Wrapped = c::VkPhysicalDeviceSparseProperties; +} + +impl PhysicalDeviceSparseProperties { + /// Returns the sparse property residencyStandard2DBlockShape. + #[inline] + pub fn residency_standard_2d_block_shape(&self) -> bool { + self.0.residencyStandard2DBlockShape != 0 + } + + /// Returns the sparse property residencyStandard2DMultisampleBlockShape. + #[inline] + pub fn residency_standard_2d_multisample_block_shape(&self) -> bool { + self.0.residencyStandard2DMultisampleBlockShape != 0 + } + + /// Returns the sparse property residencyStandard3DBlockShape. + #[inline] + pub fn residency_standard_3d_block_shape(&self) -> bool { + self.0.residencyStandard3DBlockShape != 0 + } + + /// Returns the sparse property residencyAlignedMipSize. + #[inline] + pub fn residency_aligned_mip_size(&self) -> bool { + self.0.residencyAlignedMipSize != 0 + } + + /// Returns the sparse property residencyNonResidentStrict. + #[inline] + pub fn residency_non_resident_strict(&self) -> bool { + self.0.residencyNonResidentStrict != 0 + } +} + +/// A module that provides additional utilities for scoring [`PhysicalDevice`] +/// objects +pub mod score { + use super::*; + use std::ops::AddAssign; + + /// A compound object for using multiple methodologies for scoring + /// [`PhysicalDevice`] objects. + pub struct Compound<'s, 'd, T> + where + T: Default + AddAssign + Ord, + { + scorers: &'s [&'d dyn Suitable], + } + + impl<'s, 'd, T> Compound<'s, 'd, T> + where + T: Default + AddAssign + Ord, + { + /// Constructs this [`Compound`] from a slice of [`DeviceScore`] methods + /// to use. + /// + /// # Arguments + /// + /// * `scorers` - the scorers + #[inline] + pub fn new(scorers: &'s [&'d dyn Suitable]) -> Self { + Self { scorers } + } + } + + impl<'s, 'd, T> Suitable for Compound<'s, 'd, T> + where + T: Default + AddAssign + Ord, + { + type Type = T; + + fn score(&self, device: &PhysicalDevice<'_>) -> Self::Type { + let mut val = Self::Type::default(); + for scorer in self.scorers.iter() { + val += scorer.score(device); + } + val + } + + fn is_suitable(&self, device: &PhysicalDevice<'_>) -> bool { + self.scorers.iter().all(|scorer| scorer.is_suitable(device)) + } + } + + /// A basic scoring mechanism for picking the best Vulkan [`PhysicalDevice`] + /// for graphics operations. + #[derive(Default)] + pub struct Graphics; + + impl Suitable for Graphics { + type Type = u64; + + fn score(&self, device: &PhysicalDevice<'_>) -> Self::Type { + let mut score: Self::Type = 0; + let properties = device.properties(); + let limits = properties.limits(); + + // Prioritize using discrete graphics cards over integrated. + score += match properties.device_type() { + PhysicalDeviceType::DiscreteGPU => 100_000, + PhysicalDeviceType::IntegratedGPU => 1_000, + PhysicalDeviceType::VirtualGPU => 10, + PhysicalDeviceType::CPU => 10, + _ => 0, + }; + + score += limits.max_image_dimension_2d() as u64; + score += limits.max_image_dimension_3d() as u64; + score += limits.max_draw_indexed_index_value() as u64; + score += limits.max_draw_indirect_count() as u64; + score += limits.max_framebuffer_height() as u64; + score += limits.max_framebuffer_width() as u64; + score += limits.max_framebuffer_layers() as u64; + score += limits.max_vertex_input_attributes() as u64; + score += limits.max_vertex_output_components() as u64; + + score + } + + fn is_suitable(&self, device: &PhysicalDevice<'_>) -> bool { + let features = device.features(); + if !features.geometry_shader() { + return false; + } + true + } + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for SampleCountFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut set = f.debug_set(); + if self.sample_count_1_bit() { + set.entry(&"1"); + } + if self.sample_count_2_bit() { + set.entry(&"2"); + } + if self.sample_count_4_bit() { + set.entry(&"4"); + } + if self.sample_count_8_bit() { + set.entry(&"8"); + } + if self.sample_count_16_bit() { + set.entry(&"16"); + } + if self.sample_count_32_bit() { + set.entry(&"32"); + } + if self.sample_count_64_bit() { + set.entry(&"64"); + } + + set.finish() + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for PhysicalDeviceType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::CPU => "VK_PHYSICAL_DEVICE_TYPE_CPU", + Self::DiscreteGPU => "VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU", + Self::IntegratedGPU => "VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU", + Self::VirtualGPU => "VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU", + Self::Other => "VK_PHYSICAL_DEVICE_TYPE_OTHER", + } + .fmt(f) + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for PhysicalDevice<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let properties = self.properties(); + let features = self.features(); + writeln!(f, "Properties:")?; + writeln!(f, "{:?}", properties)?; + writeln!(f, "Features:")?; + writeln!(f, "{:?}", features)?; + writeln!(f, "Queue Families:")?; + writeln!(f, "{:?}", self.queue_family_properties())?; + Ok(()) + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for PhysicalDeviceLimits { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + " maxImageDimension1D: {}", + self.max_image_dimension_1d() + )?; + writeln!( + f, + " maxImageDimension2D: {}", + self.max_image_dimension_2d() + )?; + writeln!( + f, + " maxImageDimension3D: {}", + self.max_image_dimension_3d() + )?; + writeln!( + f, + " maxImageDimensionCube: {}", + self.max_image_dimension_cube() + )?; + writeln!( + f, + " maxImageArrayLayers: {}", + self.max_image_array_layers() + )?; + writeln!( + f, + " maxTexelBufferElements: {}", + self.max_texel_buffer_elements() + )?; + writeln!( + f, + " maxUniformBufferRange: {}", + self.max_uniform_buffer_range() + )?; + writeln!( + f, + " maxStorageBufferRange: {}", + self.max_storage_buffer_range() + )?; + writeln!( + f, + " maxPushConstantsSize: {}", + self.max_push_constants_size() + )?; + writeln!( + f, + " maxMemoryAllocationCount: {}", + self.max_memory_allocation_count(), + )?; + writeln!( + f, + " maxSamplerAllocationCount: {}", + self.max_sampler_allocation_count(), + )?; + writeln!( + f, + " bufferImageGranularity: {}", + self.buffer_image_granularity() + )?; + writeln!( + f, + " sparseAddressSpaceSize: {}", + self.sparse_address_space_size() + )?; + writeln!( + f, + " maxBoundDescriptorSets: {}", + self.max_bound_descriptor_sets() + )?; + writeln!( + f, + " maxPerStageDescriptorSamplers: {}", + self.max_per_stage_descriptor_samplers(), + )?; + writeln!( + f, + " maxPerStageDescriptorUniformBuffers: {}", + self.max_per_stage_descriptor_uniform_buffers(), + )?; + writeln!( + f, + " maxPerStageDescriptorStorageBuffers: {}", + self.max_per_stage_descriptor_storage_buffers(), + )?; + writeln!( + f, + " maxPerStageDescriptorSampledImages: {}", + self.max_per_stage_descriptor_sampled_images(), + )?; + writeln!( + f, + " maxPerStageDescriptorStorageImages: {}", + self.max_per_stage_descriptor_storage_images(), + )?; + writeln!( + f, + " maxPerStageDescriptorInputAttachments: {}", + self.max_per_stage_descriptor_input_attachments(), + )?; + writeln!( + f, + " maxPerStageResources: {}", + self.max_per_stage_resources() + )?; + writeln!( + f, + " maxDescriptorSetSamplers: {}", + self.max_descriptor_set_samplers(), + )?; + writeln!( + f, + " maxDescriptorSetUniformBuffers: {}", + self.max_descriptor_set_uniform_buffers(), + )?; + writeln!( + f, + " maxDescriptorSetUniformBuffersDynamic: {}", + self.max_descriptor_set_uniform_buffers_dynamic(), + )?; + writeln!( + f, + " maxDescriptorSetStorageBuffers: {}", + self.max_descriptor_set_storage_buffers(), + )?; + writeln!( + f, + " maxDescriptorSetStorageBuffersDynamic: {}", + self.max_descriptor_set_storage_buffers_dynamic(), + )?; + writeln!( + f, + " maxDescriptorSetSampledImages: {}", + self.max_descriptor_set_sampled_images(), + )?; + writeln!( + f, + " maxDescriptorSetStorageImages: {}", + self.max_descriptor_set_storage_images(), + )?; + writeln!( + f, + " maxDescriptorSetInputAttachments: {}", + self.max_descriptor_set_input_attachments(), + )?; + writeln!( + f, + " maxVertexInputAttributes: {}", + self.max_vertex_input_attributes(), + )?; + writeln!( + f, + " maxVertexInputBindings: {}", + self.max_vertex_input_bindings() + )?; + writeln!( + f, + " maxVertexInputAttributeOffset: {}", + self.max_vertex_input_attribute_offset(), + )?; + writeln!( + f, + " maxVertexInputBindingStride: {}", + self.max_vertex_input_binding_stride(), + )?; + writeln!( + f, + " maxVertexOutputComponents: {}", + self.max_vertex_output_components(), + )?; + writeln!( + f, + " maxTessellationGenerationLevel: {}", + self.max_tessellation_generation_level(), + )?; + writeln!( + f, + " maxTessellationPatchSize: {}", + self.max_tessellation_patch_size(), + )?; + writeln!( + f, + " maxTessellationControlPerVertexInputComponents: {}", + self.max_tessellation_control_per_vertex_input_components(), + )?; + writeln!( + f, + " maxTessellationControlPerVertexOutputComponents: {}", + self.max_tessellation_control_per_vertex_output_components(), + )?; + writeln!( + f, + " maxTessellationControlPerPatchOutputComponents: {}", + self.max_tessellation_control_per_patch_output_components(), + )?; + writeln!( + f, + " maxTessellationControlTotalOutputComponents: {}", + self.max_tessellation_control_total_output_components(), + )?; + writeln!( + f, + " maxTessellationEvaluationInputComponents: {}", + self.max_tessellation_evaluation_input_components(), + )?; + writeln!( + f, + " maxTessellationEvaluationOutputComponents: {}", + self.max_tessellation_evaluation_output_components(), + )?; + writeln!( + f, + " maxGeometryShaderInvocations: {}", + self.max_geometry_shader_invocations(), + )?; + writeln!( + f, + " maxGeometryInputComponents: {}", + self.max_geometry_input_components(), + )?; + writeln!( + f, + " maxGeometryOutputComponents: {}", + self.max_geometry_output_components(), + )?; + writeln!( + f, + " maxGeometryOutputVertices: {}", + self.max_geometry_output_vertices(), + )?; + writeln!( + f, + " maxGeometryTotalOutputComponents: {}", + self.max_geometry_total_output_components(), + )?; + writeln!( + f, + " maxFragmentInputComponents: {}", + self.max_fragment_input_components(), + )?; + writeln!( + f, + " maxFragmentOutputAttachments: {}", + self.max_fragment_output_attachments(), + )?; + writeln!( + f, + " maxFragmentDualSrcAttachments: {}", + self.max_fragment_dual_src_attachments(), + )?; + writeln!( + f, + " maxFragmentCombinedOutputResources: {}", + self.max_fragment_combined_output_resources(), + )?; + writeln!( + f, + " maxComputeSharedMemorySize: {}", + self.max_compute_shared_memory_size(), + )?; + writeln!( + f, + " maxComputeWorkGroupCount: {:?}", + self.max_compute_work_group_count(), + )?; + writeln!( + f, + " maxComputeWorkGroupInvocations: {}", + self.max_compute_work_group_invocations(), + )?; + writeln!( + f, + " maxComputeWorkGroupSize: {:?}", + self.max_compute_work_group_size(), + )?; + writeln!( + f, + " subPixelPrecisionBits: {}", + self.sub_pixel_precision_bits() + )?; + writeln!( + f, + " subTexelPrecisionBits: {}", + self.sub_texel_precision_bits() + )?; + writeln!(f, " mipmapPrecisionBits: {}", self.mipmap_precision_bits())?; + writeln!( + f, + " maxDrawIndexedIndexValue: {}", + self.max_draw_indexed_index_value(), + )?; + writeln!( + f, + " maxDrawIndirectCount: {}", + self.max_draw_indirect_count() + )?; + writeln!(f, " maxSamplerLodBias: {}", self.max_sampler_lod_bias())?; + writeln!( + f, + " maxSamplerAnisotropy: {}", + self.max_sampler_anisotropy() + )?; + writeln!(f, " maxViewports: {}", self.max_viewports())?; + writeln!( + f, + " maxViewportDimensions: {:?}", + self.max_viewport_dimensions(), + )?; + writeln!( + f, + " viewportBoundsRange: {:?}", + self.viewport_bounds_range() + )?; + writeln!( + f, + " viewportSubPixelBits: {}", + self.viewport_sub_pixel_bits() + )?; + writeln!( + f, + " minMemoryMapAlignment: {}", + self.min_memory_map_alignment() + )?; + writeln!( + f, + " minTexelBufferOffsetAlignment: {}", + self.min_texel_buffer_offset_alignment(), + )?; + writeln!( + f, + " minUniformBufferOffsetAlignment: {}", + self.min_uniform_buffer_offset_alignment(), + )?; + writeln!( + f, + " minStorageBufferOffsetAlignment: {}", + self.min_storage_buffer_offset_alignment(), + )?; + writeln!(f, " minTexelOffset: {}", self.min_texel_offset())?; + writeln!(f, " maxTexelOffset: {}", self.max_texel_offset())?; + writeln!( + f, + " minTexelGatherOffset: {}", + self.min_texel_gather_offset() + )?; + writeln!( + f, + " maxTexelGatherOffset: {}", + self.max_texel_gather_offset() + )?; + writeln!( + f, + " minInterpolationOffset: {}", + self.min_interpolation_offset() + )?; + writeln!( + f, + " maxInterpolationOffset: {}", + self.max_interpolation_offset() + )?; + writeln!( + f, + " subPixelInterpolationOffsetBits: {}", + self.sub_pixel_interpolation_offset_bits(), + )?; + writeln!(f, " maxFramebufferWidth: {}", self.max_framebuffer_width())?; + writeln!( + f, + " maxFramebufferHeight: {}", + self.max_framebuffer_height() + )?; + writeln!( + f, + " maxFramebufferLayers: {}", + self.max_framebuffer_layers() + )?; + writeln!( + f, + " framebufferColorSampleCounts: {:?}", + self.framebuffer_color_sample_counts(), + )?; + writeln!( + f, + " framebufferDepthSampleCounts: {:?}", + self.framebuffer_depth_sample_counts(), + )?; + writeln!( + f, + " framebufferStencilSampleCounts: {:?}", + self.framebuffer_stencil_sample_counts(), + )?; + writeln!( + f, + " framebufferNoAttachmentsSampleCounts: {:?}", + self.framebuffer_no_attachments_sample_counts(), + )?; + writeln!(f, " maxColorAttachments: {}", self.max_color_attachments())?; + writeln!( + f, + " sampledImageColorSampleCounts: {:?}", + self.sampled_image_color_sample_counts(), + )?; + writeln!( + f, + " sampledImageIntegerSampleCounts: {:?}", + self.sampled_image_integer_sample_counts(), + )?; + writeln!( + f, + " sampledImageDepthSampleCounts: {:?}", + self.sampled_image_depth_sample_counts(), + )?; + writeln!( + f, + " sampledImageStencilSampleCounts: {:?}", + self.sampled_image_stencil_sample_counts(), + )?; + writeln!( + f, + " storageImageSampleCounts: {:?}", + self.storage_image_sample_counts(), + )?; + writeln!(f, " maxSampleMaskWords: {}", self.max_sample_mask_words())?; + writeln!( + f, + " timestampComputeAndGraphics: {}", + self.timestamp_compute_and_graphics(), + )?; + writeln!(f, " timestampPeriod: {}", self.timestamp_period())?; + writeln!(f, " maxClipDistances: {}", self.max_clip_distances())?; + writeln!(f, " maxCullDistances: {}", self.max_cull_distances())?; + writeln!( + f, + " maxCombinedClipAndCullDistances: {}", + self.max_combined_clip_and_cull_distances(), + )?; + writeln!( + f, + " discreteQueuePriorities: {}", + self.discrete_queue_priorities(), + )?; + writeln!( + f, + " pointSizeRange: {:?}", + self.point_size_range().as_slice() + )?; + writeln!(f, " lineWidthRange: {:?}", self.line_width_range())?; + writeln!( + f, + " pointSizeGranularity: {}", + self.point_size_granularity() + )?; + writeln!( + f, + " lineWidthGranularity: {}", + self.line_width_granularity() + )?; + writeln!(f, " strictLines: {}", self.strict_lines())?; + writeln!( + f, + " standardSampleLocations: {}", + self.standard_sample_locations(), + )?; + writeln!( + f, + " optimalBufferCopyOffsetAlignment: {}", + self.optimal_buffer_copy_offset_alignment(), + )?; + writeln!( + f, + " optimalBufferCopyRowPitchAlignment: {}", + self.optimal_buffer_copy_row_pitch_alignment(), + )?; + writeln!( + f, + " nonCoherentAtomSize: {}", + self.non_coherent_atom_size() + )?; + Ok(()) + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for PhysicalDeviceProperties { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, " Name: {}", self.name())?; + writeln!(f, " Pipeline Cache UUID: {}", self.pipeline_cache_uuid())?; + writeln!(f, " Type: {}", self.device_type())?; + writeln!(f, " Device ID: 0x{:x}", self.device_id())?; + writeln!(f, " Vendor ID: 0x{:x}", self.vendor_id())?; + writeln!(f, " API Version: {}", self.api_version())?; + writeln!(f, " Driver Version: {}", self.driver_version())?; + writeln!(f, "Limits:")?; + writeln!(f, "{:?}", self.limits())?; + writeln!(f, "Sparse Properties:")?; + writeln!(f, "{:?}", self.sparse_properties()) + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for PhysicalDeviceFeatures { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, " robustBufferAccess: {}", self.robust_buffer_access())?; + writeln!( + f, + " fullDrawIndexUint32: {}", + self.full_draw_index_uint32() + )?; + writeln!(f, " imageCubeArray: {}", self.image_cube_array())?; + writeln!(f, " independentBlend: {}", self.independent_blend())?; + writeln!(f, " geometryShader: {}", self.geometry_shader())?; + writeln!(f, " tessellationShader: {}", self.tessellation_shader())?; + writeln!(f, " sampleRateShading: {}", self.sample_rate_shading())?; + writeln!(f, " dualSrcBlend: {}", self.dual_src_blend())?; + writeln!(f, " logicOp: {}", self.logic_op())?; + writeln!(f, " multiDrawIndirect: {}", self.multi_draw_indirect())?; + writeln!( + f, + " drawIndirectFirstInstance: {}", + self.draw_indirect_first_instance() + )?; + writeln!(f, " depthClamp: {}", self.depth_clamp())?; + writeln!(f, " depthBiasClamp: {}", self.depth_bias_clamp())?; + writeln!(f, " fillModeNonSolid: {}", self.fill_mode_non_solid())?; + writeln!(f, " depthBounds: {}", self.depth_bounds())?; + writeln!(f, " wideLines: {}", self.wide_lines())?; + writeln!(f, " largePoints: {}", self.large_points())?; + writeln!(f, " alphaToOne: {}", self.alpha_to_one())?; + writeln!(f, " multiViewport: {}", self.multi_viewport())?; + writeln!(f, " samplerAnisotropy: {}", self.sampler_anisotropy())?; + writeln!( + f, + " textureCompressionETC2: {}", + self.texture_compression_etc2() + )?; + writeln!( + f, + " textureCompressionASTC_LDR: {}", + self.texture_compression_astc_ldr() + )?; + writeln!( + f, + " textureCompressionBC: {}", + self.texture_compression_bc() + )?; + writeln!( + f, + " occlusionQueryPrecise: {}", + self.occlusion_query_precise() + )?; + writeln!( + f, + " pipelineStatisticsQuery: {}", + self.pipeline_statistics_query() + )?; + writeln!( + f, + " vertexPipelineStoresAndAtomics: {}", + self.vertex_pipeline_stores_and_atomics() + )?; + writeln!( + f, + " fragmentStoresAndAtomics: {}", + self.fragment_stores_and_atomics() + )?; + writeln!( + f, + " shaderTessellationAndGeometryPointSize: {}", + self.shader_tessellation_and_geometry_point_size() + )?; + writeln!( + f, + " shaderImageGatherExtended: {}", + self.shader_image_gather_extended() + )?; + writeln!( + f, + " shaderStorageImageExtendedFormats: {}", + self.shader_storage_image_extended_formats() + )?; + writeln!( + f, + " shaderStorageImageMultisample: {}", + self.shader_storage_image_multisample() + )?; + writeln!( + f, + " shaderStorageImageReadWithoutFormat: {}", + self.shader_storage_image_read_without_format() + )?; + writeln!( + f, + " shaderStorageImageWriteWithoutFormat: {}", + self.shader_storage_image_write_without_format() + )?; + writeln!( + f, + " shaderUniformBufferArrayDynamicIndexing: {}", + self.shader_uniform_buffer_array_dynamic_indexing() + )?; + writeln!( + f, + " shaderSampledImageArrayDynamicIndexing: {}", + self.shader_sampled_image_array_dynamic_indexing() + )?; + writeln!( + f, + " shaderStorageBufferArrayDynamicIndexing: {}", + self.shader_storage_buffer_array_dynamic_indexing() + )?; + writeln!( + f, + " shaderStorageImageArrayDynamicIndexing: {}", + self.shader_storage_image_array_dynamic_indexing() + )?; + writeln!(f, " shaderClipDistance: {}", self.shader_clip_distance())?; + writeln!(f, " shaderCullDistance: {}", self.shader_cull_distance())?; + writeln!(f, " shaderFloat64: {}", self.shader_float64())?; + writeln!(f, " shaderInt64: {}", self.shader_int64())?; + writeln!(f, " shaderInt16: {}", self.shader_int16())?; + writeln!( + f, + " shaderResourceResidency: {}", + self.shader_resource_residency() + )?; + writeln!( + f, + " shaderResourceMinLod: {}", + self.shader_resource_min_lod() + )?; + writeln!(f, " sparseBinding: {}", self.sparse_binding())?; + writeln!( + f, + " sparseResidencyBuffer: {}", + self.sparse_residency_buffer() + )?; + writeln!( + f, + " sparseResidencyImage2D: {}", + self.sparse_residency_image2d() + )?; + writeln!( + f, + " sparseResidencyImage3D: {}", + self.sparse_residency_image3d() + )?; + writeln!( + f, + " sparseResidency2Samples: {}", + self.sparse_residency2_samples() + )?; + writeln!( + f, + " sparseResidency4Samples: {}", + self.sparse_residency4_samples() + )?; + writeln!( + f, + " sparseResidency8Samples: {}", + self.sparse_residency8_samples() + )?; + writeln!( + f, + " sparseResidency16Samples: {}", + self.sparse_residency16_samples() + )?; + writeln!( + f, + " sparseResidencyAliased: {}", + self.sparse_residency_aliased() + )?; + writeln!( + f, + " variableMultisampleRate: {}", + self.variable_multisample_rate() + )?; + writeln!(f, " inheritedQueries: {}", self.inherited_queries())?; + Ok(()) + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for PhysicalDeviceSparseProperties { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!( + f, + " ResidencyStandard2DBlockShape: {}", + self.residency_standard_2d_block_shape() + )?; + writeln!( + f, + " ResidencyStandard2DMultisampleBlockShape: {}", + self.residency_standard_2d_multisample_block_shape() + )?; + writeln!( + f, + " ResidencyStandard3DBlockShape: {}", + self.residency_standard_3d_block_shape() + )?; + writeln!( + f, + " ResidencyAlignedMipSize: {}", + self.residency_aligned_mip_size() + )?; + writeln!( + f, + " ResidencyNonResidentStrict: {}", + self.residency_non_resident_strict() + ) + } +} diff --git a/frameworks/vulkan/src/extension.rs b/frameworks/vulkan/src/extension.rs new file mode 100644 index 0000000..bc3dc51 --- /dev/null +++ b/frameworks/vulkan/src/extension.rs @@ -0,0 +1,69 @@ +//! This module provides definitions for Extensions and properties. + +use crate::c; +use core::ffi::CStr; + +/// The extension for the KHR Swapchain +pub const KHR_SWAPCHAIN: &CStr = foundation::cstr!("VK_KHR_swapchain"); + +/// The extension for KHR surfaces. +pub const KHR_SURFACE: &CStr = foundation::cstr!("VK_KHR_surface"); + +/// The extension for enumerating portabilities. +pub const PORTABILITY_ENUMERATION: &CStr = foundation::cstr!("VK_KHR_portability_enumeration"); + +/// The extension for portability subsets. +pub const PORTABILITY_SUBSET: &CStr = foundation::cstr!("VK_KHR_portability_subset"); + +/// The Apple extension for Metal-API surfaces. +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub const EXT_METAL_SURFACE: &CStr = foundation::cstr!("VK_EXT_metal_surface"); + +/// Representation of the [`VkExtensionProperties`] object. +/// +/// [`VkExtensionProperties`]: c::VkExtensionProperties +#[repr(transparent)] +pub struct ExtensionProperties(c::VkExtensionProperties); + +unsafe impl foundation::Transparent for ExtensionProperties { + type Wrapped = c::VkExtensionProperties; +} + +impl ExtensionProperties { + /// Constructs this [`ExtensionProperties`] from the underlying Vulkan + /// [`VkExtensionProperties`] + /// + /// [`VkExtensionProperties`]: c::VkExtensionProperties + pub fn from_c(properties: c::VkExtensionProperties) -> Self { + Self(properties) + } + + /// Gets the name of this extension. + pub fn extension_name(&self) -> &str { + // SAFETY: Vulkan strings are defined in terms of UTF-8 strings. + unsafe { self.extension_name_cstr().to_str().unwrap_unchecked() } + } + + /// Gets the name of this extension as a CStr + pub fn extension_name_cstr(&self) -> &CStr { + // SAFETY: Vulkan API is required to provide null-terminated strings. + unsafe { foundation::cstr_from_char_slice(&self.0.extensionName) } + } + + /// Gets the version of this extension. + /// + /// This returns an integer, incremented with backward compatible changes. + pub fn spec_version(&self) -> u32 { + self.0.specVersion + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for ExtensionProperties { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ExtensionProperties") + .field("spec_version", &self.spec_version()) + .field("extension_name", &self.extension_name()) + .finish() + } +} diff --git a/frameworks/vulkan/src/instance.rs b/frameworks/vulkan/src/instance.rs new file mode 100644 index 0000000..af43946 --- /dev/null +++ b/frameworks/vulkan/src/instance.rs @@ -0,0 +1,473 @@ +//! This module provides the Rust-wrapped definitions and builders for the +//! [`VkInstance`] C types. +//! +//! [`VkInstance`]: c::VkInstance + +use crate::alloc::AllocationCallbacks; +use crate::device::physical::PhysicalDevice; +use crate::extension::ExtensionProperties; +use crate::layer::LayerProperties; +use crate::{c, Error, Result, Version}; + +/// A builder object for creating a [`VkInstance`] object. +/// +/// [`VkInstance`]: c::VkInstance +pub struct InstanceBuilder { + application_name: CString, + engine_name: CString, + extensions: Vec, + layers: Vec, + flags: u32, + application_version: Version, + engine_version: Version, + api_version: Version, + allocation_callbacks: Option, +} + +use std::ffi::{c_char, CStr, CString}; + +impl InstanceBuilder { + /// Constructs an instance of this builder. + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + application_name: CString::from(foundation::cstr!("untitled application")), + engine_name: CString::from(foundation::cstr!("untitled engine")), + extensions: Default::default(), + layers: Default::default(), + flags: 0, + application_version: Version::default(), + engine_version: Version::default(), + api_version: crate::VERSION_1_0, + allocation_callbacks: None, + } + } + + /// Adds a single extension to the current instance being built. + /// + /// # Arguments + /// + /// * `extension` - the extension str to add. + pub fn extension(mut self, extension: &str) -> Self { + self + .extensions + .push(foundation::cstring_from_str(extension)); + self + } + + /// Adds multiple extensions to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of str extension names. + pub fn extensions<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self.extensions.extend(it.map(foundation::cstring_from_str)); + self + } + + /// Adds a single extension to the current instance being built. + /// + /// # Arguments + /// + /// * `extension` - the extension cstr to add. + pub fn extension_cstr>(mut self, extension: S) -> Self { + self.extensions.push(extension.as_ref().to_owned()); + self + } + + /// Adds multiple extensions to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of cstr extension names. + pub fn extensions_cstr<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self.extensions.extend(it.map(|s| (*s).to_owned())); + self + } + + /// Adds a single layer to the current instance being built. + /// + /// # Arguments + /// + /// * `layer` - the layer str to add. + pub fn layer(mut self, layer: &str) -> Self { + self.layers.push(foundation::cstring_from_str(layer)); + self + } + + /// Adds a multiple layers to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of str layer names. + pub fn layers<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self.layers.extend(it.map(foundation::cstring_from_str)); + self + } + + /// Adds a single layer to the current instance being built. + /// + /// # Arguments + /// + /// * `layer` - the layer cstr to add. + pub fn layer_cstr>(mut self, layer: S) -> Self { + self.layers.push(layer.as_ref().to_owned()); + self + } + + /// Adds a multiple layers to the current instance being built. + /// + /// # Arguments + /// + /// * `it` - the iterator of cstr layer names. + pub fn layers_cstr<'a, It>(mut self, it: It) -> Self + where + It: Iterator, + { + self.layers.extend(it.map(ToOwned::to_owned)); + self + } + + /// Application name sets the name of the application being built. + /// + /// # Arguments + /// + /// * `name` - the rust name of the application. + pub fn application_name(mut self, name: &str) -> Self { + self.application_name = foundation::cstring_from_str(name); + self + } + + /// Application name sets the name of the application being built. + /// + /// # Arguments + /// + /// * `name` - the C-string name of the application. + pub fn application_name_cstr>(mut self, name: S) -> Self { + self.application_name = name.as_ref().to_owned(); + self + } + + /// Sets the name of the engine being built. + /// + /// # Arguments + /// + /// * `name` - the rust name of the engine. + pub fn engine_name(mut self, name: &str) -> Self { + self.engine_name = foundation::cstring_from_str(name); + self + } + + /// Sets the name of the engine being built. + /// + /// # Arguments + /// + /// * `name` - the C-string name of the engine. + pub fn engine_name_cstr>(mut self, name: S) -> Self { + self.engine_name = name.as_ref().to_owned(); + self + } + + /// Sets the application engine [`Version`]. + /// + /// # Arguments + /// + /// * `version` - the version to set for the application. + pub fn application_version(mut self, version: Version) -> Self { + self.application_version = version; + self + } + + /// Sets the engine engine [`Version`]. + /// + /// # Arguments + /// + /// * `version` - the version to set for the engine. + pub fn engine_version(mut self, version: Version) -> Self { + self.engine_version = version; + self + } + + /// Sets the API engine [`Version`]. + /// + /// # Arguments + /// + /// * `version` - the version to set for the api. + pub fn api_version(mut self, version: Version) -> Self { + self.api_version = version; + self + } + + /// Sets the allocation callbacks. + /// + /// # Arguments + /// + /// * `version` - the version to set for the api. + pub fn allocation_callbacks(mut self, callbacks: AllocationCallbacks) -> Self { + self.allocation_callbacks = Some(callbacks); + self + } + + /// Sets the portability bit for the instance creation. + pub fn portability_bit(mut self, value: bool) -> Self { + if value { + self.flags = c::VkInstanceCreateFlagBits_VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } else { + self.flags &= !c::VkInstanceCreateFlagBits_VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; + } + self + } + + /// Builds this instance. + pub fn build(self) -> Result { + let application_info = c::VkApplicationInfo { + sType: c::VkStructureType_VK_STRUCTURE_TYPE_APPLICATION_INFO, + apiVersion: self.api_version.u32(), + applicationVersion: self.application_version.u32(), + engineVersion: self.engine_version.u32(), + pApplicationName: self.application_name.as_ptr(), + pEngineName: self.engine_name.as_ptr(), + pNext: std::ptr::null(), + }; + let extensions: Vec<*const c_char> = self.extensions.iter().map(|s| s.as_ptr()).collect(); + let layers: Vec<*const c_char> = self.layers.iter().map(|s| s.as_ptr()).collect(); + + let info = c::VkInstanceCreateInfo { + sType: c::VkStructureType_VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + enabledExtensionCount: extensions.len() as u32, + enabledLayerCount: layers.len() as u32, + flags: c::VkInstanceCreateFlagBits_VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR, + pApplicationInfo: &application_info as *const c::VkApplicationInfo, + pNext: std::ptr::null(), + ppEnabledExtensionNames: extensions.as_ptr(), + ppEnabledLayerNames: layers.as_ptr(), + }; + + let mut instance: c::VkInstance = std::ptr::null_mut(); + + // SAFETY: this is only unsafe as it is a C function call. + let result = unsafe { + c::vkCreateInstance( + &info as *const c::VkInstanceCreateInfo, + self + .allocation_callbacks + .as_ref() + .map(|v| v.as_ptr()) + .unwrap_or(std::ptr::null()), + &mut instance as *mut c::VkInstance, + ) + }; + + Error::check(result).map(|_| Instance { + instance, + allocation_callbacks: self.allocation_callbacks, + }) + } +} + +/// Representation of a [`VkInstance`] +/// +/// [`VkInstance`]: c::VkInstance +pub struct Instance { + instance: c::VkInstance, + allocation_callbacks: Option, +} + +impl AsRef for Instance { + fn as_ref(&self) -> &c::VkInstance { + &self.instance + } +} + +impl AsMut for Instance { + fn as_mut(&mut self) -> &mut c::VkInstance { + &mut self.instance + } +} + +impl std::ops::Deref for Instance { + type Target = c::VkInstance; + fn deref(&self) -> &Self::Target { + &self.instance + } +} + +impl std::ops::DerefMut for Instance { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.instance + } +} + +impl Instance { + /// Constructs this [`Instance`] from an underlying [`VkInstance`] + /// + /// # Panics + /// + /// This function will [`panic`] if the `instance` provided is null. + /// + /// # Arguments + /// + /// * `instance` - the instance to construct this from + /// + /// [`VkInstance`]: c::VkInstance + pub fn from_c(instance: c::VkInstance) -> Self { + debug_assert!(!instance.is_null()); + Self { + instance, + allocation_callbacks: None, + } + } + + /// Returns a pointer to the underlying [`VkInstance`]. + /// + /// [`VkInstance`]: c::VkInstance + #[inline] + pub fn as_ptr(&self) -> *const c::VkInstance { + &self.instance as *const c::VkInstance + } + + /// Returns a mutable pointer to the underlying [`VkInstance`]. + /// + /// [`VkInstance`]: c::VkInstance + #[inline] + pub fn as_ptr_mut(&mut self) -> *mut c::VkInstance { + &mut self.instance as *mut c::VkInstance + } + + /// Returns an optional reference to the [`AllocationCallbacks`] that this + /// instance is using. + #[inline] + pub fn allocation_callbacks(&self) -> *const c::VkAllocationCallbacks { + self + .allocation_callbacks + .as_ref() + .as_ref() + .map(|v| v.as_ptr()) + .unwrap_or(std::ptr::null()) + } + + /// Counts the number of extension properties available. + pub fn count_extension_properties() -> Result { + let mut count: u32 = 0; + + // SAFETY: This is only unsafe since it's a C call + Error::check(unsafe { + c::vkEnumerateInstanceExtensionProperties( + std::ptr::null(), + &mut count as *mut u32, + std::ptr::null_mut(), + ) + }) + .map(|_| count as usize) + } + + /// Returns up to requested number of global extension properties. + pub fn extension_properties() -> Vec { + Self::enumerate_impl(None) + } + + /// Enumerates the available extension properties on the layer of the given + /// name. + /// + /// # Arguments + /// + /// * `layer_name` - the name of the layer. This must be an ASCII string + /// without any embedded NUL characters. + pub fn extension_properties_on_layer(&self, layer_name: &str) -> Vec { + // Optimize for small layer names, since most are under 32 bytes. + if layer_name.len() <= 32 { + let mut layer_cstr: [u8; 33] = [0; 33]; + layer_cstr.copy_from_slice(layer_name.as_bytes()); + self.extension_properties_on_layer_cstr(unsafe { + CStr::from_bytes_with_nul_unchecked(&layer_cstr) + }) + } else { + let cstring = CString::new(layer_name).expect("string cannot have internal nul bytes"); + self.extension_properties_on_layer_cstr(cstring.as_c_str()) + } + } + + /// Enumerates the available extension properties on the layer of the given + /// name. + /// + /// # Arguments + /// + /// * `layer_name` - the name of the layer + pub fn extension_properties_on_layer_cstr(&self, layer_name: &CStr) -> Vec { + Self::enumerate_impl(Some(layer_name)) + } + + /// Enumerates the available instance layer properties. + pub fn instance_layer_properties(&self) -> Vec { + let mut count: u32 = 0; + unsafe { + c::vkEnumerateInstanceLayerProperties(&mut count as *mut u32, std::ptr::null_mut()); + + foundation::read_transparent_out_vec(count as usize, |p| { + let mut count = count; + c::vkEnumerateInstanceLayerProperties(&mut count as *mut u32, p); + p + }) + } + } + + /// Enumerates the list of of available physical devices. + pub fn physical_devices(&self) -> Vec> { + let mut count: u32 = 0; + unsafe { + c::vkEnumeratePhysicalDevices(*self.as_ref(), &mut count as *mut u32, std::ptr::null_mut()); + + let physical_devices = foundation::read_out_vec(count as usize, |p| { + let mut count = count; + c::vkEnumeratePhysicalDevices(*self.as_ref(), &mut count as *mut u32, p); + p + }); + + physical_devices + .into_iter() + .map(|p| PhysicalDevice::from_c(self, p)) + .collect() + } + } + + fn enumerate_impl(layer_name: Option<&CStr>) -> Vec { + let count = Self::count_extension_properties().unwrap(); + + let cstr_layer_name: *const c_char = match layer_name { + None => std::ptr::null(), + Some(name) => name.as_ptr(), + }; + + unsafe { + foundation::read_transparent_out_vec(count, |p| { + let mut count = count as u32; + c::vkEnumerateInstanceExtensionProperties(cstr_layer_name, &mut count as *mut u32, p); + p + }) + } + } +} + +impl Drop for Instance { + fn drop(&mut self) { + unsafe { + c::vkDestroyInstance( + self.instance, + self + .allocation_callbacks + .as_ref() + .map(|v| v.as_ptr()) + .unwrap_or(std::ptr::null()), + ) + } + } +} diff --git a/frameworks/vulkan/src/layer.rs b/frameworks/vulkan/src/layer.rs new file mode 100644 index 0000000..812263f --- /dev/null +++ b/frameworks/vulkan/src/layer.rs @@ -0,0 +1,65 @@ +//! This module provides definitions for validation layers and their properties. + +use crate::{c, Version}; +use core::ffi::CStr; + +/// +pub const KHRONOS_VALIDATION: &'static CStr = foundation::cstr!("VK_LAYER_KHRONOS_validation"); + +/// The rust representation of the vulkan [`VkLayerProperties`] +/// +/// [`VkLayerProperties`]: c::VkLayerProperties +#[repr(transparent)] +pub struct LayerProperties(c::VkLayerProperties); + +unsafe impl foundation::Transparent for LayerProperties { + type Wrapped = c::VkLayerProperties; +} + +impl LayerProperties { + /// Constructs this [`LayerProperties`] from the underlying Vulkan + /// [`VkLayerProperties`] + /// + /// [`VkLayerProperties`]: c::VkLayerProperties + pub fn from_c(properties: c::VkLayerProperties) -> Self { + Self(properties) + } + + /// Gets the name of this layer. + pub fn layer_name(&self) -> &str { + // SAFETY: Vulkan strings are defined in terms of UTF-8 strings. + unsafe { self.layer_name_cstr().to_str().unwrap_unchecked() } + } + + /// Gets the name of this layer as a CStr + pub fn layer_name_cstr(&self) -> &CStr { + // SAFETY: Vulkan API is required to provide null-terminated strings. + unsafe { foundation::cstr_from_char_slice(&self.0.layerName) } + } + + /// Gets the name of this layer. + pub fn description(&self) -> &str { + // SAFETY: Vulkan strings are defined in terms of UTF-8 strings. + unsafe { self.description_cstr().to_str().unwrap_unchecked() } + } + + /// Gets the name of this layer as a CStr + pub fn description_cstr(&self) -> &CStr { + // SAFETY: Vulkan API is required to provide null-terminated strings. + unsafe { foundation::cstr_from_char_slice(&self.0.description) } + } + + /// Gets the specification version of this layer. + /// + /// This returns an integer, incremented with backward compatible changes. + pub fn spec_version(&self) -> Version { + Version::from_c(self.0.specVersion) + } + + /// Gets the implementation version of this layer. + /// + /// This returns an integer, incremented with backward compatible changes. + pub fn implementation_version(&self) -> Version { + Version::from_c(self.0.implementationVersion) + } +} diff --git a/frameworks/vulkan/src/lib.rs b/frameworks/vulkan/src/lib.rs index 24bb00c..27803fe 100644 --- a/frameworks/vulkan/src/lib.rs +++ b/frameworks/vulkan/src/lib.rs @@ -7,6 +7,10 @@ #![deny(rust_2018_idioms)] /// This module provides all that raw definitions of the C Vulkan library. +/// +/// This is effectively a convenience alias to avoid needing to directly use and +/// qualify [`vulkan_sys`] everywhere; but these logically are aliases of the +/// same types. pub mod c { #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] @@ -19,18 +23,77 @@ pub mod c { pub use vulkan_sys::*; } -/// Counts the number of extension properties available. -pub fn count_extension_properties() -> usize { - let mut count: u32 = 0; - - // SAFETY: This is only unsafe since it's a C call - let r = unsafe { - c::vkEnumerateInstanceExtensionProperties( - std::ptr::null(), - &mut count as *mut u32, - std::ptr::null_mut(), - ); - }; - let _ = r; - count as usize +mod result; +mod version; + +pub mod alloc; +pub mod debug; +pub mod device; +pub mod extension; +pub mod instance; +pub mod layer; +pub mod queue; +pub mod shader; +pub mod surface; + +#[doc(inline)] +pub use result::Error; + +#[doc(inline)] +pub use result::Result; + +#[doc(inline)] +pub use version::Version; + +/// A special constant representing the 1.0 vulkan version. +pub const VERSION_1_0: Version = unsafe { Version::new_unchecked(0, 1, 0, 0) }; + +/// A special constant representing the 1.1 vulkan version. +pub const VERSION_1_1: Version = unsafe { Version::new_unchecked(0, 1, 1, 0) }; + +/// A special constant representing the 1.2 vulkan version. +pub const VERSION_1_2: Version = unsafe { Version::new_unchecked(0, 1, 2, 0) }; + +/// A special constant representing the 1.3 vulkan version. +pub const VERSION_1_3: Version = unsafe { Version::new_unchecked(0, 1, 3, 0) }; + +#[doc(inline)] +pub use c::VkDeviceSize as DeviceSize; + +#[doc(inline)] +pub use c::VkExtent2D as Extent2D; + +#[doc(inline)] +pub use c::VkExtent3D as Extent3D; + +/// Representation of the [`VkBool32`] +/// +/// [`VkBool32`]: c::VkBool32 +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(transparent)] +pub struct Bool32(c::VkBool32); + +unsafe impl foundation::Transparent for Bool32 { + type Wrapped = c::VkBool32; +} + +impl Bool32 { + /// Constructs this [`Bool32`] with a boolean value. + pub const fn new(b: bool) -> Self { + Self(b as c::VkBool32) + } +} + +impl PartialEq for Bool32 { + #[inline] + fn eq(&self, other: &bool) -> bool { + self.0 == *other as c::VkBool32 + } +} + +impl PartialEq for bool { + #[inline] + fn eq(&self, other: &Bool32) -> bool { + *self as c::VkBool32 == other.0 + } } diff --git a/frameworks/vulkan/src/queue.rs b/frameworks/vulkan/src/queue.rs new file mode 100644 index 0000000..e1bace8 --- /dev/null +++ b/frameworks/vulkan/src/queue.rs @@ -0,0 +1,175 @@ +//! This module provides functionality for working with Vulkan Queue operations. +use std::marker::PhantomData; + +use crate::c; +use crate::device::physical::PhysicalDevice; +use crate::Extent3D; + +/// A rust wrapper around the [`VkQueueFamilyProperties`]. +/// +/// [`VkQueueFamilyProperties`]: c::VkQueueFamilyProperties +#[repr(transparent)] +pub struct QueueFamilyProperties(c::VkQueueFamilyProperties); + +unsafe impl foundation::Transparent for QueueFamilyProperties { + type Wrapped = c::VkQueueFamilyProperties; +} + +/// +#[repr(transparent)] +pub struct Queue(c::VkQueue); + +unsafe impl foundation::Transparent for Queue { + type Wrapped = c::VkQueue; +} + +impl Queue { + /// + pub fn from_c(queue: c::VkQueue) -> Self { + Self(queue) + } +} + +impl QueueFamilyProperties { + /// Constructs this [`QueueFamilyProperties`] from the underlying Vulkan + /// [`VkQueueFamilyProperties`]. + /// + /// # Arguments + /// + /// * `value` - the value to use + /// + /// [`VkQueueFamilyProperties`]: c::VkQueueFamilyProperties + #[inline] + pub fn from_c(value: c::VkQueueFamilyProperties) -> Self { + Self(value) + } + + /// Returns the QueueFlags for this Queue family. + #[inline] + pub fn flags(&self) -> QueueFlags { + QueueFlags::from_c(self.0.queueFlags) + } + + /// Returns the queueCount. + #[inline] + pub fn queue_count(&self) -> u32 { + self.0.queueCount + } + + /// Returns the timestampValidBits. + #[inline] + pub fn timestamp_valid_bits(&self) -> u32 { + self.0.timestampValidBits + } + + /// Returns the minImageTransferGranularity. + #[inline] + pub fn min_image_transfer_granularity(&self) -> Extent3D { + self.0.minImageTransferGranularity + } +} + +/// A bitset that wraps [`VkQueueFlags`]. +/// +/// [`VkQueueFlags`]: c::VkQueueFlags +pub struct QueueFlags(u32); + +impl QueueFlags { + /// Constructs this [`QueueFlags`] from the underlying [`VkQueueFlags`] + /// + /// # Arguments + /// + /// * `value` - the value to set + /// + /// [`VkQueueFlags`]: c::VkQueueFlags + #[inline] + pub fn from_c(value: c::VkQueueFlags) -> Self { + Self(value) + } + + /// Queries if the VK_QUEUE_GRAPHICS_BIT is set. + #[inline] + pub fn graphics_bit(&self) -> bool { + self.0 & c::VkQueueFlagBits_VK_QUEUE_GRAPHICS_BIT != 0 + } + + /// Queries if the VK_QUEUE_COMPUTE_BIT is set. + #[inline] + pub fn compute_bit(&self) -> bool { + self.0 & c::VkQueueFlagBits_VK_QUEUE_COMPUTE_BIT != 0 + } + + /// Queries if the VK_QUEUE_TRANSFER_BIT is set. + #[inline] + pub fn transfer_bit(&self) -> bool { + self.0 & c::VkQueueFlagBits_VK_QUEUE_TRANSFER_BIT != 0 + } + + /// Queries if the VK_QUEUE_SPARSE_BINDING_BIT is set. + #[inline] + pub fn sparse_binding_bit(&self) -> bool { + self.0 & c::VkQueueFlagBits_VK_QUEUE_SPARSE_BINDING_BIT != 0 + } + + /// Queries if the VK_QUEUE_PROTECTED_BIT is set. + #[inline] + pub fn protected_bit(&self) -> bool { + self.0 & c::VkQueueFlagBits_VK_QUEUE_PROTECTED_BIT != 0 + } + + /// Queries if the VK_QUEUE_VIDEO_DECODE_BIT_KHR is set. + #[inline] + pub fn video_decode_bit_khr(&self) -> bool { + self.0 & c::VkQueueFlagBits_VK_QUEUE_VIDEO_DECODE_BIT_KHR != 0 + } + + /// Queries if the VK_QUEUE_OPTICAL_FLOW_BIT_NV is set. + #[inline] + pub fn optical_flow_bit_nv(&self) -> bool { + self.0 & c::VkQueueFlagBits_VK_QUEUE_OPTICAL_FLOW_BIT_NV != 0 + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for QueueFamilyProperties { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut s = f.debug_struct("QueueFamilyProperties"); + s.field("queue_count", &self.queue_count()); + s.field("timestamp_valid_bits", &self.timestamp_valid_bits()); + s.field("flags", &self.flags()); + s.field( + "min_image_transfer_granularity", + &self.min_image_transfer_granularity(), + ); + s.finish() + } +} + +#[cfg(feature = "debug")] +impl std::fmt::Debug for QueueFlags { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut set = f.debug_set(); + if self.graphics_bit() { + set.entry(&"graphics"); + } + if self.compute_bit() { + set.entry(&"compute"); + } + if self.transfer_bit() { + set.entry(&"transfer"); + } + if self.sparse_binding_bit() { + set.entry(&"sparse_binding"); + } + if self.protected_bit() { + set.entry(&"protected"); + } + if self.video_decode_bit_khr() { + set.entry(&"video_decode_khr"); + } + if self.optical_flow_bit_nv() { + set.entry(&"optical_flow_nv"); + } + set.finish() + } +} diff --git a/frameworks/vulkan/src/result.rs b/frameworks/vulkan/src/result.rs new file mode 100644 index 0000000..a055da0 --- /dev/null +++ b/frameworks/vulkan/src/result.rs @@ -0,0 +1,110 @@ +use crate::c; + +/// An error returned from the Vulkan API +pub struct Error(c::VkResult); + +impl Error { + /// Constructs a [`Result`] object that corresponds to whether the value + /// being checked is a [`VK_SUCCESS`] or an error. + /// + /// # Arguments + /// + /// * `result` - the result status from a Vulkan API call. + /// + /// [`VK_SUCCESS`]: c::VkResult_VK_SUCCESS + pub const fn check(result: c::VkResult) -> Result<()> { + if !Self::is_error_status(result) { + Ok(()) + } else { + Err(Error(result)) + } + } + + /// Queries whether the input result is an error status. + pub const fn is_error_status(result: c::VkResult) -> bool { + match result { + c::VkResult_VK_SUCCESS + | c::VkResult_VK_EVENT_RESET + | c::VkResult_VK_EVENT_SET + | c::VkResult_VK_INCOMPLETE + | c::VkResult_VK_NOT_READY + | c::VkResult_VK_SUBOPTIMAL_KHR + | c::VkResult_VK_TIMEOUT => false, + _ => true, + } + } + + /// Constructs this [`Error`] from a [`VkResult`]. + /// + /// [`VkResult`]: c::VkResult + #[inline] + pub const fn from_c(value: c::VkResult) -> Self { + Self(value) + } + + /// Converts this error into a string. + pub const fn as_str(&self) -> &str { + match self.0 { + c::VkResult_VK_ERROR_DEVICE_LOST => "VK_ERROR_DEVICE_LOST", + c::VkResult_VK_ERROR_EXTENSION_NOT_PRESENT => "VK_ERROR_EXTENSION_NOT_PRESENT", + c::VkResult_VK_ERROR_FEATURE_NOT_PRESENT => "VK_ERROR_FEATURE_NOT_PRESENT", + c::VkResult_VK_ERROR_FORMAT_NOT_SUPPORTED => "VK_ERROR_FORMAT_NOT_SUPPORTED", + c::VkResult_VK_ERROR_FRAGMENTATION_EXT => "VK_ERROR_FRAGMENTATION_EXT", + c::VkResult_VK_ERROR_FRAGMENTED_POOL => "VK_ERROR_FRAGMENTED_POOL", + c::VkResult_VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT => { + "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT" + } + c::VkResult_VK_ERROR_INCOMPATIBLE_DISPLAY_KHR => "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR", + c::VkResult_VK_ERROR_INCOMPATIBLE_DRIVER => "VK_ERROR_INCOMPATIBLE_DRIVER", + c::VkResult_VK_ERROR_INITIALIZATION_FAILED => "VK_ERROR_INITIALIZATION_FAILED", + c::VkResult_VK_ERROR_INVALID_DEVICE_ADDRESS_EXT => "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT", + c::VkResult_VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT => { + "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT" + } + c::VkResult_VK_ERROR_INVALID_EXTERNAL_HANDLE => "VK_ERROR_INVALID_EXTERNAL_HANDLE", + c::VkResult_VK_ERROR_INVALID_SHADER_NV => "VK_ERROR_INVALID_SHADER_NV", + c::VkResult_VK_ERROR_LAYER_NOT_PRESENT => "VK_ERROR_LAYER_NOT_PRESENT", + c::VkResult_VK_ERROR_MEMORY_MAP_FAILED => "VK_ERROR_MEMORY_MAP_FAILED", + c::VkResult_VK_ERROR_NATIVE_WINDOW_IN_USE_KHR => "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR", + c::VkResult_VK_ERROR_NOT_PERMITTED_EXT => "VK_ERROR_NOT_PERMITTED_EXT", + c::VkResult_VK_ERROR_OUT_OF_DATE_KHR => "VK_ERROR_OUT_OF_DATE_KHR", + c::VkResult_VK_ERROR_OUT_OF_DEVICE_MEMORY => "VK_ERROR_OUT_OF_DEVICE_MEMORY", + c::VkResult_VK_ERROR_OUT_OF_HOST_MEMORY => "VK_ERROR_OUT_OF_HOST_MEMORY", + c::VkResult_VK_ERROR_OUT_OF_POOL_MEMORY => "VK_ERROR_OUT_OF_POOL_MEMORY", + c::VkResult_VK_ERROR_SURFACE_LOST_KHR => "VK_ERROR_SURFACE_LOST_KHR", + c::VkResult_VK_ERROR_TOO_MANY_OBJECTS => "VK_ERROR_TOO_MANY_OBJECTS", + c::VkResult_VK_ERROR_VALIDATION_FAILED_EXT => "VK_ERROR_VALIDATION_FAILED_EXT", + c::VkResult_VK_EVENT_RESET => "VK_EVENT_RESET", + c::VkResult_VK_EVENT_SET => "VK_EVENT_SET", + c::VkResult_VK_INCOMPLETE => "VK_INCOMPLETE", + c::VkResult_VK_NOT_READY => "VK_NOT_READY", + c::VkResult_VK_SUBOPTIMAL_KHR => "VK_SUBOPTIMAL_KHR", + c::VkResult_VK_SUCCESS => "VK_SUCCESS", + c::VkResult_VK_TIMEOUT => "VK_TIMEOUT", + _ => "Unhandled VkResult", + } + } + + /// Returns an instance of this [`Error`] as a [`VkResult`] + /// + /// [`VkResult`]: c::VkResult + #[inline] + pub const fn result(&self) -> c::VkResult { + self.0 + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_str().fmt(f) + } +} +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}({})", self.as_str(), self.0) + } +} +impl std::error::Error for Error {} + +/// The result of a Vulkan operation that may fail. +pub type Result = std::result::Result; diff --git a/frameworks/vulkan/src/shader.rs b/frameworks/vulkan/src/shader.rs new file mode 100644 index 0000000..14e51d7 --- /dev/null +++ b/frameworks/vulkan/src/shader.rs @@ -0,0 +1,57 @@ +//! This module provides wrappers around Vulkan shader objects. + +use crate::c; +use crate::device::logical::Device; +use crate::Error; +use crate::Result; + +/// Representation of code in a Vulkan shader. +/// +/// The bytecode for shaders must be aligned to a 4-byte boundary, and so it is +/// +pub struct Code(Vec); + +impl Code { + /// + pub fn new(code: Vec) -> Self { + Self(code) + } + + /// + pub fn from_bytes(bytes: &[u8]) -> Result { + if bytes.len() % 4 != 0 { + todo!("return relevant error"); + } + if bytes.as_ptr() as usize % 4 != 0 { + todo!("return relevant error"); + } + todo!() + } + + /// + pub fn read(r: &mut R) -> Result { + todo!() + } + + /// + pub fn from_file(path: &str) -> Result { + todo!() + } +} + +/// Representation of a +pub struct Shader<'a> { + shader: c::VkShaderModule, + device: &'a Device<'a>, +} + +// impl<'a> Shader<'a> { +// pub fn new(device: &'_ Device<'a>, code: Code) -> Self { +// let info = c::VkShaderModuleCreateInfo{ + +// }; +// unsafe { c::vkCreateShaderModule(device, )} +// } + +// pub fn from_c(device: &'a Device, ) +// } diff --git a/frameworks/vulkan/src/surface.rs b/frameworks/vulkan/src/surface.rs new file mode 100644 index 0000000..aa3b7b9 --- /dev/null +++ b/frameworks/vulkan/src/surface.rs @@ -0,0 +1,287 @@ +//! The surface module provides definitions for working with a Vulkan surface. +use crate::device::physical::PhysicalDevice; +use crate::{c, Error, Extent2D, Result}; + +/// +#[repr(transparent)] +pub struct SurfaceCapabilities(c::VkSurfaceCapabilitiesKHR); + +unsafe impl foundation::Transparent for SurfaceCapabilities { + type Wrapped = c::VkSurfaceCapabilitiesKHR; +} + +impl SurfaceCapabilities { + /// Returns the minImageCount from the underlying capabilities object. + pub fn min_image_count(&self) -> u32 { + self.0.minImageCount + } + + /// Returns the maxImageCount from the underlying capabilities object. + pub fn max_image_count(&self) -> u32 { + self.0.maxImageCount + } + + /// Returns the currentExtent from the underlying capabilities object. + pub fn current_extent(&self) -> Extent2D { + self.0.currentExtent + } + + /// Returns the minImageExtent from the underlying capabilities object. + pub fn min_image_extent(&self) -> Extent2D { + self.0.minImageExtent + } + + /// Returns the maxImageExtent from the underlying capabilities object. + pub fn max_image_extent(&self) -> Extent2D { + self.0.maxImageExtent + } + + /// Returns the maxImageArrayLayers from the underlying capabilities object. + pub fn max_image_array_layers(&self) -> u32 { + self.0.maxImageArrayLayers + } + + /// Returns the supportedTransforms from the underlying capabilities object. + pub fn supported_transforms(&self) -> c::VkSurfaceTransformFlagsKHR { + self.0.supportedTransforms + } + + /// Returns the currentTransform from the underlying capabilities object. + pub fn current_transform(&self) -> c::VkSurfaceTransformFlagBitsKHR { + self.0.currentTransform + } + + /// Returns the supportedCompositeAlpha from the underlying capabilities object. + pub fn supported_composite_alpha(&self) -> c::VkCompositeAlphaFlagsKHR { + self.0.supportedCompositeAlpha + } + + /// Returns the supportedUsageFlags from the underlying capabilities object. + pub fn supported_usage_flags(&self) -> c::VkImageUsageFlags { + self.0.supportedUsageFlags + } +} + +/// A rust wrapper around the [`VkSurfaceKHR`] object +/// +/// [`VkSurfaceKHR`]: c::VkSurfaceKHR +pub struct Surface<'a> { + surface: c::VkSurfaceKHR, + physical_device: &'a PhysicalDevice<'a>, +} + +impl<'a> AsRef for Surface<'a> { + fn as_ref(&self) -> &c::VkSurfaceKHR { + &self.surface + } +} + +impl<'a> AsMut for Surface<'a> { + fn as_mut(&mut self) -> &mut c::VkSurfaceKHR { + &mut self.surface + } +} + +impl<'a> Surface<'a> { + /// Constructs this [`Surface`] from the underlying Vulkan [`VkSurfaceKHR`] + /// + /// # Panics + /// + /// This function will [`panic`] if `value` is null. + /// + /// # Arguments + /// + /// * `surface` - the underlying value + /// + /// [`VkSurfaceKHR`]: c::VkSurfaceKHR + pub fn from_c(physical_device: &'a PhysicalDevice<'a>, surface: c::VkSurfaceKHR) -> Self { + debug_assert!(!surface.is_null()); + Self { + surface, + physical_device, + } + } + + /// Gets the [`SurfaceCapabilities`] for the physical device using this surface. + pub fn physical_device_surface_capabilities(&self) -> Result { + unsafe { + let mut properties: c::VkSurfaceCapabilitiesKHR = std::mem::zeroed(); + Error::check(c::vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + *self.physical_device.as_ref(), + self.surface, + &mut properties as *mut c::VkSurfaceCapabilitiesKHR, + )) + .map(|_| SurfaceCapabilities(properties)) + } + } + + /// Gets the [`SurfaceFormat`] for the physical device using this surface. + pub fn physical_device_surface_formats(&self) -> Vec { + let mut count: u32 = 0; + unsafe { + c::vkGetPhysicalDeviceSurfaceFormatsKHR( + *self.physical_device.as_ref(), + self.surface, + &mut count as *mut u32, + std::ptr::null_mut(), + ); + foundation::read_transparent_out_vec(count as usize, |p| { + let mut count = count; + c::vkGetPhysicalDeviceSurfaceFormatsKHR( + *self.physical_device.as_ref(), + self.surface, + &mut count as *mut u32, + p, + ); + p + }) + } + } + + /// Gets the [`SurfacePresentMode`] for the physical device using this surface. + pub fn physical_device_surface_present_modes(&self) -> Vec { + let mut count: u32 = 0; + unsafe { + c::vkGetPhysicalDeviceSurfacePresentModesKHR( + *self.physical_device.as_ref(), + self.surface, + &mut count as *mut u32, + std::ptr::null_mut(), + ); + foundation::read_transparent_out_vec(count as usize, |p| { + let mut count = count; + c::vkGetPhysicalDeviceSurfacePresentModesKHR( + *self.physical_device.as_ref(), + self.surface, + &mut count as *mut u32, + p, + ); + p + }) + } + } +} + +impl<'a> Drop for Surface<'a> { + fn drop(&mut self) { + unsafe { + c::vkDestroySurfaceKHR( + *self.physical_device.instance().as_ref(), + self.surface, + self.physical_device.instance().allocation_callbacks(), + ) + } + } +} + +/// A rust wrapper around the [`VkSurfaceFormatKHR`] as a Rust object. +/// +/// [`VkSurfaceFormatKHR`]: c::VkSurfaceFormatKHR +#[repr(transparent)] +pub struct SurfaceFormat(c::VkSurfaceFormatKHR); + +unsafe impl foundation::Transparent for SurfaceFormat { + type Wrapped = c::VkSurfaceFormatKHR; +} + +impl SurfaceFormat { + /// Gets the underlying format from the [`SurfaceFormat`]. + pub fn format(&self) -> c::VkFormat { + self.0.format + } + + /// Gets the underlying colorSpace from the [`SurfaceFormat`]. + pub fn color_space(&self) -> c::VkColorSpaceKHR { + self.0.colorSpace + } +} + +/// A rust wrapper around the [`VkSurfacePresentModeEXT`] as a Rust object. +/// +/// [`VkSurfacePresentModeEXT`]: c::VkSurfacePresentModeEXT +#[repr(transparent)] +pub struct SurfacePresentMode(c::VkSurfacePresentModeEXT); + +unsafe impl foundation::Transparent for SurfacePresentMode { + type Wrapped = c::VkPresentModeKHR; +} + +impl SurfacePresentMode { + /// + pub fn present_mode(&self) -> PresentMode { + PresentMode::from_c(self.0.presentMode) + } +} + +/// +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum PresentMode { + /// Images submitted by your application are transferred to the screen right + /// away, which may result in tearing. + Immediate, + + /// This is another variation of the second mode. Instead of blocking the + /// application when the queue is full, the images that are already queued + /// are simply replaced with the newer ones. This mode can be used to render + /// frames as fast as possible while still avoiding tearing, resulting in + /// fewer latency issues than standard vertical sync. This is commonly known + /// as "triple buffering", although the existence of three buffers alone does + /// not necessarily mean that the framerate is unlocked. + Mailbox, + + /// The swap chain is a queue where the display takes an image from the front + /// of the queue when the display is refreshed and the program inserts + /// rendered images at the back of the queue. If the queue is full then the + /// program has to wait. This is most similar to vertical sync as found in + /// modern games. The moment that the display is refreshed is known as + /// "vertical blank". + Fifo, + + /// This mode only differs from the previous one if the application is late + /// and the queue was empty at the last vertical blank. Instead of waiting + /// for the next vertical blank, the image is transferred right away when it + /// finally arrives. This may result in visible tearing. + FifoRelaxed, + /// + SharedDemandRefresh, + /// + SharedContinuousRefresh, + /// + Unknown(c::VkPresentModeKHR), +} + +impl PresentMode { + /// + pub fn from_c(value: c::VkPresentModeKHR) -> PresentMode { + match value { + c::VkPresentModeKHR_VK_PRESENT_MODE_IMMEDIATE_KHR => PresentMode::Immediate, + c::VkPresentModeKHR_VK_PRESENT_MODE_MAILBOX_KHR => PresentMode::Mailbox, + c::VkPresentModeKHR_VK_PRESENT_MODE_FIFO_KHR => PresentMode::Fifo, + c::VkPresentModeKHR_VK_PRESENT_MODE_FIFO_RELAXED_KHR => PresentMode::FifoRelaxed, + c::VkPresentModeKHR_VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR => { + PresentMode::SharedDemandRefresh + } + c::VkPresentModeKHR_VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR => { + PresentMode::SharedContinuousRefresh + } + _ => PresentMode::Unknown(value), + } + } + + /// Converts this enum into the C equivalent. + pub fn to_c(&self) -> c::VkPresentModeKHR { + match &self { + PresentMode::Immediate => c::VkPresentModeKHR_VK_PRESENT_MODE_IMMEDIATE_KHR, + PresentMode::Mailbox => c::VkPresentModeKHR_VK_PRESENT_MODE_MAILBOX_KHR, + PresentMode::Fifo => c::VkPresentModeKHR_VK_PRESENT_MODE_FIFO_KHR, + PresentMode::FifoRelaxed => c::VkPresentModeKHR_VK_PRESENT_MODE_FIFO_RELAXED_KHR, + PresentMode::SharedDemandRefresh => { + c::VkPresentModeKHR_VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR + } + PresentMode::SharedContinuousRefresh => { + c::VkPresentModeKHR_VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR + } + PresentMode::Unknown(value) => *value, + } + } +} diff --git a/frameworks/vulkan/src/version.rs b/frameworks/vulkan/src/version.rs new file mode 100644 index 0000000..40546af --- /dev/null +++ b/frameworks/vulkan/src/version.rs @@ -0,0 +1,127 @@ +/// Representation of a semantic version, as defined by Vulkan. +/// +/// Versions in Vulkan use 7 bits for the major version, 10 for the minor, +/// 12 for the patch, and then an additional 3 for the variant. +#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub struct Version(u32); + +impl Default for Version { + fn default() -> Self { + // SAFETY: each value is within the valid range. + unsafe { Self::new_unchecked(0, 0, 1, 0) } + } +} + +impl Version { + const VARIANT_BITS: u32 = 3; + const MAJOR_BITS: u32 = 7; + const MINOR_BITS: u32 = 10; + const PATCH_BITS: u32 = 12; + + const VARIANT_OFFSET: u32 = Self::PATCH_BITS + Self::MINOR_BITS + Self::MAJOR_BITS; + const MAJOR_OFFSET: u32 = Self::PATCH_BITS + Self::MINOR_BITS; + const MINOR_OFFSET: u32 = Self::PATCH_BITS; + const PATCH_OFFSET: u32 = 0; + + const VARIANT_MASK: u32 = (1 << Self::VARIANT_BITS) - 1; + const MAJOR_MASK: u32 = (1 << Self::MAJOR_BITS) - 1; + const MINOR_MASK: u32 = (1 << Self::MINOR_BITS) - 1; + const PATCH_MASK: u32 = (1 << Self::PATCH_BITS) - 1; + + const VARIANT_MAX: u32 = (1 << Self::VARIANT_BITS) - 1; + const MAJOR_MAX: u32 = (1 << Self::MAJOR_BITS) - 1; + const MINOR_MAX: u32 = (1 << Self::MINOR_BITS) - 1; + const PATCH_MAX: u32 = (1 << Self::PATCH_BITS) - 1; + + /// Constructs a new [`Version`] object, given the 4 components of the version. + /// + /// This function will return [`None`] if any of the component values exceed + /// the maximum values for their components + /// + /// # Arguments + /// + /// * `variant` - the variant version component (between 0-7) + /// * `major` - the major version component (between 0-127) + /// * `minor` - the minor version component (between 0-1023) + /// * `patch` - the patch version component (between 0-4095) + pub const fn new(variant: u32, major: u32, minor: u32, patch: u32) -> Option { + if variant > Self::VARIANT_MAX + || major > Self::MAJOR_MAX + || minor > Self::MINOR_MAX + || patch > Self::PATCH_MAX + { + None + } else { + // SAFETY: this *is* checked immediately above. + Some(unsafe { Self::new_unchecked(variant, major, minor, patch) }) + } + } + + /// Constructs a version without checking that each component is within the + /// valid range of values. + /// + /// This assumes the value has been checked before being called. + /// + /// # Arguments + /// + /// * `variant` - the variant version component (between 0-7) + /// * `major` - the major version component (between 0-127) + /// * `minor` - the minor version component (between 0-1023) + /// * `patch` - the patch version component (between 0-4095) + pub const unsafe fn new_unchecked(variant: u32, major: u32, minor: u32, patch: u32) -> Self { + Self( + (variant << Self::VARIANT_OFFSET) + | (major << Self::MAJOR_OFFSET) + | (minor << Self::MINOR_OFFSET) + | (patch << Self::PATCH_OFFSET), + ) + } + + /// Constructs this [`Version`] directly from the C value. + /// + /// # Arguments + /// + /// * `value` - the value + #[inline] + pub const fn from_c(value: u32) -> Self { + Self(value) + } + + /// Gets the variant component of the version. + pub const fn variant(&self) -> u32 { + (self.0 >> Self::VARIANT_OFFSET) & Self::VARIANT_MASK + } + + /// Gets the major component of the version. + pub const fn major(&self) -> u32 { + (self.0 >> Self::MAJOR_OFFSET) & Self::MAJOR_MASK + } + + /// Gets the minor component of the version + pub const fn minor(&self) -> u32 { + (self.0 >> Self::MINOR_OFFSET) & Self::MINOR_MASK + } + + /// Gets the patch component of the verrsion + pub const fn patch(&self) -> u32 { + (self.0 >> Self::PATCH_OFFSET) & Self::PATCH_MASK + } + + /// Gets the version as a raw u32 value. + pub const fn u32(&self) -> u32 { + self.0 + } +} + +impl std::fmt::Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}.{}.{} ({})", + self.major(), + self.minor(), + self.patch(), + self.0, + ) + } +}