// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. //! A semaphore provides synchronization between multiple queues, with non-command buffer //! commands on the same queue, or between the device and an external source. use crate::{ device::{Device, DeviceOwned, Queue}, macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum}, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; use parking_lot::{Mutex, MutexGuard}; #[cfg(unix)] use std::fs::File; use std::{ error::Error, fmt::{Display, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ptr, sync::{Arc, Weak}, }; /// Used to provide synchronization between command buffers during their execution. /// /// It is similar to a fence, except that it is purely on the GPU side. The CPU can't query a /// semaphore's status or wait for it to be signaled. #[derive(Debug)] pub struct Semaphore { handle: ash::vk::Semaphore, device: Arc, id: NonZeroU64, must_put_in_pool: bool, export_handle_types: ExternalSemaphoreHandleTypes, state: Mutex, } impl Semaphore { /// Creates a new `Semaphore`. #[inline] pub fn new( device: Arc, create_info: 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; if !export_handle_types.is_empty() { if !(device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_external_semaphore) { return Err(SemaphoreError::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_semaphore"], ..Default::default() }, }); } // VUID-VkExportSemaphoreCreateInfo-handleTypes-parameter export_handle_types.validate_device(device)?; // VUID-VkExportSemaphoreCreateInfo-handleTypes-01124 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(()) } #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] 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() }); }; 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 = { let fns = device.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.create_semaphore)( device.handle(), &create_info_vk, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Semaphore { handle, device, id: Self::next_id(), must_put_in_pool: false, export_handle_types, state: Mutex::new(Default::default()), }) } /// 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. #[inline] pub fn from_pool(device: Arc) -> Result { let handle = device.semaphore_pool().lock().pop(); let semaphore = match handle { Some(handle) => Semaphore { handle, device, id: Self::next_id(), must_put_in_pool: true, export_handle_types: ExternalSemaphoreHandleTypes::empty(), state: Mutex::new(Default::default()), }, 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 a raw object handle. /// /// # Safety /// /// - `handle` must be a valid Vulkan object handle created from `device`. /// - `create_info` must match the info used to create the object. #[inline] pub unsafe fn from_handle( device: Arc, handle: ash::vk::Semaphore, create_info: SemaphoreCreateInfo, ) -> Semaphore { let SemaphoreCreateInfo { export_handle_types, _ne: _, } = create_info; Semaphore { handle, device, id: Self::next_id(), must_put_in_pool: false, export_handle_types, state: Mutex::new(Default::default()), } } /// Exports the semaphore into a POSIX file descriptor. The caller owns the returned `File`. #[cfg(unix)] #[inline] pub fn export_fd( &self, handle_type: ExternalSemaphoreHandleType, ) -> Result { let mut state = self.state.lock(); self.validate_export_fd(handle_type, &state)?; 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 { required_for: "`Semaphore::export_fd`", requires_one_of: RequiresOneOf { device_extensions: &["khr_external_semaphore_fd"], ..Default::default() }, }); } // VUID-VkSemaphoreGetFdInfoKHR-handleType-parameter handle_type.validate_device(&self.device)?; // VUID-VkSemaphoreGetFdInfoKHR-handleType-01132 if !self.export_handle_types.intersects(handle_type.into()) { return Err(SemaphoreError::HandleTypeNotEnabled); } // VUID-VkSemaphoreGetFdInfoKHR-semaphore-01133 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-VkSemaphoreGetFdInfoKHR-handleType-01134 if state.is_wait_pending() { return Err(SemaphoreError::QueueIsWaiting); } // 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::HandleTypeNotFd); } Ok(()) } #[cfg(unix)] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[inline] pub unsafe fn export_fd_unchecked( &self, handle_type: ExternalSemaphoreHandleType, ) -> Result { let mut state = self.state.lock(); self.export_fd_unchecked_locked(handle_type, &mut state) } #[cfg(unix)] unsafe fn export_fd_unchecked_locked( &self, handle_type: ExternalSemaphoreHandleType, state: &mut SemaphoreState, ) -> 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.handle(), &info, output.as_mut_ptr(), ) .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: "`Semaphore::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.handle(), &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: "`Semaphore::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.handle(), &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: "`Semaphore::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.intersects(SemaphoreImportFlags::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.handle(), &info_vk) .result() .map_err(VulkanError::from)?; state.import( handle_type, flags.intersects(SemaphoreImportFlags::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: "`Semaphore::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.intersects(SemaphoreImportFlags::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.handle(), &info_vk) .result() .map_err(VulkanError::from)?; state.import( handle_type, flags.intersects(SemaphoreImportFlags::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: "`Semaphore::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.intersects(SemaphoreImportFlags::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.handle(), &info_vk) .result() .map_err(VulkanError::from)?; state.import( handle_type, flags.intersects(SemaphoreImportFlags::TEMPORARY), ); Ok(()) } pub(crate) fn state(&self) -> MutexGuard<'_, SemaphoreState> { self.state.lock() } } impl Drop for Semaphore { #[inline] fn drop(&mut self) { unsafe { if self.must_put_in_pool { let raw_sem = self.handle; self.device.semaphore_pool().lock().push(raw_sem); } else { let fns = self.device.fns(); (fns.v1_0.destroy_semaphore)(self.device.handle(), self.handle, ptr::null()); } } } } unsafe impl VulkanObject for Semaphore { type Handle = ash::vk::Semaphore; #[inline] fn handle(&self) -> Self::Handle { self.handle } } unsafe impl DeviceOwned for Semaphore { #[inline] fn device(&self) -> &Arc { &self.device } } impl_id_counter!(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 { /// The handle types that can be exported from the semaphore. /// /// The default value is [`ExternalSemaphoreHandleTypes::empty()`]. pub export_handle_types: ExternalSemaphoreHandleTypes, pub _ne: crate::NonExhaustive, } impl Default for SemaphoreCreateInfo { #[inline] fn default() -> Self { Self { export_handle_types: ExternalSemaphoreHandleTypes::empty(), _ne: crate::NonExhaustive(()), } } } vulkan_bitflags_enum! { #[non_exhaustive] /// A set of [`ExternalSemaphoreHandleType`] values. ExternalSemaphoreHandleTypes, /// The handle type used to export or import semaphores to/from an external source. ExternalSemaphoreHandleType impl { /// Returns whether the given handle type has *copy transference* rather than *reference /// transference*. /// /// Imports of handles with copy transference must always be temporary. Exports of such /// handles must only occur if no queue is waiting on the semaphore, and only if the semaphore /// is already signaled, or if there is a semaphore signal operation pending in a queue. #[inline] pub fn has_copy_transference(self) -> bool { // As defined by // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-semaphore-handletypes-win32 // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-semaphore-handletypes-fd // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-semaphore-handletypes-fuchsia matches!(self, Self::SyncFd) } }, = ExternalSemaphoreHandleTypeFlags(u32); /// A POSIX file descriptor handle that is only usable with Vulkan and compatible APIs. /// /// This handle type has *reference transference*. OPAQUE_FD, OpaqueFd = OPAQUE_FD, /// A Windows NT handle that is only usable with Vulkan and compatible APIs. /// /// This handle type has *reference transference*. OPAQUE_WIN32, OpaqueWin32 = OPAQUE_WIN32, /// A Windows global share handle that is only usable with Vulkan and compatible APIs. /// /// This handle type has *reference transference*. OPAQUE_WIN32_KMT, OpaqueWin32Kmt = OPAQUE_WIN32_KMT, /// A Windows NT handle that refers to a Direct3D 11 or 12 fence. /// /// This handle type has *reference transference*. D3D12_FENCE, D3D12Fence = D3D12_FENCE, /// A POSIX file descriptor handle to a Linux Sync File or Android Fence object. /// /// This handle type has *copy transference*. SYNC_FD, SyncFd = SYNC_FD, /// A handle to a Zircon event object. /// /// This handle type has *reference transference*. /// /// The [`fuchsia_external_semaphore`] extension must be enabled on the device. /// /// [`fuchsia_external_semaphore`]: crate::device::DeviceExtensions::fuchsia_external_semaphore ZIRCON_EVENT, ZirconEvent = ZIRCON_EVENT_FUCHSIA { device_extensions: [fuchsia_external_semaphore], }, } vulkan_bitflags! { #[non_exhaustive] /// Additional parameters for a semaphore payload import. SemaphoreImportFlags = SemaphoreImportFlags(u32); /// The semaphore payload will be imported only temporarily, regardless of the permanence of the /// imported handle type. 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)] pub struct ExternalSemaphoreInfo { /// The external handle type that will be used with the semaphore. pub handle_type: ExternalSemaphoreHandleType, pub _ne: crate::NonExhaustive, } impl ExternalSemaphoreInfo { /// Returns an `ExternalSemaphoreInfo` with the specified `handle_type`. #[inline] pub fn handle_type(handle_type: ExternalSemaphoreHandleType) -> Self { Self { handle_type, _ne: crate::NonExhaustive(()), } } } /// The properties for exporting or importing external handles, when a semaphore is created /// with a specific configuration. #[derive(Clone, Debug)] #[non_exhaustive] pub struct ExternalSemaphoreProperties { /// 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: ExternalSemaphoreHandleTypes, /// Which external handle types can be enabled along with the queried external handle type /// 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 SemaphoreError { /// Not enough memory available. OomError(OomError), RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, /// 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. 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 { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::OomError(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::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, "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"), } } } impl From for SemaphoreError { fn from(err: VulkanError) -> Self { match err { e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => { Self::OomError(e.into()) } _ => panic!("unexpected error: {:?}", err), } } } impl From for SemaphoreError { fn from(err: OomError) -> Self { Self::OomError(err) } } impl From for SemaphoreError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } } #[cfg(test)] mod tests { #[cfg(unix)] use crate::{ device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo}, instance::{Instance, InstanceCreateInfo, InstanceExtensions}, sync::semaphore::{ ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, SemaphoreCreateInfo, }, VulkanLibrary, }; use crate::{sync::semaphore::Semaphore, VulkanObject}; #[test] fn semaphore_create() { let (device, _) = gfx_dev_and_queue!(); let _ = Semaphore::new(device, Default::default()); } #[test] fn semaphore_pool() { let (device, _) = gfx_dev_and_queue!(); assert_eq!(device.semaphore_pool().lock().len(), 0); let sem1_internal_obj = { let sem = Semaphore::from_pool(device.clone()).unwrap(); assert_eq!(device.semaphore_pool().lock().len(), 0); sem.handle() }; assert_eq!(device.semaphore_pool().lock().len(), 1); let sem2 = Semaphore::from_pool(device.clone()).unwrap(); assert_eq!(device.semaphore_pool().lock().len(), 0); assert_eq!(sem2.handle(), sem1_internal_obj); } #[test] #[cfg(unix)] fn semaphore_export_fd() { let library = match VulkanLibrary::new() { Ok(x) => x, Err(_) => return, }; let instance = match Instance::new( library, InstanceCreateInfo { enabled_extensions: InstanceExtensions { khr_get_physical_device_properties2: true, khr_external_semaphore_capabilities: true, ..InstanceExtensions::empty() }, ..Default::default() }, ) { Ok(x) => x, Err(_) => return, }; let physical_device = match instance.enumerate_physical_devices() { Ok(mut x) => x.next().unwrap(), Err(_) => return, }; let (device, _) = match Device::new( physical_device, DeviceCreateInfo { enabled_extensions: DeviceExtensions { khr_external_semaphore: true, khr_external_semaphore_fd: true, ..DeviceExtensions::empty() }, queue_create_infos: vec![QueueCreateInfo { queue_family_index: 0, ..Default::default() }], ..Default::default() }, ) { Ok(x) => x, Err(_) => return, }; let sem = Semaphore::new( device, SemaphoreCreateInfo { export_handle_types: ExternalSemaphoreHandleTypes::OPAQUE_FD, ..Default::default() }, ) .unwrap(); let _fd = sem .export_fd(ExternalSemaphoreHandleType::OpaqueFd) .unwrap(); } }