diff options
Diffstat (limited to 'src/sync/semaphore/semaphore.rs')
-rw-r--r-- | src/sync/semaphore/semaphore.rs | 355 |
1 files changed, 355 insertions, 0 deletions
diff --git a/src/sync/semaphore/semaphore.rs b/src/sync/semaphore/semaphore.rs new file mode 100644 index 0000000..29c9952 --- /dev/null +++ b/src/sync/semaphore/semaphore.rs @@ -0,0 +1,355 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use crate::check_errors; +use crate::device::Device; +use crate::device::DeviceOwned; +use crate::Error; +use crate::OomError; +use crate::SafeDeref; +use crate::VulkanObject; +use std::fmt; +#[cfg(target_os = "linux")] +use std::fs::File; +use std::mem::MaybeUninit; +#[cfg(target_os = "linux")] +use std::os::unix::io::FromRawFd; +use std::ptr; +use std::sync::Arc; + +use crate::sync::semaphore::ExternalSemaphoreHandleType; + +/// 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<D = Arc<Device>> +where + D: SafeDeref<Target = Device>, +{ + semaphore: ash::vk::Semaphore, + device: D, + must_put_in_pool: bool, +} + +// TODO: Add support for VkExportSemaphoreWin32HandleInfoKHR +// TODO: Add suport for importable semaphores +pub struct SemaphoreBuilder<D = Arc<Device>> +where + D: SafeDeref<Target = Device>, +{ + device: D, + export_info: Option<ash::vk::ExportSemaphoreCreateInfo>, + create: ash::vk::SemaphoreCreateInfo, + must_put_in_pool: bool, +} + +impl<D> SemaphoreBuilder<D> +where + D: SafeDeref<Target = Device>, +{ + pub fn new(device: D) -> Self { + let create = ash::vk::SemaphoreCreateInfo::default(); + + Self { + device, + export_info: None, + create, + must_put_in_pool: false, + } + } + /// Configures the semaphore to be added to the semaphore pool once it is destroyed. + pub(crate) fn in_pool(mut self) -> Self { + self.must_put_in_pool = true; + self + } + + /// Sets an optional field for exportable allocations in the `SemaphoreBuilder`. + /// + /// # Panic + /// + /// - Panics if the export info has already been set. + pub fn export_info(mut self, handle_types: ExternalSemaphoreHandleType) -> Self { + assert!(self.export_info.is_none()); + let export_info = ash::vk::ExportSemaphoreCreateInfo { + handle_types: handle_types.into(), + ..Default::default() + }; + + self.export_info = Some(export_info); + self.create.p_next = unsafe { std::mem::transmute(&export_info) }; + + self + } + + pub fn build(self) -> Result<Semaphore<D>, SemaphoreError> { + if self.export_info.is_some() + && !self + .device + .instance() + .enabled_extensions() + .khr_external_semaphore_capabilities + { + Err(SemaphoreError::MissingExtension( + "khr_external_semaphore_capabilities", + )) + } else { + let semaphore = unsafe { + let fns = self.device.fns(); + let mut output = MaybeUninit::uninit(); + check_errors(fns.v1_0.create_semaphore( + self.device.internal_object(), + &self.create, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(Semaphore { + device: self.device, + semaphore, + must_put_in_pool: self.must_put_in_pool, + }) + } + } +} + +impl<D> Semaphore<D> +where + D: SafeDeref<Target = Device>, +{ + /// Takes a semaphore from the vulkano-provided semaphore pool. + /// If the pool is empty, a new semaphore will be allocated. + /// Upon `drop`, the semaphore is put back into the pool. + /// + /// For most applications, using the pool should be preferred, + /// in order to avoid creating new semaphores every frame. + pub fn from_pool(device: D) -> Result<Semaphore<D>, SemaphoreError> { + let maybe_raw_sem = device.semaphore_pool().lock().unwrap().pop(); + match maybe_raw_sem { + Some(raw_sem) => Ok(Semaphore { + device, + semaphore: raw_sem, + must_put_in_pool: true, + }), + None => { + // Pool is empty, alloc new semaphore + SemaphoreBuilder::new(device).in_pool().build() + } + } + } + + /// Builds a new semaphore. + #[inline] + pub fn alloc(device: D) -> Result<Semaphore<D>, SemaphoreError> { + SemaphoreBuilder::new(device).build() + } + + /// Same as `alloc`, but allows exportable opaque file descriptor on Linux + #[inline] + #[cfg(target_os = "linux")] + pub fn alloc_with_exportable_fd(device: D) -> Result<Semaphore<D>, SemaphoreError> { + SemaphoreBuilder::new(device) + .export_info(ExternalSemaphoreHandleType::posix()) + .build() + } + + #[cfg(target_os = "linux")] + pub fn export_opaque_fd(&self) -> Result<File, SemaphoreError> { + let fns = self.device.fns(); + + assert!(self.device.enabled_extensions().khr_external_semaphore); + assert!(self.device.enabled_extensions().khr_external_semaphore_fd); + + let fd = unsafe { + let info = ash::vk::SemaphoreGetFdInfoKHR { + semaphore: self.semaphore, + handle_type: ash::vk::ExternalSemaphoreHandleTypeFlagsKHR::OPAQUE_FD, + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.khr_external_semaphore_fd.get_semaphore_fd_khr( + self.device.internal_object(), + &info, + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + let file = unsafe { File::from_raw_fd(fd) }; + Ok(file) + } +} + +unsafe impl DeviceOwned for Semaphore { + #[inline] + fn device(&self) -> &Arc<Device> { + &self.device + } +} + +unsafe impl<D> VulkanObject for Semaphore<D> +where + D: SafeDeref<Target = Device>, +{ + type Object = ash::vk::Semaphore; + + #[inline] + fn internal_object(&self) -> ash::vk::Semaphore { + self.semaphore + } +} + +impl<D> Drop for Semaphore<D> +where + D: SafeDeref<Target = Device>, +{ + #[inline] + fn drop(&mut self) { + unsafe { + if self.must_put_in_pool { + let raw_sem = self.semaphore; + self.device.semaphore_pool().lock().unwrap().push(raw_sem); + } else { + let fns = self.device.fns(); + fns.v1_0.destroy_semaphore( + self.device.internal_object(), + self.semaphore, + ptr::null(), + ); + } + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum SemaphoreError { + /// Not enough memory available. + OomError(OomError), + /// An extensions is missing. + MissingExtension(&'static str), +} + +impl fmt::Display for SemaphoreError { + fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + SemaphoreError::OomError(_) => write!(fmt, "not enough memory available"), + SemaphoreError::MissingExtension(s) => { + write!(fmt, "Missing the following extension: {}", s) + } + } + } +} + +impl From<Error> for SemaphoreError { + #[inline] + fn from(err: Error) -> SemaphoreError { + match err { + e @ Error::OutOfHostMemory | e @ Error::OutOfDeviceMemory => { + SemaphoreError::OomError(e.into()) + } + _ => panic!("unexpected error: {:?}", err), + } + } +} + +impl std::error::Error for SemaphoreError { + #[inline] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + SemaphoreError::OomError(ref err) => Some(err), + _ => None, + } + } +} + +impl From<OomError> for SemaphoreError { + #[inline] + fn from(err: OomError) -> SemaphoreError { + SemaphoreError::OomError(err) + } +} + +#[cfg(test)] +mod tests { + use crate::device::physical::PhysicalDevice; + use crate::device::{Device, DeviceExtensions}; + use crate::instance::{Instance, InstanceExtensions}; + use crate::VulkanObject; + use crate::{sync::Semaphore, Version}; + + #[test] + fn semaphore_create() { + let (device, _) = gfx_dev_and_queue!(); + let _ = Semaphore::alloc(device.clone()); + } + + #[test] + fn semaphore_pool() { + let (device, _) = gfx_dev_and_queue!(); + + assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0); + let sem1_internal_obj = { + let sem = Semaphore::from_pool(device.clone()).unwrap(); + assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0); + sem.internal_object() + }; + + assert_eq!(device.semaphore_pool().lock().unwrap().len(), 1); + let sem2 = Semaphore::from_pool(device.clone()).unwrap(); + assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0); + assert_eq!(sem2.internal_object(), sem1_internal_obj); + } + + #[test] + #[cfg(target_os = "linux")] + fn semaphore_export() { + let supported_ext = InstanceExtensions::supported_by_core().unwrap(); + if supported_ext.khr_get_display_properties2 + && supported_ext.khr_external_semaphore_capabilities + { + let instance = Instance::new( + None, + Version::V1_1, + &InstanceExtensions { + khr_get_physical_device_properties2: true, + khr_external_semaphore_capabilities: true, + ..InstanceExtensions::none() + }, + None, + ) + .unwrap(); + + let physical = PhysicalDevice::enumerate(&instance).next().unwrap(); + + let queue_family = physical.queue_families().next().unwrap(); + + let device_ext = DeviceExtensions { + khr_external_semaphore: true, + khr_external_semaphore_fd: true, + ..DeviceExtensions::none() + }; + let (device, _) = Device::new( + physical, + physical.supported_features(), + &device_ext, + [(queue_family, 0.5)].iter().cloned(), + ) + .unwrap(); + + let supported_ext = physical.supported_extensions(); + if supported_ext.khr_external_semaphore && supported_ext.khr_external_semaphore_fd { + let sem = Semaphore::alloc_with_exportable_fd(device.clone()).unwrap(); + let fd = sem.export_opaque_fd().unwrap(); + } + } + } +} |