diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index 7154389d..738a6781 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -43,8 +43,8 @@ mod linux { sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, swapchain::{AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError}, sync::{ - now, ExternalSemaphoreHandleTypes, FlushError, GpuFuture, PipelineStages, Semaphore, - SemaphoreCreateInfo, + now, ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, FlushError, GpuFuture, + PipelineStages, Semaphore, SemaphoreCreateInfo, }, VulkanLibrary, }; @@ -146,8 +146,16 @@ mod linux { .unwrap(), ); - let acquire_fd = unsafe { acquire_sem.export_opaque_fd().unwrap() }; - let release_fd = unsafe { release_sem.export_opaque_fd().unwrap() }; + let acquire_fd = unsafe { + acquire_sem + .export_fd(ExternalSemaphoreHandleType::OpaqueFd) + .unwrap() + }; + let release_fd = unsafe { + release_sem + .export_fd(ExternalSemaphoreHandleType::OpaqueFd) + .unwrap() + }; let barrier_clone = barrier.clone(); let barrier_2_clone = barrier_2.clone(); diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index 7eb5bdb9..c5200f22 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -23,7 +23,10 @@ use crate::{ ColorSpace, FullScreenExclusive, PresentMode, SupportedSurfaceTransforms, Surface, SurfaceApi, SurfaceCapabilities, SurfaceInfo, }, - sync::{ExternalSemaphoreInfo, ExternalSemaphoreProperties}, + sync::{ + ExternalFenceInfo, ExternalFenceProperties, ExternalSemaphoreInfo, + ExternalSemaphoreProperties, + }, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; use bytemuck::cast_slice; @@ -499,6 +502,107 @@ impl PhysicalDevice { } } + /// Retrieves the external handle properties supported for fences with a given + /// configuration. + /// + /// The instance API version must be at least 1.1, or the + /// [`khr_external_fence_capabilities`](crate::instance::InstanceExtensions::khr_external_fence_capabilities) + /// extension must be enabled on the instance. + #[inline] + pub fn external_fence_properties( + &self, + info: ExternalFenceInfo, + ) -> Result { + self.validate_external_fence_properties(&info)?; + + unsafe { Ok(self.external_fence_properties_unchecked(info)) } + } + + fn validate_external_fence_properties( + &self, + info: &ExternalFenceInfo, + ) -> Result<(), ExternalFenceSemaphorePropertiesError> { + if !(self.instance.api_version() >= Version::V1_1 + || self + .instance + .enabled_extensions() + .khr_external_fence_capabilities) + { + return Err(ExternalFenceSemaphorePropertiesError::RequirementNotMet { + required_for: "`external_fence_properties`", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_1), + instance_extensions: &["khr_external_fence_capabilities"], + ..Default::default() + }, + }); + } + + let &ExternalFenceInfo { + handle_type, + _ne: _, + } = info; + + // VUID-VkPhysicalDeviceExternalFenceInfo-handleType-parameter + handle_type.validate_physical_device(self)?; + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn external_fence_properties_unchecked( + &self, + info: ExternalFenceInfo, + ) -> ExternalFenceProperties { + /* Input */ + + let ExternalFenceInfo { + handle_type, + _ne: _, + } = info; + + let external_fence_info = ash::vk::PhysicalDeviceExternalFenceInfo { + handle_type: handle_type.into(), + ..Default::default() + }; + + /* Output */ + + let mut external_fence_properties = ash::vk::ExternalFenceProperties::default(); + + /* Call */ + + let fns = self.instance.fns(); + + if self.instance.api_version() >= Version::V1_1 { + (fns.v1_1.get_physical_device_external_fence_properties)( + self.handle, + &external_fence_info, + &mut external_fence_properties, + ) + } else { + (fns.khr_external_fence_capabilities + .get_physical_device_external_fence_properties_khr)( + self.handle, + &external_fence_info, + &mut external_fence_properties, + ); + } + + ExternalFenceProperties { + exportable: external_fence_properties + .external_fence_features + .intersects(ash::vk::ExternalFenceFeatureFlags::EXPORTABLE), + importable: external_fence_properties + .external_fence_features + .intersects(ash::vk::ExternalFenceFeatureFlags::IMPORTABLE), + export_from_imported_handle_types: external_fence_properties + .export_from_imported_handle_types + .into(), + compatible_handle_types: external_fence_properties.compatible_handle_types.into(), + } + } + /// Retrieves the external handle properties supported for semaphores with a given /// configuration. /// @@ -509,7 +613,7 @@ impl PhysicalDevice { pub fn external_semaphore_properties( &self, info: ExternalSemaphoreInfo, - ) -> Result { + ) -> Result { self.validate_external_semaphore_properties(&info)?; unsafe { Ok(self.external_semaphore_properties_unchecked(info)) } @@ -518,14 +622,14 @@ impl PhysicalDevice { fn validate_external_semaphore_properties( &self, info: &ExternalSemaphoreInfo, - ) -> Result<(), ExternalSemaphorePropertiesError> { + ) -> Result<(), ExternalFenceSemaphorePropertiesError> { if !(self.instance.api_version() >= Version::V1_1 || self .instance .enabled_extensions() .khr_external_semaphore_capabilities) { - return Err(ExternalSemaphorePropertiesError::RequirementNotMet { + return Err(ExternalFenceSemaphorePropertiesError::RequirementNotMet { required_for: "`external_semaphore_properties`", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_1), @@ -1977,18 +2081,18 @@ impl From for ExternalBufferPropertiesError { } } -/// Error that can happen when retrieving properties of an external semaphore. +/// Error that can happen when retrieving properties of an external fence or semaphore. #[derive(Clone, Debug)] -pub enum ExternalSemaphorePropertiesError { +pub enum ExternalFenceSemaphorePropertiesError { RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, } -impl Error for ExternalSemaphorePropertiesError {} +impl Error for ExternalFenceSemaphorePropertiesError {} -impl Display for ExternalSemaphorePropertiesError { +impl Display for ExternalFenceSemaphorePropertiesError { #[inline] fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { match self { @@ -2004,7 +2108,7 @@ impl Display for ExternalSemaphorePropertiesError { } } -impl From for ExternalSemaphorePropertiesError { +impl From for ExternalFenceSemaphorePropertiesError { #[inline] fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { diff --git a/vulkano/src/swapchain/swapchain.rs b/vulkano/src/swapchain/swapchain.rs index b2f609d3..3063647f 100644 --- a/vulkano/src/swapchain/swapchain.rs +++ b/vulkano/src/swapchain/swapchain.rs @@ -28,8 +28,8 @@ use crate::{ macros::vulkan_enum, swapchain::{SurfaceApi, SurfaceInfo, SurfaceSwapchainLock}, sync::{ - AccessCheckError, AccessError, AccessFlags, Fence, FlushError, GpuFuture, PipelineStages, - Semaphore, SemaphoreCreationError, Sharing, + AccessCheckError, AccessError, AccessFlags, Fence, FenceError, FlushError, GpuFuture, + PipelineStages, Semaphore, SemaphoreError, Sharing, }, DeviceSize, OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject, }; @@ -1717,8 +1717,11 @@ pub enum AcquireError { /// surface's new properties and recreate a new swapchain if you want to continue drawing. OutOfDate, - /// Error during semaphore creation - SemaphoreError(SemaphoreCreationError), + /// Error during fence creation. + FenceError(FenceError), + + /// Error during semaphore creation. + SemaphoreError(SemaphoreError), } impl Error for AcquireError { @@ -1746,14 +1749,21 @@ impl Display for AcquireError { AcquireError::FullScreenExclusiveModeLost => { "the swapchain no longer has full-screen exclusivity" } + AcquireError::FenceError(_) => "error creating fence", AcquireError::SemaphoreError(_) => "error creating semaphore", } ) } } -impl From for AcquireError { - fn from(err: SemaphoreCreationError) -> Self { +impl From for AcquireError { + fn from(err: FenceError) -> Self { + AcquireError::FenceError(err) + } +} + +impl From for AcquireError { + fn from(err: SemaphoreError) -> Self { AcquireError::SemaphoreError(err) } } diff --git a/vulkano/src/sync/fence.rs b/vulkano/src/sync/fence.rs index 8010829f..1d72353e 100644 --- a/vulkano/src/sync/fence.rs +++ b/vulkano/src/sync/fence.rs @@ -9,7 +9,8 @@ use crate::{ device::{Device, DeviceOwned}, - OomError, VulkanError, VulkanObject, + macros::{vulkan_bitflags, vulkan_enum}, + OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; use smallvec::SmallVec; use std::{ @@ -35,6 +36,8 @@ pub struct Fence { handle: ash::vk::Fence, device: Arc, + _export_handle_types: ExternalFenceHandleTypes, + // If true, we know that the `Fence` is signaled. If false, we don't know. // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences` // multiple times. @@ -47,8 +50,55 @@ pub struct Fence { impl Fence { /// Creates a new `Fence`. - pub fn new(device: Arc, create_info: FenceCreateInfo) -> Result { - let FenceCreateInfo { signaled, _ne: _ } = create_info; + #[inline] + pub fn new(device: Arc, create_info: FenceCreateInfo) -> Result { + Self::validate_new(&device, &create_info)?; + + unsafe { Ok(Self::new_unchecked(device, create_info)?) } + } + + fn validate_new(device: &Device, create_info: &FenceCreateInfo) -> Result<(), FenceError> { + let FenceCreateInfo { + signaled: _, + export_handle_types, + _ne: _, + } = create_info; + + if !export_handle_types.is_empty() { + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_external_fence) + { + return Err(FenceError::RequirementNotMet { + required_for: "`create_info.export_handle_types` is not empty", + requires_one_of: RequiresOneOf { + api_version: Some(Version::V1_1), + device_extensions: &["khr_external_fence"], + ..Default::default() + }, + }); + } + + // VUID-VkExportFenceCreateInfo-handleTypes-01446 + export_handle_types.validate_device(device)?; + + // VUID-VkExportFenceCreateInfo-handleTypes-01446 + // TODO: `vkGetPhysicalDeviceExternalFenceProperties` can only be called with one + // handle type, so which one do we give it? + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn new_unchecked( + device: Arc, + create_info: FenceCreateInfo, + ) -> Result { + let FenceCreateInfo { + signaled, + export_handle_types, + _ne: _, + } = create_info; let mut flags = ash::vk::FenceCreateFlags::empty(); @@ -56,17 +106,30 @@ impl Fence { flags |= ash::vk::FenceCreateFlags::SIGNALED; } - let create_info = ash::vk::FenceCreateInfo { + let mut create_info_vk = ash::vk::FenceCreateInfo { flags, ..Default::default() }; + let mut export_fence_create_info_vk = None; - let handle = unsafe { + if !export_handle_types.is_empty() { + let _ = export_fence_create_info_vk.insert(ash::vk::ExportFenceCreateInfo { + handle_types: export_handle_types.into(), + ..Default::default() + }); + } + + if let Some(info) = export_fence_create_info_vk.as_mut() { + info.p_next = create_info_vk.p_next; + create_info_vk.p_next = info as *const _ as *const _; + } + + let handle = { let fns = device.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.create_fence)( device.internal_object(), - &create_info, + &create_info_vk, ptr::null(), output.as_mut_ptr(), ) @@ -78,18 +141,43 @@ impl Fence { Ok(Fence { handle, device, + _export_handle_types: export_handle_types, is_signaled: AtomicBool::new(signaled), must_put_in_pool: false, }) } + /// Creates a new `Fence` from an ash-handle + /// # Safety + /// The `handle` has to be a valid vulkan object handle and + /// the `create_info` must match the info used to create said object + pub unsafe fn from_handle( + handle: ash::vk::Fence, + create_info: FenceCreateInfo, + device: Arc, + ) -> Fence { + let FenceCreateInfo { + signaled, + export_handle_types, + _ne: _, + } = create_info; + + Fence { + handle, + device, + _export_handle_types: export_handle_types, + is_signaled: AtomicBool::new(signaled), + must_put_in_pool: false, + } + } + /// Takes a fence from the vulkano-provided fence pool. /// If the pool is empty, a new fence will be created. /// Upon `drop`, the fence is put back into the pool. /// /// For most applications, using the fence pool should be preferred, /// in order to avoid creating new fences every frame. - pub fn from_pool(device: Arc) -> Result { + pub fn from_pool(device: Arc) -> Result { let handle = device.fence_pool().lock().pop(); let fence = match handle { Some(handle) => { @@ -104,6 +192,7 @@ impl Fence { Fence { handle, device, + _export_handle_types: ExternalFenceHandleTypes::empty(), is_signaled: AtomicBool::new(false), must_put_in_pool: true, } @@ -119,25 +208,6 @@ impl Fence { Ok(fence) } - /// Creates a new `Fence` from an ash-handle - /// # Safety - /// The `handle` has to be a valid vulkan object handle and - /// the `create_info` must match the info used to create said object - pub unsafe fn from_handle( - handle: ash::vk::Fence, - create_info: FenceCreateInfo, - device: Arc, - ) -> Fence { - let FenceCreateInfo { signaled, _ne: _ } = create_info; - - Fence { - handle, - device, - is_signaled: AtomicBool::new(signaled), - must_put_in_pool: false, - } - } - /// Returns true if the fence is signaled. #[inline] pub fn is_signaled(&self) -> Result { @@ -164,7 +234,7 @@ impl Fence { /// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead. /// /// If you pass a duration of 0, then the function will return without blocking. - pub fn wait(&self, timeout: Option) -> Result<(), FenceWaitError> { + pub fn wait(&self, timeout: Option) -> Result<(), FenceError> { unsafe { if self.is_signaled.load(Ordering::Relaxed) { return Ok(()); @@ -193,7 +263,7 @@ impl Fence { self.is_signaled.store(true, Ordering::Relaxed); Ok(()) } - ash::vk::Result::TIMEOUT => Err(FenceWaitError::Timeout), + ash::vk::Result::TIMEOUT => Err(FenceError::Timeout), err => Err(VulkanError::from(err).into()), } } @@ -204,7 +274,7 @@ impl Fence { /// # Panic /// /// Panics if not all fences belong to the same device. - pub fn multi_wait<'a, I>(iter: I, timeout: Option) -> Result<(), FenceWaitError> + pub fn multi_wait<'a, I>(iter: I, timeout: Option) -> Result<(), FenceError> where I: IntoIterator, { @@ -256,7 +326,7 @@ impl Fence { match result { ash::vk::Result::SUCCESS => Ok(()), - ash::vk::Result::TIMEOUT => Err(FenceWaitError::Timeout), + ash::vk::Result::TIMEOUT => Err(FenceError::Timeout), err => Err(VulkanError::from(err).into()), } } @@ -377,6 +447,8 @@ pub struct FenceCreateInfo { /// The default value is `false`. pub signaled: bool, + pub export_handle_types: ExternalFenceHandleTypes, + pub _ne: crate::NonExhaustive, } @@ -385,57 +457,189 @@ impl Default for FenceCreateInfo { fn default() -> Self { Self { signaled: false, + export_handle_types: ExternalFenceHandleTypes::empty(), _ne: crate::NonExhaustive(()), } } } -/// Error that can be returned when waiting on a fence. +vulkan_enum! { + /// The handle type used for Vulkan external fence APIs. + #[non_exhaustive] + ExternalFenceHandleType = ExternalFenceHandleTypeFlags(u32); + + // TODO: document + OpaqueFd = OPAQUE_FD, + + // TODO: document + OpaqueWin32 = OPAQUE_WIN32, + + // TODO: document + OpaqueWin32Kmt = OPAQUE_WIN32_KMT, + + // TODO: document + SyncFd = SYNC_FD, +} + +vulkan_bitflags! { + /// A mask of multiple external fence handle types. + #[non_exhaustive] + ExternalFenceHandleTypes = ExternalFenceHandleTypeFlags(u32); + + // TODO: document + opaque_fd = OPAQUE_FD, + + // TODO: document + opaque_win32 = OPAQUE_WIN32, + + // TODO: document + opaque_win32_kmt = OPAQUE_WIN32_KMT, + + // TODO: document + sync_fd = SYNC_FD, +} + +impl From for ExternalFenceHandleTypes { + #[inline] + fn from(val: ExternalFenceHandleType) -> Self { + let mut result = Self::empty(); + + match val { + ExternalFenceHandleType::OpaqueFd => result.opaque_fd = true, + ExternalFenceHandleType::OpaqueWin32 => result.opaque_win32 = true, + ExternalFenceHandleType::OpaqueWin32Kmt => result.opaque_win32_kmt = true, + ExternalFenceHandleType::SyncFd => result.sync_fd = true, + } + + result + } +} + +vulkan_bitflags! { + /// Additional parameters for a fence payload import. + #[non_exhaustive] + FenceImportFlags = FenceImportFlags(u32); + + /// The fence payload will be imported only temporarily, regardless of the permanence of the + /// imported handle type. + temporary = TEMPORARY, +} + +/// The fence configuration to query in +/// [`PhysicalDevice::external_fence_properties`](crate::device::physical::PhysicalDevice::external_fence_properties). +#[derive(Clone, Debug)] +pub struct ExternalFenceInfo { + /// The external handle type that will be used with the fence. + pub handle_type: ExternalFenceHandleType, + + pub _ne: crate::NonExhaustive, +} + +impl ExternalFenceInfo { + /// Returns an `ExternalFenceInfo` with the specified `handle_type`. + #[inline] + pub fn handle_type(handle_type: ExternalFenceHandleType) -> Self { + Self { + handle_type, + _ne: crate::NonExhaustive(()), + } + } +} + +/// The properties for exporting or importing external handles, when a fence is created +/// with a specific configuration. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub struct ExternalFenceProperties { + /// Whether a handle can be exported to an external source with the queried + /// external handle type. + pub exportable: bool, + + /// Whether a handle can be imported from an external source with the queried + /// external handle type. + pub importable: bool, + + /// Which external handle types can be re-exported after the queried external handle type has + /// been imported. + pub export_from_imported_handle_types: ExternalFenceHandleTypes, + + /// Which external handle types can be enabled along with the queried external handle type + /// when creating the fence. + pub compatible_handle_types: ExternalFenceHandleTypes, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum FenceWaitError { - /// Not enough memory to complete the wait. +pub enum FenceError { + /// Not enough memory available. OomError(OomError), + /// The device has been lost. + DeviceLost, + /// The specified timeout wasn't long enough. Timeout, - /// The device has been lost. - DeviceLostError, + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, } -impl Error for FenceWaitError { +impl Error for FenceError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { match *self { - FenceWaitError::OomError(ref err) => Some(err), + Self::OomError(ref err) => Some(err), _ => None, } } } -impl Display for FenceWaitError { - #[inline] +impl Display for FenceError { fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - write!( - f, - "{}", - match *self { - FenceWaitError::OomError(_) => "no memory available", - FenceWaitError::Timeout => "the timeout has been reached", - FenceWaitError::DeviceLostError => "the device was lost", - } - ) + match self { + Self::OomError(_) => write!(f, "not enough memory available"), + Self::DeviceLost => write!(f, "the device was lost"), + Self::Timeout => write!(f, "the timeout has been reached"), + + Self::RequirementNotMet { + required_for, + requires_one_of, + } => write!( + f, + "a requirement was not met for: {}; requires one of: {}", + required_for, requires_one_of, + ), + } } } -impl From for FenceWaitError { +impl From for FenceError { #[inline] - fn from(err: VulkanError) -> FenceWaitError { + fn from(err: VulkanError) -> Self { match err { - VulkanError::OutOfHostMemory => FenceWaitError::OomError(From::from(err)), - VulkanError::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)), - VulkanError::DeviceLost => FenceWaitError::DeviceLostError, - _ => panic!("Unexpected error value"), + e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => { + Self::OomError(e.into()) + } + VulkanError::DeviceLost => Self::DeviceLost, + _ => panic!("unexpected error: {:?}", err), + } + } +} + +impl From for FenceError { + #[inline] + fn from(err: OomError) -> Self { + Self::OomError(err) + } +} + +impl From for FenceError { + #[inline] + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, } } } diff --git a/vulkano/src/sync/future/mod.rs b/vulkano/src/sync/future/mod.rs index 392bde90..edc4bb28 100644 --- a/vulkano/src/sync/future/mod.rs +++ b/vulkano/src/sync/future/mod.rs @@ -13,7 +13,7 @@ pub use self::{ now::{now, NowFuture}, semaphore_signal::SemaphoreSignalFuture, }; -use super::{AccessFlags, FenceWaitError, PipelineStages}; +use super::{AccessFlags, FenceError, PipelineStages}; use crate::{ buffer::sys::UnsafeBuffer, command_buffer::{ @@ -578,13 +578,14 @@ impl From for FlushError { } } -impl From for FlushError { +impl From for FlushError { #[inline] - fn from(err: FenceWaitError) -> FlushError { + fn from(err: FenceError) -> FlushError { match err { - FenceWaitError::OomError(err) => FlushError::OomError(err), - FenceWaitError::Timeout => FlushError::Timeout, - FenceWaitError::DeviceLostError => FlushError::DeviceLost, + FenceError::OomError(err) => FlushError::OomError(err), + FenceError::Timeout => FlushError::Timeout, + FenceError::DeviceLost => FlushError::DeviceLost, + FenceError::RequirementNotMet { .. } => unreachable!(), } } } diff --git a/vulkano/src/sync/mod.rs b/vulkano/src/sync/mod.rs index f453e99c..fa1f2e3f 100644 --- a/vulkano/src/sync/mod.rs +++ b/vulkano/src/sync/mod.rs @@ -105,7 +105,10 @@ pub use self::{ event::{Event, EventCreateInfo}, - fence::{Fence, FenceCreateInfo, FenceWaitError}, + fence::{ + ExternalFenceHandleType, ExternalFenceHandleTypes, ExternalFenceInfo, + ExternalFenceProperties, Fence, FenceCreateInfo, FenceError, FenceImportFlags, + }, future::{ now, AccessCheckError, AccessError, FenceSignalFuture, FlushError, GpuFuture, JoinFuture, NowFuture, SemaphoreSignalFuture, @@ -116,7 +119,8 @@ pub use self::{ }, semaphore::{ ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, ExternalSemaphoreInfo, - ExternalSemaphoreProperties, Semaphore, SemaphoreCreateInfo, SemaphoreCreationError, + ExternalSemaphoreProperties, Semaphore, SemaphoreCreateInfo, SemaphoreError, + SemaphoreImportFlags, }, }; use crate::device::Queue; diff --git a/vulkano/src/sync/semaphore.rs b/vulkano/src/sync/semaphore.rs index b9c44d32..299ef3a6 100644 --- a/vulkano/src/sync/semaphore.rs +++ b/vulkano/src/sync/semaphore.rs @@ -30,18 +30,29 @@ use std::{ pub struct Semaphore { handle: ash::vk::Semaphore, device: Arc, - must_put_in_pool: bool, export_handle_types: ExternalSemaphoreHandleTypes, + + must_put_in_pool: bool, } impl Semaphore { /// Creates a new `Semaphore`. + #[inline] pub fn new( device: Arc, create_info: SemaphoreCreateInfo, - ) -> Result { - let SemaphoreCreateInfo { + ) -> Result { + Self::validate_new(&device, &create_info)?; + + unsafe { Ok(Self::new_unchecked(device, create_info)?) } + } + + fn validate_new( + device: &Device, + create_info: &SemaphoreCreateInfo, + ) -> Result<(), SemaphoreError> { + let &SemaphoreCreateInfo { export_handle_types, _ne: _, } = create_info; @@ -50,7 +61,7 @@ impl Semaphore { if !(device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_external_semaphore) { - return Err(SemaphoreCreationError::RequirementNotMet { + return Err(SemaphoreError::RequirementNotMet { required_for: "`create_info.export_handle_types` is not empty", requires_one_of: RequiresOneOf { api_version: Some(Version::V1_1), @@ -61,34 +72,50 @@ impl Semaphore { } // VUID-VkExportSemaphoreCreateInfo-handleTypes-parameter - export_handle_types.validate_device(&device)?; + export_handle_types.validate_device(device)?; // VUID-VkExportSemaphoreCreateInfo-handleTypes-01124 // TODO: `vkGetPhysicalDeviceExternalSemaphoreProperties` can only be called with one // handle type, so which one do we give it? } - let mut create_info = ash::vk::SemaphoreCreateInfo::builder(); + Ok(()) + } - let mut export_semaphore_create_info = if !export_handle_types.is_empty() { - Some(ash::vk::ExportSemaphoreCreateInfo { + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn new_unchecked( + device: Arc, + create_info: SemaphoreCreateInfo, + ) -> Result { + let SemaphoreCreateInfo { + export_handle_types, + _ne: _, + } = create_info; + + let mut create_info_vk = ash::vk::SemaphoreCreateInfo { + flags: ash::vk::SemaphoreCreateFlags::empty(), + ..Default::default() + }; + let mut export_semaphore_create_info_vk = None; + + if !export_handle_types.is_empty() { + let _ = export_semaphore_create_info_vk.insert(ash::vk::ExportSemaphoreCreateInfo { handle_types: export_handle_types.into(), ..Default::default() - }) - } else { - None + }); }; - if let Some(info) = export_semaphore_create_info.as_mut() { - create_info = create_info.push_next(info); + if let Some(info) = export_semaphore_create_info_vk.as_mut() { + info.p_next = create_info_vk.p_next; + create_info_vk.p_next = info as *const _ as *const _; } - let handle = unsafe { + let handle = { let fns = device.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.create_semaphore)( device.internal_object(), - &create_info.build(), + &create_info_vk, ptr::null(), output.as_mut_ptr(), ) @@ -100,39 +127,13 @@ impl Semaphore { Ok(Semaphore { device, handle, - must_put_in_pool: false, export_handle_types, + + must_put_in_pool: false, }) } - /// Takes a semaphore from the vulkano-provided semaphore pool. - /// If the pool is empty, a new semaphore will be allocated. - /// Upon `drop`, the semaphore is put back into the pool. - /// - /// For most applications, using the pool should be preferred, - /// in order to avoid creating new semaphores every frame. - pub fn from_pool(device: Arc) -> Result { - let handle = device.semaphore_pool().lock().pop(); - let semaphore = match handle { - Some(handle) => Semaphore { - device, - handle, - must_put_in_pool: true, - - export_handle_types: ExternalSemaphoreHandleTypes::empty(), - }, - None => { - // Pool is empty, alloc new semaphore - let mut semaphore = Semaphore::new(device, Default::default())?; - semaphore.must_put_in_pool = true; - semaphore - } - }; - - Ok(semaphore) - } - /// Creates a new `Semaphore` from an ash-handle /// # Safety /// The `handle` has to be a valid vulkan object handle and @@ -150,58 +151,138 @@ impl Semaphore { Semaphore { device, handle, - must_put_in_pool: false, export_handle_types, + + must_put_in_pool: false, } } + /// Takes a semaphore from the vulkano-provided semaphore pool. + /// If the pool is empty, a new semaphore will be allocated. + /// Upon `drop`, the semaphore is put back into the pool. + /// + /// For most applications, using the pool should be preferred, + /// in order to avoid creating new semaphores every frame. + pub fn from_pool(device: Arc) -> Result { + let handle = device.semaphore_pool().lock().pop(); + let semaphore = match handle { + Some(handle) => Semaphore { + device, + handle, + + export_handle_types: ExternalSemaphoreHandleTypes::empty(), + + must_put_in_pool: true, + }, + None => { + // Pool is empty, alloc new semaphore + let mut semaphore = Semaphore::new(device, Default::default())?; + semaphore.must_put_in_pool = true; + semaphore + } + }; + + Ok(semaphore) + } + + /// Exports the semaphore into a POSIX file descriptor. The caller owns the returned `File`. + /// /// # Safety /// /// - The semaphore must not be used, or have been used, to acquire a swapchain image. - pub unsafe fn export_opaque_fd(&self) -> Result { - let fns = self.device.fns(); + #[inline] + pub unsafe fn export_fd( + &self, + handle_type: ExternalSemaphoreHandleType, + ) -> Result { + self.validate_export_fd(handle_type)?; - // VUID-VkSemaphoreGetFdInfoKHR-handleType-01132 - if !self.export_handle_types.opaque_fd { - return Err(SemaphoreExportError::HandleTypeNotSupported { - handle_type: ExternalSemaphoreHandleType::OpaqueFd, + Ok(self.export_fd_unchecked(handle_type)?) + } + + fn validate_export_fd( + &self, + handle_type: ExternalSemaphoreHandleType, + ) -> Result<(), SemaphoreError> { + if !self.device.enabled_extensions().khr_external_semaphore_fd { + return Err(SemaphoreError::RequirementNotMet { + required_for: "`export_fd`", + requires_one_of: RequiresOneOf { + device_extensions: &["khr_external_semaphore_fd"], + ..Default::default() + }, }); } - assert!(self.device.enabled_extensions().khr_external_semaphore); - assert!(self.device.enabled_extensions().khr_external_semaphore_fd); + // VUID-VkMemoryGetFdInfoKHR-handleType-parameter + handle_type.validate_device(&self.device)?; + + // VUID-VkSemaphoreGetFdInfoKHR-handleType-01132 + if !self.export_handle_types.intersects(&handle_type.into()) { + return Err(SemaphoreError::HandleTypeNotSupported { handle_type }); + } // VUID-VkSemaphoreGetFdInfoKHR-semaphore-01133 // Can't validate for swapchain. - #[cfg(not(unix))] - unreachable!("`khr_external_semaphore_fd` was somehow enabled on a non-Unix system"); + // VUID-VkSemaphoreGetFdInfoKHR-handleType-01134 + // TODO: - #[cfg(unix)] - { - use std::os::unix::io::FromRawFd; + // VUID-VkSemaphoreGetFdInfoKHR-handleType-01135 + // TODO: - let fd = { - let info = ash::vk::SemaphoreGetFdInfoKHR { - semaphore: self.handle, - handle_type: ash::vk::ExternalSemaphoreHandleTypeFlagsKHR::OPAQUE_FD, - ..Default::default() - }; - - let mut output = MaybeUninit::uninit(); - (fns.khr_external_semaphore_fd.get_semaphore_fd_khr)( - self.device.internal_object(), - &info, - output.as_mut_ptr(), - ) - .result() - .map_err(VulkanError::from)?; - output.assume_init() - }; - let file = File::from_raw_fd(fd); - Ok(file) + // VUID-VkSemaphoreGetFdInfoKHR-handleType-01136 + if !matches!( + handle_type, + ExternalSemaphoreHandleType::OpaqueFd | ExternalSemaphoreHandleType::SyncFd + ) { + return Err(SemaphoreError::HandleTypeNotSupported { handle_type }); } + + // VUID-VkSemaphoreGetFdInfoKHR-handleType-03253 + // TODO: + + // VUID-VkSemaphoreGetFdInfoKHR-handleType-03254 + // TODO: + + Ok(()) + } + + #[cfg(not(unix))] + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn export_fd_unchecked( + &self, + _handle_type: ExternalSemaphoreHandleType, + ) -> Result { + unreachable!("`khr_external_semaphore_fd` was somehow enabled on a non-Unix system"); + } + + #[cfg(unix)] + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn export_fd_unchecked( + &self, + handle_type: ExternalSemaphoreHandleType, + ) -> Result { + use std::os::unix::io::FromRawFd; + + let info = ash::vk::SemaphoreGetFdInfoKHR { + semaphore: self.handle, + handle_type: handle_type.into(), + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + let fns = self.device.fns(); + (fns.khr_external_semaphore_fd.get_semaphore_fd_khr)( + self.device.internal_object(), + &info, + output.as_mut_ptr(), + ) + .result() + .map_err(VulkanError::from)?; + + Ok(File::from_raw_fd(output.assume_init())) } } @@ -257,73 +338,6 @@ impl Hash for Semaphore { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum SemaphoreCreationError { - /// Not enough memory available. - OomError(OomError), - - RequirementNotMet { - required_for: &'static str, - requires_one_of: RequiresOneOf, - }, -} - -impl Error for SemaphoreCreationError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match *self { - Self::OomError(ref err) => Some(err), - _ => None, - } - } -} - -impl Display for SemaphoreCreationError { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - match self { - Self::OomError(_) => write!(f, "not enough memory available"), - - Self::RequirementNotMet { - required_for, - requires_one_of, - } => write!( - f, - "a requirement was not met for: {}; requires one of: {}", - required_for, requires_one_of, - ), - } - } -} - -impl From for SemaphoreCreationError { - #[inline] - fn from(err: VulkanError) -> Self { - match err { - e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => { - Self::OomError(e.into()) - } - _ => panic!("unexpected error: {:?}", err), - } - } -} - -impl From for SemaphoreCreationError { - #[inline] - fn from(err: OomError) -> Self { - Self::OomError(err) - } -} - -impl From for SemaphoreCreationError { - #[inline] - fn from(err: RequirementNotMet) -> Self { - Self::RequirementNotMet { - required_for: err.required_for, - requires_one_of: err.requires_one_of, - } - } -} - /// Parameters to create a new `Semaphore`. #[derive(Clone, Debug)] pub struct SemaphoreCreateInfo { @@ -346,7 +360,7 @@ impl Default for SemaphoreCreateInfo { } vulkan_enum! { - /// Describes the handle type used for Vulkan external semaphore APIs. + /// The handle type used for Vulkan external semaphore APIs. #[non_exhaustive] ExternalSemaphoreHandleType = ExternalSemaphoreHandleTypeFlags(u32); @@ -374,7 +388,7 @@ vulkan_enum! { } vulkan_bitflags! { - /// A mask of multiple handle types. + /// A mask of multiple external semaphore handle types. #[non_exhaustive] ExternalSemaphoreHandleTypes = ExternalSemaphoreHandleTypeFlags(u32); @@ -401,6 +415,33 @@ vulkan_bitflags! { */ } +impl From for ExternalSemaphoreHandleTypes { + #[inline] + fn from(val: ExternalSemaphoreHandleType) -> Self { + let mut result = Self::empty(); + + match val { + ExternalSemaphoreHandleType::OpaqueFd => result.opaque_fd = true, + ExternalSemaphoreHandleType::OpaqueWin32 => result.opaque_win32 = true, + ExternalSemaphoreHandleType::OpaqueWin32Kmt => result.opaque_win32_kmt = true, + ExternalSemaphoreHandleType::D3D12Fence => result.d3d12_fence = true, + ExternalSemaphoreHandleType::SyncFd => result.sync_fd = true, + } + + result + } +} + +vulkan_bitflags! { + /// Additional parameters for a semaphore payload import. + #[non_exhaustive] + SemaphoreImportFlags = SemaphoreImportFlags(u32); + + /// The semaphore payload will be imported only temporarily, regardless of the permanence of the + /// imported handle type. + temporary = TEMPORARY, +} + /// The semaphore configuration to query in /// [`PhysicalDevice::external_semaphore_properties`](crate::device::physical::PhysicalDevice::external_semaphore_properties). #[derive(Clone, Debug)] @@ -440,15 +481,21 @@ pub struct ExternalSemaphoreProperties { pub export_from_imported_handle_types: ExternalSemaphoreHandleTypes, /// Which external handle types can be enabled along with the queried external handle type - /// when creating the buffer or image. + /// when creating the semaphore. pub compatible_handle_types: ExternalSemaphoreHandleTypes, } +/// Error that can be returned from operations on a semaphore. #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum SemaphoreExportError { +pub enum SemaphoreError { /// Not enough memory available. OomError(OomError), + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, + /// The requested export handle type was not provided in `export_handle_types` when creating the /// semaphore. HandleTypeNotSupported { @@ -456,10 +503,30 @@ pub enum SemaphoreExportError { }, } -impl Display for SemaphoreExportError { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { +impl Error for SemaphoreError { + #[inline] + fn source(&self) -> Option<&(dyn Error + 'static)> { match *self { + Self::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl Display for SemaphoreError { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + match self { Self::OomError(_) => write!(f, "not enough memory available"), + + Self::RequirementNotMet { + required_for, + requires_one_of, + } => write!( + f, + "a requirement was not met for: {}; requires one of: {}", + required_for, requires_one_of, + ), + Self::HandleTypeNotSupported { handle_type } => write!( f, "the requested export handle type ({:?}) was not provided in `export_handle_types` when creating the semaphore", @@ -469,7 +536,7 @@ impl Display for SemaphoreExportError { } } -impl From for SemaphoreExportError { +impl From for SemaphoreError { #[inline] fn from(err: VulkanError) -> Self { match err { @@ -481,25 +548,26 @@ impl From for SemaphoreExportError { } } -impl Error for SemaphoreExportError { - #[inline] - fn source(&self) -> Option<&(dyn Error + 'static)> { - match *self { - Self::OomError(ref err) => Some(err), - _ => None, - } - } -} - -impl From for SemaphoreExportError { +impl From for SemaphoreError { #[inline] fn from(err: OomError) -> Self { Self::OomError(err) } } +impl From for SemaphoreError { + #[inline] + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, + } + } +} + #[cfg(test)] mod tests { + use super::ExternalSemaphoreHandleType; use crate::{ device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo}, instance::{Instance, InstanceCreateInfo, InstanceExtensions}, @@ -587,6 +655,9 @@ mod tests { }, ) .unwrap(); - let _fd = unsafe { sem.export_opaque_fd().unwrap() }; + let _fd = unsafe { + sem.export_fd(ExternalSemaphoreHandleType::OpaqueFd) + .unwrap() + }; } }