diff --git a/examples/src/bin/gl-interop.rs b/examples/src/bin/gl-interop.rs index 93a8135946..d0c69121f8 100644 --- a/examples/src/bin/gl-interop.rs +++ b/examples/src/bin/gl-interop.rs @@ -153,16 +153,12 @@ mod linux { .unwrap(), ); - let acquire_fd = unsafe { - acquire_sem - .export_fd(ExternalSemaphoreHandleType::OpaqueFd) - .unwrap() - }; - let release_fd = unsafe { - release_sem - .export_fd(ExternalSemaphoreHandleType::OpaqueFd) - .unwrap() - }; + let acquire_fd = acquire_sem + .export_fd(ExternalSemaphoreHandleType::OpaqueFd) + .unwrap(); + let release_fd = 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/queue.rs b/vulkano/src/device/queue.rs index ca8bf37485..ed364abb8b 100644 --- a/vulkano/src/device/queue.rs +++ b/vulkano/src/device/queue.rs @@ -18,7 +18,7 @@ use crate::{ BindSparseInfo, SparseBufferMemoryBind, SparseImageMemoryBind, SparseImageOpaqueMemoryBind, }, swapchain::{PresentInfo, SwapchainPresentInfo}, - sync::{Fence, FenceState, PipelineStage}, + sync::{Fence, FenceState, PipelineStage, SemaphoreState}, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; use parking_lot::{Mutex, MutexGuard}; @@ -165,19 +165,45 @@ impl<'a> QueueGuard<'a> { bind_infos: impl IntoIterator, fence: Option>, ) -> Result<(), VulkanError> { + let bind_infos: SmallVec<[_; 4]> = bind_infos.into_iter().collect(); + let mut bind_infos_state = bind_infos + .iter() + .map(|bind_info| { + ( + bind_info + .wait_semaphores + .iter() + .map(|semaphore| semaphore.state()) + .collect(), + bind_info + .signal_semaphores + .iter() + .map(|semaphore| semaphore.state()) + .collect(), + ) + }) + .collect(); + self.bind_sparse_unchecked_locked( - bind_infos.into_iter().collect(), + &bind_infos, fence.as_ref().map(|fence| { let state = fence.state(); (fence, state) }), + &mut bind_infos_state, ) } unsafe fn bind_sparse_unchecked_locked( &mut self, - bind_infos: SmallVec<[BindSparseInfo; 4]>, + bind_infos: &SmallVec<[BindSparseInfo; 4]>, fence: Option<(&Arc, MutexGuard<'_, FenceState>)>, + bind_infos_state: &mut SmallVec< + [( + SmallVec<[MutexGuard<'_, SemaphoreState>; 4]>, + SmallVec<[MutexGuard<'_, SemaphoreState>; 4]>, + ); 4], + >, ) -> Result<(), VulkanError> { struct PerBindSparseInfo { wait_semaphores_vk: SmallVec<[ash::vk::Semaphore; 4]>, @@ -439,12 +465,24 @@ impl<'a> QueueGuard<'a> { .result() .map_err(VulkanError::from)?; + for (wait_semaphores_state, signal_semaphores_state) in bind_infos_state { + for semaphore in wait_semaphores_state { + semaphore.add_queue_wait(self.queue); + } + + for semaphore in signal_semaphores_state { + semaphore.add_queue_signal(self.queue); + } + } + let fence = fence.map(|(fence, mut state)| { - state.add_to_queue(self.queue); + state.add_queue_signal(self.queue); fence.clone() }); - self.state.operations.push_back((bind_infos.into(), fence)); + self.state + .operations + .push_back((bind_infos.clone().into(), fence)); Ok(()) } @@ -454,7 +492,20 @@ impl<'a> QueueGuard<'a> { pub unsafe fn present_unchecked( &mut self, present_info: PresentInfo, - ) -> impl ExactSizeIterator> { + ) -> Result>, VulkanError> { + let mut wait_semaphores_state = present_info + .wait_semaphores + .iter() + .map(|semaphore| semaphore.state()) + .collect(); + self.present_unchecked_locked(&present_info, &mut wait_semaphores_state) + } + + unsafe fn present_unchecked_locked( + &mut self, + present_info: &PresentInfo, + wait_semaphores_state: &mut SmallVec<[MutexGuard<'_, SemaphoreState>; 4]>, + ) -> Result>, VulkanError> { let PresentInfo { ref wait_semaphores, ref swapchain_infos, @@ -550,7 +601,22 @@ impl<'a> QueueGuard<'a> { } let fns = self.queue.device().fns(); - let _ = (fns.khr_swapchain.queue_present_khr)(self.queue.handle, &info_vk); + let result = (fns.khr_swapchain.queue_present_khr)(self.queue.handle, &info_vk); + + // Per the documentation of `vkQueuePresentKHR`, certain results indicate that the whole + // operation has failed, while others only indicate failure of a particular present. + // If we got a result that is not one of these per-present ones, we return it directly. + // Otherwise, we consider the present to be enqueued. + if !matches!( + result, + ash::vk::Result::SUCCESS + | ash::vk::Result::SUBOPTIMAL_KHR + | ash::vk::Result::ERROR_OUT_OF_DATE_KHR + | ash::vk::Result::ERROR_SURFACE_LOST_KHR + | ash::vk::Result::ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT, + ) { + return Err(VulkanError::from(result)); + } // If a presentation results in a loss of full-screen exclusive mode, // signal that to the relevant swapchain. @@ -563,15 +629,19 @@ impl<'a> QueueGuard<'a> { } } - // Some presents may succeed and some may fail. Since we don't know what the implementation - // is going to do, we act as if they all succeeded. - self.state.operations.push_back((present_info.into(), None)); + for semaphore in wait_semaphores_state { + semaphore.add_queue_wait(self.queue); + } - results.into_iter().map(|result| match result { + self.state + .operations + .push_back((present_info.clone().into(), None)); + + Ok(results.into_iter().map(|result| match result { ash::vk::Result::SUCCESS => Ok(false), ash::vk::Result::SUBOPTIMAL_KHR => Ok(true), err => Err(VulkanError::from(err)), - }) + })) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] @@ -580,19 +650,45 @@ impl<'a> QueueGuard<'a> { submit_infos: impl IntoIterator, fence: Option>, ) -> Result<(), VulkanError> { + let submit_infos: SmallVec<[_; 4]> = submit_infos.into_iter().collect(); + let mut submit_infos_state = submit_infos + .iter() + .map(|submit_info| { + ( + submit_info + .wait_semaphores + .iter() + .map(|semaphore_submit_info| semaphore_submit_info.semaphore.state()) + .collect(), + submit_info + .signal_semaphores + .iter() + .map(|semaphore_submit_info| semaphore_submit_info.semaphore.state()) + .collect(), + ) + }) + .collect(); + self.submit_unchecked_locked( - submit_infos.into_iter().collect(), + &submit_infos, fence.as_ref().map(|fence| { let state = fence.state(); (fence, state) }), + &mut submit_infos_state, ) } unsafe fn submit_unchecked_locked( &mut self, - submit_infos: SmallVec<[SubmitInfo; 4]>, + submit_infos: &SmallVec<[SubmitInfo; 4]>, fence: Option<(&Arc, MutexGuard<'_, FenceState>)>, + submit_infos_state: &mut SmallVec< + [( + SmallVec<[MutexGuard<'_, SemaphoreState>; 4]>, + SmallVec<[MutexGuard<'_, SemaphoreState>; 4]>, + ); 4], + >, ) -> Result<(), VulkanError> { if self.queue.device.enabled_features().synchronization2 { struct PerSubmitInfo { @@ -829,14 +925,24 @@ impl<'a> QueueGuard<'a> { .map_err(VulkanError::from)?; } + for (wait_semaphores_state, signal_semaphores_state) in submit_infos_state { + for semaphore in wait_semaphores_state { + semaphore.add_queue_wait(self.queue); + } + + for semaphore in signal_semaphores_state { + semaphore.add_queue_signal(self.queue); + } + } + let fence = fence.map(|(fence, mut state)| { - state.add_to_queue(self.queue); + state.add_queue_signal(self.queue); fence.clone() }); self.state .operations - .push_back((submit_infos.into(), fence)); + .push_back((submit_infos.clone().into(), fence)); Ok(()) } @@ -1010,6 +1116,127 @@ impl<'a> QueueGuard<'a> { } } +#[derive(Debug, Default)] +struct QueueState { + operations: VecDeque<(QueueOperation, Option>)>, +} + +impl QueueState { + fn wait_idle(&mut self, device: &Device, handle: ash::vk::Queue) -> Result<(), OomError> { + unsafe { + let fns = device.fns(); + (fns.v1_0.queue_wait_idle)(handle) + .result() + .map_err(VulkanError::from)?; + + // Since we now know that the queue is finished with all work, + // we can safely release all resources. + for (operation, _) in take(&mut self.operations) { + operation.unlock(); + } + + Ok(()) + } + } + + /// Called by `fence` when it finds that it is signaled. + fn fence_signaled(&mut self, fence: &Fence) { + // Find the most recent operation that uses `fence`. + let fence_index = self + .operations + .iter() + .enumerate() + .rev() + .find_map(|(index, (_, f))| { + f.as_ref().map_or(false, |f| **f == *fence).then_some(index) + }); + + if let Some(index) = fence_index { + // Remove all operations up to this index, and perform cleanup if needed. + for (operation, fence) in self.operations.drain(..index + 1) { + unsafe { + operation.unlock(); + + if let Some(fence) = fence { + fence.state().set_signal_finished(); + } + } + } + } + } +} + +#[derive(Debug)] +enum QueueOperation { + BindSparse(SmallVec<[BindSparseInfo; 4]>), + Present(PresentInfo), + Submit(SmallVec<[SubmitInfo; 4]>), +} + +impl QueueOperation { + unsafe fn unlock(self) { + match self { + QueueOperation::BindSparse(bind_infos) => { + for bind_info in bind_infos { + for semaphore in bind_info.wait_semaphores { + semaphore.state().set_wait_finished(); + } + + for semaphore in bind_info.signal_semaphores { + semaphore.state().set_signal_finished(); + } + } + + // TODO: Do we need to unlock buffers and images here? + } + QueueOperation::Present(present_info) => { + for semaphore in present_info.wait_semaphores { + semaphore.state().set_wait_finished(); + } + } + QueueOperation::Submit(submit_infos) => { + for submit_info in submit_infos { + for semaphore_submit_info in submit_info.wait_semaphores { + semaphore_submit_info.semaphore.state().set_wait_finished(); + } + + for semaphore_submit_info in submit_info.signal_semaphores { + semaphore_submit_info + .semaphore + .state() + .set_signal_finished(); + } + + for command_buffer in submit_info.command_buffers { + command_buffer.unlock(); + } + } + } + } + } +} + +impl From> for QueueOperation { + #[inline] + fn from(val: SmallVec<[BindSparseInfo; 4]>) -> Self { + Self::BindSparse(val) + } +} + +impl From for QueueOperation { + #[inline] + fn from(val: PresentInfo) -> Self { + Self::Present(val) + } +} + +impl From> for QueueOperation { + #[inline] + fn from(val: SmallVec<[SubmitInfo; 4]>) -> Self { + Self::Submit(val) + } +} + /// Properties of a queue family in a physical device. #[derive(Clone, Debug)] #[non_exhaustive] @@ -1141,102 +1368,6 @@ impl From for QueueError { } } -#[derive(Debug, Default)] -struct QueueState { - operations: VecDeque<(QueueOperation, Option>)>, -} - -impl QueueState { - fn wait_idle(&mut self, device: &Device, handle: ash::vk::Queue) -> Result<(), OomError> { - unsafe { - let fns = device.fns(); - (fns.v1_0.queue_wait_idle)(handle) - .result() - .map_err(VulkanError::from)?; - - // Since we now know that the queue is finished with all work, - // we can safely release all resources. - for (operation, _) in take(&mut self.operations) { - operation.unlock(); - } - - Ok(()) - } - } - - /// Called by `fence` when it finds that it is signaled. - fn fence_signaled(&mut self, fence: &Fence) { - // Find the most recent operation that uses `fence`. - let fence_index = self - .operations - .iter() - .enumerate() - .rev() - .find_map(|(index, (_, f))| { - f.as_ref().map_or(false, |f| **f == *fence).then_some(index) - }); - - if let Some(index) = fence_index { - // Remove all operations up to this index, and perform cleanup if needed. - for (operation, fence) in self.operations.drain(..index + 1) { - unsafe { - operation.unlock(); - - if let Some(fence) = fence { - fence.state().set_finished(); - } - } - } - } - } -} - -#[derive(Debug)] -enum QueueOperation { - BindSparse(SmallVec<[BindSparseInfo; 4]>), - Present(PresentInfo), - Submit(SmallVec<[SubmitInfo; 4]>), -} - -impl QueueOperation { - unsafe fn unlock(self) { - match self { - QueueOperation::BindSparse(_bind_infos) => { - // TODO: Do we need to unlock buffers and images here? - } - QueueOperation::Present(_) => (), - QueueOperation::Submit(submit_infos) => { - for submit_info in submit_infos { - for command_buffer in submit_info.command_buffers { - command_buffer.unlock(); - } - } - } - } - } -} - -impl From> for QueueOperation { - #[inline] - fn from(val: SmallVec<[BindSparseInfo; 4]>) -> Self { - Self::BindSparse(val) - } -} - -impl From for QueueOperation { - #[inline] - fn from(val: PresentInfo) -> Self { - Self::Present(val) - } -} - -impl From> for QueueOperation { - #[inline] - fn from(val: SmallVec<[SubmitInfo; 4]>) -> Self { - Self::Submit(val) - } -} - #[cfg(test)] mod tests { use crate::sync::Fence; diff --git a/vulkano/src/swapchain/swapchain.rs b/vulkano/src/swapchain/swapchain.rs index a4f581ec41..25b914c92c 100644 --- a/vulkano/src/swapchain/swapchain.rs +++ b/vulkano/src/swapchain/swapchain.rs @@ -2145,7 +2145,7 @@ where Ok(self .queue - .with(|mut q| q.present_unchecked(present_info)) + .with(|mut q| q.present_unchecked(present_info))? .map(|r| r.map(|_| ())) .fold(Ok(()), Result::and)?) } @@ -2313,6 +2313,11 @@ pub unsafe fn acquire_next_image_raw( err => return Err(VulkanError::from(err).into()), }; + if let Some(semaphore) = semaphore { + let mut state = semaphore.state(); + state.swapchain_acquire(); + } + if let Some(fence) = fence { let mut state = fence.state(); state.import_swapchain_acquire(); diff --git a/vulkano/src/sync/fence.rs b/vulkano/src/sync/fence.rs index 1560c2be05..3a9bf421c3 100644 --- a/vulkano/src/sync/fence.rs +++ b/vulkano/src/sync/fence.rs @@ -52,9 +52,6 @@ use std::{ pub struct Fence { handle: ash::vk::Fence, device: Arc, - - // Indicates whether this fence was taken from the fence pool. - // If true, will be put back into fence pool on drop. must_put_in_pool: bool, export_handle_types: ExternalFenceHandleTypes, @@ -732,7 +729,7 @@ impl Fence { // VUID-VkFenceGetWin32HandleInfoKHR-handleType-01449 if matches!(handle_type, ExternalFenceHandleType::OpaqueWin32) - && state.opaque_win32_exported() + && state.is_exported(handle_type) { return Err(FenceError::AlreadyExported); } @@ -828,7 +825,7 @@ impl Fence { /// # Safety /// /// - If in `import_fence_fd_info`, `handle_type` is `ExternalHandleType::OpaqueFd`, - /// then `file` must have been exported from Vulkan or a compatible API, + /// then `file` must represent a fence that was exported from Vulkan or a compatible API, /// with a driver and device UUID equal to those of the device that owns `self`. #[cfg(unix)] #[inline] @@ -906,7 +903,6 @@ impl Fence { self.import_fd_unchecked_locked(import_fence_fd_info, &mut state) } - #[cfg(unix)] #[cfg(unix)] unsafe fn import_fd_unchecked_locked( &self, @@ -947,10 +943,9 @@ impl Fence { /// /// # Safety /// - /// - If in `import_fence_win32_handle_info`, `handle_type` is - /// `ExternalHandleType::OpaqueWin32` or `ExternalHandleType::OpaqueWin32Kmt`, - /// then `handle` must have been exported from Vulkan or a compatible API, - /// with a driver and device UUID equal to those of the device that owns `self`. + /// - In `import_fence_win32_handle_info`, `handle` must represent a fence that was exported + /// from Vulkan or a compatible API, with a driver and device UUID equal to those of the + /// device that owns `self`. #[cfg(windows)] #[inline] pub unsafe fn import_win32_handle( @@ -1117,10 +1112,10 @@ impl Hash for Fence { #[derive(Debug, Default)] pub(crate) struct FenceState { is_signaled: bool, - in_queue: Option>, + pending_signal: Option>, reference_exported: bool, - opaque_win32_exported: bool, + exported_handle_types: ExternalFenceHandleTypes, current_import: Option, permanent_import: Option, } @@ -1139,7 +1134,7 @@ impl FenceState { #[inline] fn is_in_queue(&self) -> bool { - self.in_queue.is_some() + self.pending_signal.is_some() } /// Returns whether there are any potential external references to the fence payload. @@ -1151,13 +1146,13 @@ impl FenceState { #[allow(dead_code)] #[inline] - fn opaque_win32_exported(&self) -> bool { - self.opaque_win32_exported + fn is_exported(&self, handle_type: ExternalFenceHandleType) -> bool { + self.exported_handle_types.intersects(&handle_type.into()) } #[inline] - pub(crate) unsafe fn add_to_queue(&mut self, queue: &Arc) { - self.in_queue = Some(Arc::downgrade(queue)); + pub(crate) unsafe fn add_queue_signal(&mut self, queue: &Arc) { + self.pending_signal = Some(Arc::downgrade(queue)); } /// Called when a fence first discovers that it is signaled. @@ -1168,18 +1163,18 @@ impl FenceState { // Fences with external references can't be used to determine queue completion. if self.has_external_reference() { - self.in_queue = None; + self.pending_signal = None; None } else { - self.in_queue.take().and_then(|queue| queue.upgrade()) + self.pending_signal.take().and_then(|queue| queue.upgrade()) } } /// Called when a queue is unlocking resources. #[inline] - pub(crate) unsafe fn set_finished(&mut self) { + pub(crate) unsafe fn set_signal_finished(&mut self) { self.is_signaled = true; - self.in_queue = None; + self.pending_signal = None; } #[inline] @@ -1189,11 +1184,10 @@ impl FenceState { self.is_signaled = false; } + #[allow(dead_code)] #[inline] unsafe fn export(&mut self, handle_type: ExternalFenceHandleType) { - if matches!(handle_type, ExternalFenceHandleType::OpaqueWin32) { - self.opaque_win32_exported = true; - } + self.exported_handle_types |= handle_type.into(); if handle_type.has_copy_transference() { self.reset(); @@ -1202,6 +1196,7 @@ impl FenceState { } } + #[allow(dead_code)] #[inline] unsafe fn import(&mut self, handle_type: ExternalFenceHandleType, temporary: bool) { debug_assert!(!self.is_in_queue()); diff --git a/vulkano/src/sync/future/fence_signal.rs b/vulkano/src/sync/future/fence_signal.rs index 64b956d5b7..9d38faeedd 100644 --- a/vulkano/src/sync/future/fence_signal.rs +++ b/vulkano/src/sync/future/fence_signal.rs @@ -317,7 +317,7 @@ where } queue - .with(|mut q| q.present_unchecked(present_info)) + .with(|mut q| q.present_unchecked(present_info))? .map(|r| r.map(|_| ())) .fold(Ok(()), Result::and) }; diff --git a/vulkano/src/sync/future/semaphore_signal.rs b/vulkano/src/sync/future/semaphore_signal.rs index 37e8eb9b39..f6b2992f24 100644 --- a/vulkano/src/sync/future/semaphore_signal.rs +++ b/vulkano/src/sync/future/semaphore_signal.rs @@ -166,7 +166,7 @@ where } queue.with(|mut q| { - q.present_unchecked(present_info) + q.present_unchecked(present_info)? .map(|r| r.map(|_| ())) .fold(Ok(()), Result::and)?; // FIXME: problematic because if we return an error and flush() is called again, then we'll submit the present twice diff --git a/vulkano/src/sync/mod.rs b/vulkano/src/sync/mod.rs index 2c61ad1f1d..7724e3bb4a 100644 --- a/vulkano/src/sync/mod.rs +++ b/vulkano/src/sync/mod.rs @@ -103,7 +103,6 @@ //! TODO: talk about fence + semaphore simultaneously //! TODO: talk about using fences to clean up -pub(crate) use self::fence::FenceState; #[cfg(unix)] pub use self::fence::ImportFenceFdInfo; #[cfg(windows)] @@ -128,6 +127,7 @@ pub use self::{ SemaphoreImportFlags, }, }; +pub(crate) use self::{fence::FenceState, semaphore::SemaphoreState}; use crate::device::Queue; use std::sync::Arc; diff --git a/vulkano/src/sync/semaphore.rs b/vulkano/src/sync/semaphore.rs index 9f537036ac..1c103ec1cb 100644 --- a/vulkano/src/sync/semaphore.rs +++ b/vulkano/src/sync/semaphore.rs @@ -8,10 +8,11 @@ // according to those terms. use crate::{ - device::{Device, DeviceOwned}, + device::{Device, DeviceOwned, Queue}, macros::{vulkan_bitflags, vulkan_enum}, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; +use parking_lot::{Mutex, MutexGuard}; use std::{ error::Error, fmt::{Display, Error as FmtError, Formatter}, @@ -19,7 +20,7 @@ use std::{ hash::{Hash, Hasher}, mem::MaybeUninit, ptr, - sync::Arc, + sync::{Arc, Weak}, }; /// Used to provide synchronization between command buffers during their execution. @@ -30,10 +31,11 @@ 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, + state: Mutex, } impl Semaphore { @@ -75,8 +77,26 @@ impl Semaphore { 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? + for handle_type in export_handle_types.into_iter() { + let external_semaphore_properties = unsafe { + device + .physical_device() + .external_semaphore_properties_unchecked( + ExternalSemaphoreInfo::handle_type(handle_type), + ) + }; + + if !external_semaphore_properties.exportable { + return Err(SemaphoreError::HandleTypeNotExportable { handle_type }); + } + + if !external_semaphore_properties + .compatible_handle_types + .contains(&export_handle_types) + { + return Err(SemaphoreError::ExportHandleTypesNotCompatible); + } + } } Ok(()) @@ -128,10 +148,11 @@ impl Semaphore { Ok(Semaphore { device, handle, + must_put_in_pool: false, export_handle_types, - must_put_in_pool: false, + state: Mutex::new(Default::default()), }) } @@ -148,10 +169,11 @@ impl Semaphore { Some(handle) => Semaphore { device, handle, + must_put_in_pool: true, export_handle_types: ExternalSemaphoreHandleTypes::empty(), - must_put_in_pool: true, + state: Mutex::new(Default::default()), }, None => { // Pool is empty, alloc new semaphore @@ -184,31 +206,32 @@ impl Semaphore { Semaphore { device, handle, + must_put_in_pool: false, export_handle_types, - must_put_in_pool: false, + state: Mutex::new(Default::default()), } } /// 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. + #[cfg(unix)] #[inline] - pub unsafe fn export_fd( + pub fn export_fd( &self, handle_type: ExternalSemaphoreHandleType, ) -> Result { - self.validate_export_fd(handle_type)?; + let mut state = self.state.lock(); + self.validate_export_fd(handle_type, &state)?; - Ok(self.export_fd_unchecked(handle_type)?) + unsafe { Ok(self.export_fd_unchecked_locked(handle_type, &mut state)?) } } + #[cfg(unix)] fn validate_export_fd( &self, handle_type: ExternalSemaphoreHandleType, + state: &SemaphoreState, ) -> Result<(), SemaphoreError> { if !self.device.enabled_extensions().khr_external_semaphore_fd { return Err(SemaphoreError::RequirementNotMet { @@ -225,51 +248,76 @@ impl Semaphore { // VUID-VkSemaphoreGetFdInfoKHR-handleType-01132 if !self.export_handle_types.intersects(&handle_type.into()) { - return Err(SemaphoreError::HandleTypeNotSupported { handle_type }); + return Err(SemaphoreError::HandleTypeNotEnabled); } // VUID-VkSemaphoreGetFdInfoKHR-semaphore-01133 - // Can't validate for swapchain. + if let Some(imported_handle_type) = state.current_import { + match imported_handle_type { + ImportType::SwapchainAcquire => { + return Err(SemaphoreError::ImportedForSwapchainAcquire) + } + ImportType::ExternalSemaphore(imported_handle_type) => { + let external_semaphore_properties = unsafe { + self.device + .physical_device() + .external_semaphore_properties_unchecked( + ExternalSemaphoreInfo::handle_type(handle_type), + ) + }; + + if !external_semaphore_properties + .export_from_imported_handle_types + .intersects(&imported_handle_type.into()) + { + return Err(SemaphoreError::ExportFromImportedNotSupported { + imported_handle_type, + }); + } + } + } + } - // VUID-VkSemaphoreGetFdInfoKHR-handleType-01134 - // TODO: + if handle_type.has_copy_transference() { + // VUID-VkSemaphoreGetFdInfoKHR-handleType-01134 + if state.is_wait_pending() { + return Err(SemaphoreError::QueueIsWaiting); + } - // VUID-VkSemaphoreGetFdInfoKHR-handleType-01135 - // TODO: + // VUID-VkSemaphoreGetFdInfoKHR-handleType-01135 + // VUID-VkSemaphoreGetFdInfoKHR-handleType-03254 + if !(state.is_signaled().unwrap_or(false) || state.is_signal_pending()) { + return Err(SemaphoreError::HandleTypeCopyNotSignaled); + } + } // VUID-VkSemaphoreGetFdInfoKHR-handleType-01136 if !matches!( handle_type, ExternalSemaphoreHandleType::OpaqueFd | ExternalSemaphoreHandleType::SyncFd ) { - return Err(SemaphoreError::HandleTypeNotSupported { handle_type }); + return Err(SemaphoreError::HandleTypeNotFd); } - // VUID-VkSemaphoreGetFdInfoKHR-handleType-03253 - // TODO: - - // VUID-VkSemaphoreGetFdInfoKHR-handleType-03254 - // TODO: - Ok(()) } - #[cfg(not(unix))] + #[cfg(unix)] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn export_fd_unchecked( &self, - _handle_type: ExternalSemaphoreHandleType, + handle_type: ExternalSemaphoreHandleType, ) -> Result { - unreachable!("`khr_external_semaphore_fd` was somehow enabled on a non-Unix system"); + let mut state = self.state.lock(); + self.export_fd_unchecked_locked(handle_type, &mut state) } #[cfg(unix)] - #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] - #[inline] - pub unsafe fn export_fd_unchecked( + unsafe fn export_fd_unchecked_locked( &self, handle_type: ExternalSemaphoreHandleType, + state: &mut SemaphoreState, ) -> Result { use std::os::unix::io::FromRawFd; @@ -289,8 +337,650 @@ impl Semaphore { .result() .map_err(VulkanError::from)?; + state.export(handle_type); + Ok(File::from_raw_fd(output.assume_init())) } + + /// Exports the semaphore into a Win32 handle. + /// + /// The [`khr_external_semaphore_win32`](crate::device::DeviceExtensions::khr_external_semaphore_win32) + /// extension must be enabled on the device. + #[cfg(windows)] + #[inline] + pub fn export_win32_handle( + &self, + handle_type: ExternalSemaphoreHandleType, + ) -> Result<*mut std::ffi::c_void, SemaphoreError> { + let mut state = self.state.lock(); + self.validate_export_win32_handle(handle_type, &state)?; + + unsafe { Ok(self.export_win32_handle_unchecked_locked(handle_type, &mut state)?) } + } + + #[cfg(windows)] + fn validate_export_win32_handle( + &self, + handle_type: ExternalSemaphoreHandleType, + state: &SemaphoreState, + ) -> Result<(), SemaphoreError> { + if !self + .device + .enabled_extensions() + .khr_external_semaphore_win32 + { + return Err(SemaphoreError::RequirementNotMet { + required_for: "`export_win32_handle`", + requires_one_of: RequiresOneOf { + device_extensions: &["khr_external_semaphore_win32"], + ..Default::default() + }, + }); + } + + // VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-parameter + handle_type.validate_device(&self.device)?; + + // VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01126 + if !self.export_handle_types.intersects(&handle_type.into()) { + return Err(SemaphoreError::HandleTypeNotEnabled); + } + + // VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01127 + if matches!( + handle_type, + ExternalSemaphoreHandleType::OpaqueWin32 | ExternalSemaphoreHandleType::D3D12Fence + ) && state.is_exported(handle_type) + { + return Err(SemaphoreError::AlreadyExported); + } + + // VUID-VkSemaphoreGetWin32HandleInfoKHR-semaphore-01128 + if let Some(imported_handle_type) = state.current_import { + match imported_handle_type { + ImportType::SwapchainAcquire => { + return Err(SemaphoreError::ImportedForSwapchainAcquire) + } + ImportType::ExternalSemaphore(imported_handle_type) => { + let external_semaphore_properties = unsafe { + self.device + .physical_device() + .external_semaphore_properties_unchecked( + ExternalSemaphoreInfo::handle_type(handle_type), + ) + }; + + if !external_semaphore_properties + .export_from_imported_handle_types + .intersects(&imported_handle_type.into()) + { + return Err(SemaphoreError::ExportFromImportedNotSupported { + imported_handle_type, + }); + } + } + } + } + + if handle_type.has_copy_transference() { + // VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01129 + if state.is_wait_pending() { + return Err(SemaphoreError::QueueIsWaiting); + } + + // VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01130 + if !(state.is_signaled().unwrap_or(false) || state.is_signal_pending()) { + return Err(SemaphoreError::HandleTypeCopyNotSignaled); + } + } + + // VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01131 + if !matches!( + handle_type, + ExternalSemaphoreHandleType::OpaqueWin32 + | ExternalSemaphoreHandleType::OpaqueWin32Kmt + | ExternalSemaphoreHandleType::D3D12Fence + ) { + return Err(SemaphoreError::HandleTypeNotWin32); + } + + Ok(()) + } + + #[cfg(windows)] + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + #[inline] + pub unsafe fn export_win32_handle_unchecked( + &self, + handle_type: ExternalSemaphoreHandleType, + ) -> Result<*mut std::ffi::c_void, VulkanError> { + let mut state = self.state.lock(); + self.export_win32_handle_unchecked_locked(handle_type, &mut state) + } + + #[cfg(windows)] + unsafe fn export_win32_handle_unchecked_locked( + &self, + handle_type: ExternalSemaphoreHandleType, + state: &mut SemaphoreState, + ) -> Result<*mut std::ffi::c_void, VulkanError> { + let info_vk = ash::vk::SemaphoreGetWin32HandleInfoKHR { + semaphore: self.handle, + handle_type: handle_type.into(), + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + let fns = self.device.fns(); + (fns.khr_external_semaphore_win32 + .get_semaphore_win32_handle_khr)( + self.device.internal_object(), + &info_vk, + output.as_mut_ptr(), + ) + .result() + .map_err(VulkanError::from)?; + + state.export(handle_type); + + Ok(output.assume_init()) + } + + /// Exports the semaphore into a Zircon event handle. + #[cfg(target_os = "fuchsia")] + #[inline] + pub fn export_zircon_handle( + &self, + handle_type: ExternalSemaphoreHandleType, + ) -> Result { + let mut state = self.state.lock(); + self.validate_export_zircon_handle(handle_type, &state)?; + + unsafe { Ok(self.export_zircon_handle_unchecked_locked(handle_type, &mut state)?) } + } + + #[cfg(target_os = "fuchsia")] + fn validate_export_zircon_handle( + &self, + handle_type: ExternalSemaphoreHandleType, + state: &SemaphoreState, + ) -> Result<(), SemaphoreError> { + if !self.device.enabled_extensions().fuchsia_external_semaphore { + return Err(SemaphoreError::RequirementNotMet { + required_for: "`export_zircon_handle`", + requires_one_of: RequiresOneOf { + device_extensions: &["fuchsia_external_semaphore"], + ..Default::default() + }, + }); + } + + // VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-parameter + handle_type.validate_device(&self.device)?; + + // VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-04758 + if !self.export_handle_types.intersects(&handle_type.into()) { + return Err(SemaphoreError::HandleTypeNotEnabled); + } + + // VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-semaphore-04759 + if let Some(imported_handle_type) = state.current_import { + match imported_handle_type { + ImportType::SwapchainAcquire => { + return Err(SemaphoreError::ImportedForSwapchainAcquire) + } + ImportType::ExternalSemaphore(imported_handle_type) => { + let external_semaphore_properties = unsafe { + self.device + .physical_device() + .external_semaphore_properties_unchecked( + ExternalSemaphoreInfo::handle_type(handle_type), + ) + }; + + if !external_semaphore_properties + .export_from_imported_handle_types + .intersects(&imported_handle_type.into()) + { + return Err(SemaphoreError::ExportFromImportedNotSupported { + imported_handle_type, + }); + } + } + } + } + + if handle_type.has_copy_transference() { + // VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-04760 + if state.is_wait_pending() { + return Err(SemaphoreError::QueueIsWaiting); + } + + // VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-04761 + if !(state.is_signaled().unwrap_or(false) || state.is_signal_pending()) { + return Err(SemaphoreError::HandleTypeCopyNotSignaled); + } + } + + // VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-04762 + if !matches!(handle_type, ExternalSemaphoreHandleType::ZirconEvent) { + return Err(SemaphoreError::HandleTypeNotZircon); + } + + Ok(()) + } + + #[cfg(target_os = "fuchsia")] + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + #[inline] + pub unsafe fn export_zircon_handle_unchecked( + &self, + handle_type: ExternalSemaphoreHandleType, + ) -> Result { + let mut state = self.state.lock(); + self.export_zircon_handle_unchecked_locked(handle_type, &mut state) + } + + #[cfg(target_os = "fuchsia")] + unsafe fn export_zircon_handle_unchecked_locked( + &self, + handle_type: ExternalSemaphoreHandleType, + state: &mut SemaphoreState, + ) -> Result { + let info = ash::vk::SemaphoreGetZirconHandleInfoFUCHSIA { + semaphore: self.handle, + handle_type: handle_type.into(), + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + let fns = self.device.fns(); + (fns.fuchsia_external_semaphore + .get_semaphore_zircon_handle_fuchsia)( + self.device.internal_object(), + &info, + output.as_mut_ptr(), + ) + .result() + .map_err(VulkanError::from)?; + + state.export(handle_type); + + Ok(output.assume_init()) + } + + /// Imports a semaphore from a POSIX file descriptor. + /// + /// The [`khr_external_semaphore_fd`](crate::device::DeviceExtensions::khr_external_semaphore_fd) + /// extension must be enabled on the device. + /// + /// # Safety + /// + /// - If in `import_semaphore_fd_info`, `handle_type` is `ExternalHandleType::OpaqueFd`, + /// then `file` must represent a binary semaphore that was exported from Vulkan or a + /// compatible API, with a driver and device UUID equal to those of the device that owns + /// `self`. + #[cfg(unix)] + #[inline] + pub unsafe fn import_fd( + &self, + import_semaphore_fd_info: ImportSemaphoreFdInfo, + ) -> Result<(), SemaphoreError> { + let mut state = self.state.lock(); + self.validate_import_fd(&import_semaphore_fd_info, &state)?; + + Ok(self.import_fd_unchecked_locked(import_semaphore_fd_info, &mut state)?) + } + + #[cfg(unix)] + fn validate_import_fd( + &self, + import_semaphore_fd_info: &ImportSemaphoreFdInfo, + state: &SemaphoreState, + ) -> Result<(), SemaphoreError> { + if !self.device.enabled_extensions().khr_external_semaphore_fd { + return Err(SemaphoreError::RequirementNotMet { + required_for: "`import_fd`", + requires_one_of: RequiresOneOf { + device_extensions: &["khr_external_semaphore_fd"], + ..Default::default() + }, + }); + } + + // VUID-vkImportSemaphoreFdKHR-semaphore-01142 + if state.is_in_queue() { + return Err(SemaphoreError::InQueue); + } + + let &ImportSemaphoreFdInfo { + flags, + handle_type, + file: _, + _ne: _, + } = import_semaphore_fd_info; + + // VUID-VkImportSemaphoreFdInfoKHR-flags-parameter + flags.validate_device(&self.device)?; + + // VUID-VkImportSemaphoreFdInfoKHR-handleType-parameter + handle_type.validate_device(&self.device)?; + + // VUID-VkImportSemaphoreFdInfoKHR-handleType-01143 + if !matches!( + handle_type, + ExternalSemaphoreHandleType::OpaqueFd | ExternalSemaphoreHandleType::SyncFd + ) { + return Err(SemaphoreError::HandleTypeNotFd); + } + + // VUID-VkImportSemaphoreFdInfoKHR-fd-01544 + // VUID-VkImportSemaphoreFdInfoKHR-handleType-03263 + // Can't validate, therefore unsafe + + // VUID-VkImportSemaphoreFdInfoKHR-handleType-07307 + if handle_type.has_copy_transference() && !flags.temporary { + return Err(SemaphoreError::HandletypeCopyNotTemporary); + } + + Ok(()) + } + + #[cfg(unix)] + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + #[inline] + pub unsafe fn import_fd_unchecked( + &self, + import_semaphore_fd_info: ImportSemaphoreFdInfo, + ) -> Result<(), VulkanError> { + let mut state = self.state.lock(); + self.import_fd_unchecked_locked(import_semaphore_fd_info, &mut state) + } + + #[cfg(unix)] + unsafe fn import_fd_unchecked_locked( + &self, + import_semaphore_fd_info: ImportSemaphoreFdInfo, + state: &mut SemaphoreState, + ) -> Result<(), VulkanError> { + use std::os::unix::io::IntoRawFd; + + let ImportSemaphoreFdInfo { + flags, + handle_type, + file, + _ne: _, + } = import_semaphore_fd_info; + + let info_vk = ash::vk::ImportSemaphoreFdInfoKHR { + semaphore: self.handle, + flags: flags.into(), + handle_type: handle_type.into(), + fd: file.map_or(-1, |file| file.into_raw_fd()), + ..Default::default() + }; + + let fns = self.device.fns(); + (fns.khr_external_semaphore_fd.import_semaphore_fd_khr)( + self.device.internal_object(), + &info_vk, + ) + .result() + .map_err(VulkanError::from)?; + + state.import(handle_type, flags.temporary); + + Ok(()) + } + + /// Imports a semaphore from a Win32 handle. + /// + /// The [`khr_external_semaphore_win32`](crate::device::DeviceExtensions::khr_external_semaphore_win32) + /// extension must be enabled on the device. + /// + /// # Safety + /// + /// - In `import_semaphore_win32_handle_info`, `handle` must represent a binary semaphore that + /// was exported from Vulkan or a compatible API, with a driver and device UUID equal to + /// those of the device that owns `self`. + #[cfg(windows)] + #[inline] + pub unsafe fn import_win32_handle( + &self, + import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo, + ) -> Result<(), SemaphoreError> { + let mut state = self.state.lock(); + self.validate_import_win32_handle(&import_semaphore_win32_handle_info, &state)?; + + Ok(self + .import_win32_handle_unchecked_locked(import_semaphore_win32_handle_info, &mut state)?) + } + + #[cfg(windows)] + fn validate_import_win32_handle( + &self, + import_semaphore_win32_handle_info: &ImportSemaphoreWin32HandleInfo, + state: &SemaphoreState, + ) -> Result<(), SemaphoreError> { + if !self + .device + .enabled_extensions() + .khr_external_semaphore_win32 + { + return Err(SemaphoreError::RequirementNotMet { + required_for: "`import_win32_handle`", + requires_one_of: RequiresOneOf { + device_extensions: &["khr_external_semaphore_win32"], + ..Default::default() + }, + }); + } + + // VUID? + if state.is_in_queue() { + return Err(SemaphoreError::InQueue); + } + + let &ImportSemaphoreWin32HandleInfo { + flags, + handle_type, + handle: _, + _ne: _, + } = import_semaphore_win32_handle_info; + + // VUID-VkImportSemaphoreWin32HandleInfoKHR-flags-parameter + flags.validate_device(&self.device)?; + + // VUID-VkImportSemaphoreWin32HandleInfoKHR-handleType-01140 + handle_type.validate_device(&self.device)?; + + // VUID-VkImportSemaphoreWin32HandleInfoKHR-handleType-01140 + if !matches!( + handle_type, + ExternalSemaphoreHandleType::OpaqueWin32 + | ExternalSemaphoreHandleType::OpaqueWin32Kmt + | ExternalSemaphoreHandleType::D3D12Fence + ) { + return Err(SemaphoreError::HandleTypeNotWin32); + } + + // VUID-VkImportSemaphoreWin32HandleInfoKHR-handle-01542 + // Can't validate, therefore unsafe + + // VUID? + if handle_type.has_copy_transference() && !flags.temporary { + return Err(SemaphoreError::HandletypeCopyNotTemporary); + } + + Ok(()) + } + + #[cfg(windows)] + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + #[inline] + pub unsafe fn import_win32_handle_unchecked( + &self, + import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo, + ) -> Result<(), VulkanError> { + let mut state = self.state.lock(); + self.import_win32_handle_unchecked_locked(import_semaphore_win32_handle_info, &mut state) + } + + #[cfg(windows)] + unsafe fn import_win32_handle_unchecked_locked( + &self, + import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo, + state: &mut SemaphoreState, + ) -> Result<(), VulkanError> { + let ImportSemaphoreWin32HandleInfo { + flags, + handle_type, + handle, + _ne: _, + } = import_semaphore_win32_handle_info; + + let info_vk = ash::vk::ImportSemaphoreWin32HandleInfoKHR { + semaphore: self.handle, + flags: flags.into(), + handle_type: handle_type.into(), + handle, + name: ptr::null(), // TODO: support? + ..Default::default() + }; + + let fns = self.device.fns(); + (fns.khr_external_semaphore_win32 + .import_semaphore_win32_handle_khr)(self.device.internal_object(), &info_vk) + .result() + .map_err(VulkanError::from)?; + + state.import(handle_type, flags.temporary); + + Ok(()) + } + + /// Imports a semaphore from a Zircon event handle. + /// + /// The [`fuchsia_external_semaphore`](crate::device::DeviceExtensions::fuchsia_external_semaphore) + /// extension must be enabled on the device. + /// + /// # Safety + /// + /// - In `import_semaphore_zircon_handle_info`, `zircon_handle` must have `ZX_RIGHTS_BASIC` and + /// `ZX_RIGHTS_SIGNAL`. + #[cfg(target_os = "fuchsia")] + #[inline] + pub unsafe fn import_zircon_handle( + &self, + import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo, + ) -> Result<(), SemaphoreError> { + let mut state = self.state.lock(); + self.validate_import_zircon_handle(&import_semaphore_zircon_handle_info, &state)?; + + Ok(self.import_zircon_handle_unchecked_locked( + import_semaphore_zircon_handle_info, + &mut state, + )?) + } + + #[cfg(target_os = "fuchsia")] + fn validate_import_zircon_handle( + &self, + import_semaphore_zircon_handle_info: &ImportSemaphoreZirconHandleInfo, + state: &SemaphoreState, + ) -> Result<(), SemaphoreError> { + if !self.device.enabled_extensions().fuchsia_external_semaphore { + return Err(SemaphoreError::RequirementNotMet { + required_for: "`import_zircon_handle`", + requires_one_of: RequiresOneOf { + device_extensions: &["fuchsia_external_semaphore"], + ..Default::default() + }, + }); + } + + // VUID-vkImportSemaphoreZirconHandleFUCHSIA-semaphore-04764 + if state.is_in_queue() { + return Err(SemaphoreError::InQueue); + } + + let &ImportSemaphoreZirconHandleInfo { + flags, + handle_type, + zircon_handle: _, + _ne: _, + } = import_semaphore_zircon_handle_info; + + // VUID-VkImportSemaphoreZirconHandleInfoFUCHSIA-flags-parameter + flags.validate_device(&self.device)?; + + // VUID-VkImportSemaphoreZirconHandleInfoFUCHSIA-handleType-parameter + handle_type.validate_device(&self.device)?; + + // VUID-VkImportSemaphoreZirconHandleInfoFUCHSIA-handleType-04765 + if !matches!(handle_type, ExternalSemaphoreHandleType::ZirconEvent) { + return Err(SemaphoreError::HandleTypeNotFd); + } + + // VUID-VkImportSemaphoreZirconHandleInfoFUCHSIA-zirconHandle-04766 + // VUID-VkImportSemaphoreZirconHandleInfoFUCHSIA-zirconHandle-04767 + // Can't validate, therefore unsafe + + if handle_type.has_copy_transference() && !flags.temporary { + return Err(SemaphoreError::HandletypeCopyNotTemporary); + } + + Ok(()) + } + + #[cfg(target_os = "fuchsia")] + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + #[inline] + pub unsafe fn import_zircon_handle_unchecked( + &self, + import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo, + ) -> Result<(), VulkanError> { + let mut state = self.state.lock(); + self.import_zircon_handle_unchecked_locked(import_semaphore_zircon_handle_info, &mut state) + } + + #[cfg(target_os = "fuchsia")] + unsafe fn import_zircon_handle_unchecked_locked( + &self, + import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo, + state: &mut SemaphoreState, + ) -> Result<(), VulkanError> { + let ImportSemaphoreZirconHandleInfo { + flags, + handle_type, + zircon_handle, + _ne: _, + } = import_semaphore_zircon_handle_info; + + let info_vk = ash::vk::ImportSemaphoreZirconHandleInfoFUCHSIA { + semaphore: self.handle, + flags: flags.into(), + handle_type: handle_type.into(), + zircon_handle, + ..Default::default() + }; + + let fns = self.device.fns(); + (fns.fuchsia_external_semaphore + .import_semaphore_zircon_handle_fuchsia)( + self.device.internal_object(), &info_vk + ) + .result() + .map_err(VulkanError::from)?; + + state.import(handle_type, flags.temporary); + + Ok(()) + } + + pub(crate) fn state(&self) -> MutexGuard<'_, SemaphoreState> { + self.state.lock() + } } impl Drop for Semaphore { @@ -344,6 +1034,136 @@ impl Hash for Semaphore { } } +#[derive(Debug, Default)] +pub(crate) struct SemaphoreState { + is_signaled: bool, + pending_signal: Option, + pending_wait: Option>, + + reference_exported: bool, + exported_handle_types: ExternalSemaphoreHandleTypes, + current_import: Option, + permanent_import: Option, +} + +impl SemaphoreState { + /// If the semaphore does not have a pending operation and has no external references, + /// returns the current status. + #[inline] + fn is_signaled(&self) -> Option { + // If any of these is true, we can't be certain of the status. + if self.pending_signal.is_some() + || self.pending_wait.is_some() + || self.has_external_reference() + { + None + } else { + Some(self.is_signaled) + } + } + + #[inline] + fn is_signal_pending(&self) -> bool { + self.pending_signal.is_some() + } + + #[inline] + fn is_wait_pending(&self) -> bool { + self.pending_wait.is_some() + } + + #[inline] + fn is_in_queue(&self) -> bool { + matches!(self.pending_signal, Some(SignalType::Queue(_))) || self.pending_wait.is_some() + } + + /// Returns whether there are any potential external references to the semaphore payload. + /// That is, the semaphore has been exported by reference transference, or imported. + #[inline] + fn has_external_reference(&self) -> bool { + self.reference_exported || self.current_import.is_some() + } + + #[allow(dead_code)] + #[inline] + fn is_exported(&self, handle_type: ExternalSemaphoreHandleType) -> bool { + self.exported_handle_types.intersects(&handle_type.into()) + } + + #[inline] + pub(crate) unsafe fn add_queue_signal(&mut self, queue: &Arc) { + self.pending_signal = Some(SignalType::Queue(Arc::downgrade(queue))); + } + + #[inline] + pub(crate) unsafe fn add_queue_wait(&mut self, queue: &Arc) { + self.pending_wait = Some(Arc::downgrade(queue)); + } + + /// Called when a queue is unlocking resources. + #[inline] + pub(crate) unsafe fn set_signal_finished(&mut self) { + self.pending_signal = None; + self.is_signaled = true; + } + + /// Called when a queue is unlocking resources. + #[inline] + pub(crate) unsafe fn set_wait_finished(&mut self) { + self.pending_wait = None; + self.current_import = self.permanent_import.map(Into::into); + self.is_signaled = false; + } + + #[allow(dead_code)] + #[inline] + unsafe fn export(&mut self, handle_type: ExternalSemaphoreHandleType) { + self.exported_handle_types |= handle_type.into(); + + if handle_type.has_copy_transference() { + self.current_import = self.permanent_import.map(Into::into); + self.is_signaled = false; + } else { + self.reference_exported = true; + } + } + + #[allow(dead_code)] + #[inline] + unsafe fn import(&mut self, handle_type: ExternalSemaphoreHandleType, temporary: bool) { + self.current_import = Some(handle_type.into()); + + if !temporary { + self.permanent_import = Some(handle_type); + } + } + + #[inline] + pub(crate) unsafe fn swapchain_acquire(&mut self) { + self.pending_signal = Some(SignalType::SwapchainAcquire); + self.current_import = Some(ImportType::SwapchainAcquire); + } +} + +#[derive(Clone, Debug)] +enum SignalType { + Queue(Weak), + SwapchainAcquire, +} + +#[derive(Clone, Copy, Debug)] +enum ImportType { + SwapchainAcquire, + ExternalSemaphore(ExternalSemaphoreHandleType), +} + +impl From for ImportType { + #[inline] + fn from(handle_type: ExternalSemaphoreHandleType) -> Self { + Self::ExternalSemaphore(handle_type) + } +} + /// Parameters to create a new `Semaphore`. #[derive(Clone, Debug)] pub struct SemaphoreCreateInfo { @@ -484,6 +1304,31 @@ impl From for ExternalSemaphoreHandleTypes { } } +impl ExternalSemaphoreHandleTypes { + fn into_iter(self) -> impl IntoIterator { + let Self { + opaque_fd, + opaque_win32, + opaque_win32_kmt, + d3d12_fence, + sync_fd, + zircon_event, + _ne: _, + } = self; + + [ + opaque_fd.then_some(ExternalSemaphoreHandleType::OpaqueFd), + opaque_win32.then_some(ExternalSemaphoreHandleType::OpaqueWin32), + opaque_win32_kmt.then_some(ExternalSemaphoreHandleType::OpaqueWin32Kmt), + d3d12_fence.then_some(ExternalSemaphoreHandleType::D3D12Fence), + sync_fd.then_some(ExternalSemaphoreHandleType::SyncFd), + zircon_event.then_some(ExternalSemaphoreHandleType::ZirconEvent), + ] + .into_iter() + .flatten() + } +} + vulkan_bitflags! { /// Additional parameters for a semaphore payload import. #[non_exhaustive] @@ -494,6 +1339,122 @@ vulkan_bitflags! { temporary = TEMPORARY, } +#[cfg(unix)] +#[derive(Debug)] +pub struct ImportSemaphoreFdInfo { + /// Additional parameters for the import operation. + /// + /// If `handle_type` has *copy transference*, this must include the `temporary` flag. + /// + /// The default value is [`SemaphoreImportFlags::empty()`]. + pub flags: SemaphoreImportFlags, + + /// The handle type of `file`. + /// + /// There is no default value. + pub handle_type: ExternalSemaphoreHandleType, + + /// The file to import the semaphore from. + /// + /// If `handle_type` is `ExternalSemaphoreHandleType::SyncFd`, then `file` can be `None`. + /// Instead of an imported file descriptor, a dummy file descriptor `-1` is used, + /// which represents a semaphore that is always signaled. + /// + /// The default value is `None`, which must be overridden if `handle_type` is not + /// `ExternalSemaphoreHandleType::SyncFd`. + pub file: Option, + + pub _ne: crate::NonExhaustive, +} + +#[cfg(unix)] +impl ImportSemaphoreFdInfo { + /// Returns an `ImportSemaphoreFdInfo` with the specified `handle_type`. + #[inline] + pub fn handle_type(handle_type: ExternalSemaphoreHandleType) -> Self { + Self { + flags: SemaphoreImportFlags::empty(), + handle_type, + file: None, + _ne: crate::NonExhaustive(()), + } + } +} + +#[cfg(windows)] +#[derive(Debug)] +pub struct ImportSemaphoreWin32HandleInfo { + /// Additional parameters for the import operation. + /// + /// If `handle_type` has *copy transference*, this must include the `temporary` flag. + /// + /// The default value is [`SemaphoreImportFlags::empty()`]. + pub flags: SemaphoreImportFlags, + + /// The handle type of `handle`. + /// + /// There is no default value. + pub handle_type: ExternalSemaphoreHandleType, + + /// The handle to import the semaphore from. + /// + /// The default value is `null`, which must be overridden. + pub handle: *mut std::ffi::c_void, + + pub _ne: crate::NonExhaustive, +} + +#[cfg(windows)] +impl ImportSemaphoreWin32HandleInfo { + /// Returns an `ImportSemaphoreWin32HandleInfo` with the specified `handle_type`. + #[inline] + pub fn handle_type(handle_type: ExternalSemaphoreHandleType) -> Self { + Self { + flags: SemaphoreImportFlags::empty(), + handle_type, + handle: ptr::null_mut(), + _ne: crate::NonExhaustive(()), + } + } +} + +#[cfg(target_os = "fuchsia")] +#[derive(Debug)] +pub struct ImportSemaphoreZirconHandleInfo { + /// Additional parameters for the import operation. + /// + /// If `handle_type` has *copy transference*, this must include the `temporary` flag. + /// + /// The default value is [`SemaphoreImportFlags::empty()`]. + pub flags: SemaphoreImportFlags, + + /// The handle type of `handle`. + /// + /// There is no default value. + pub handle_type: ExternalSemaphoreHandleType, + + /// The handle to import the semaphore from. + /// + /// The default value is `ZX_HANDLE_INVALID`, which must be overridden. + pub zircon_handle: ash::vk::zx_handle_t, + + pub _ne: crate::NonExhaustive, +} + +#[cfg(target_os = "fuchsia")] +impl ImportSemaphoreZirconHandleInfo { + /// Returns an `ImportSemaphoreZirconHandleInfo` with the specified `handle_type`. + #[inline] + pub fn handle_type(handle_type: ExternalSemaphoreHandleType) -> Self { + Self { + flags: SemaphoreImportFlags::empty(), + handle_type, + zircon_handle: 0, + _ne: crate::NonExhaustive(()), + } + } +} + /// The semaphore configuration to query in /// [`PhysicalDevice::external_semaphore_properties`](crate::device::physical::PhysicalDevice::external_semaphore_properties). #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -548,11 +1509,52 @@ pub enum SemaphoreError { requires_one_of: RequiresOneOf, }, - /// The requested export handle type was not provided in `export_handle_types` when creating the + /// The provided handle type does not permit more than one export, + /// and a handle of this type was already exported previously. + AlreadyExported, + + /// The provided handle type cannot be exported from the current import handle type. + ExportFromImportedNotSupported { + imported_handle_type: ExternalSemaphoreHandleType, + }, + + /// One of the export handle types is not compatible with the other provided handles. + ExportHandleTypesNotCompatible, + + /// A handle type with copy transference was provided, but the semaphore is not signaled and + /// there is no pending queue operation that will signal it. + HandleTypeCopyNotSignaled, + + /// A handle type with copy transference was provided, + /// but the `temporary` import flag was not set. + HandletypeCopyNotTemporary, + + /// The provided export handle type was not set in `export_handle_types` when creating the /// semaphore. - HandleTypeNotSupported { + HandleTypeNotEnabled, + + /// Exporting is not supported for the provided handle type. + HandleTypeNotExportable { handle_type: ExternalSemaphoreHandleType, }, + + /// The provided handle type is not a POSIX file descriptor handle. + HandleTypeNotFd, + + /// The provided handle type is not a Win32 handle. + HandleTypeNotWin32, + + /// The provided handle type is not a Zircon event handle. + HandleTypeNotZircon, + + /// The semaphore currently has a temporary import for a swapchain acquire operation. + ImportedForSwapchainAcquire, + + /// The semaphore is currently in use by a queue. + InQueue, + + /// A queue is currently waiting on the semaphore. + QueueIsWaiting, } impl Error for SemaphoreError { @@ -576,12 +1578,60 @@ impl Display for SemaphoreError { "a requirement was not met for: {}; requires one of: {}", required_for, requires_one_of, ), - Self::HandleTypeNotSupported { handle_type } => write!( + + Self::AlreadyExported => write!( + f, + "the provided handle type does not permit more than one export, and a handle of \ + this type was already exported previously", + ), + Self::ExportFromImportedNotSupported { + imported_handle_type, + } => write!( + f, + "the provided handle type cannot be exported from the current imported handle type \ + {:?}", + imported_handle_type, + ), + Self::ExportHandleTypesNotCompatible => write!( f, - "the requested export handle type ({:?}) was not provided in `export_handle_types` \ - when creating the semaphore", + "one of the export handle types is not compatible with the other provided handles", + ), + Self::HandleTypeCopyNotSignaled => write!( + f, + "a handle type with copy transference was provided, but the semaphore is not \ + signaled and there is no pending queue operation that will signal it", + ), + Self::HandletypeCopyNotTemporary => write!( + f, + "a handle type with copy transference was provided, but the `temporary` \ + import flag was not set", + ), + Self::HandleTypeNotEnabled => write!( + f, + "the provided export handle type was not set in `export_handle_types` when \ + creating the semaphore", + ), + Self::HandleTypeNotExportable { handle_type } => write!( + f, + "exporting is not supported for handles of type {:?}", handle_type, ), + Self::HandleTypeNotFd => write!( + f, + "the provided handle type is not a POSIX file descriptor handle", + ), + Self::HandleTypeNotWin32 => { + write!(f, "the provided handle type is not a Win32 handle") + } + Self::HandleTypeNotZircon => { + write!(f, "the provided handle type is not a Zircon event handle") + } + Self::ImportedForSwapchainAcquire => write!( + f, + "the semaphore currently has a temporary import for a swapchain acquire operation", + ), + Self::InQueue => write!(f, "the semaphore is currently in use by a queue"), + Self::QueueIsWaiting => write!(f, "a queue is currently waiting on the semaphore"), } } } @@ -646,7 +1696,8 @@ mod tests { } #[test] - fn semaphore_export() { + #[cfg(unix)] + fn semaphore_export_fd() { let library = match VulkanLibrary::new() { Ok(x) => x, Err(_) => return, @@ -702,9 +1753,8 @@ mod tests { }, ) .unwrap(); - let _fd = unsafe { - sem.export_fd(ExternalSemaphoreHandleType::OpaqueFd) - .unwrap() - }; + let _fd = sem + .export_fd(ExternalSemaphoreHandleType::OpaqueFd) + .unwrap(); } }