aboutsummaryrefslogtreecommitdiff
path: root/src/sync/fence.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sync/fence.rs')
-rw-r--r--src/sync/fence.rs1851
1 files changed, 1548 insertions, 303 deletions
diff --git a/src/sync/fence.rs b/src/sync/fence.rs
index 208573a..05968f2 100644
--- a/src/sync/fence.rs
+++ b/src/sync/fence.rs
@@ -7,148 +7,293 @@
// 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::Success;
-use crate::VulkanObject;
+//! A fence provides synchronization between the device and the host, or between an external source
+//! and the host.
+
+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};
use smallvec::SmallVec;
-use std::error;
-use std::fmt;
-use std::mem::MaybeUninit;
-use std::ptr;
-use std::sync::atomic::AtomicBool;
-use std::sync::atomic::Ordering;
-use std::sync::Arc;
-use std::time::Duration;
-
-/// A fence is used to know when a command buffer submission has finished its execution.
+#[cfg(unix)]
+use std::fs::File;
+use std::{
+ error::Error,
+ fmt::{Display, Error as FmtError, Formatter},
+ future::Future,
+ mem::MaybeUninit,
+ num::NonZeroU64,
+ pin::Pin,
+ ptr,
+ sync::{Arc, Weak},
+ task::{Context, Poll},
+ time::Duration,
+};
+
+/// A two-state synchronization primitive that is signalled by the device and waited on by the host.
+///
+/// # Queue-to-host synchronization
+///
+/// The primary use of a fence is to know when execution of a queue has reached a particular point.
+/// When adding a command to a queue, a fence can be provided with the command, to be signaled
+/// when the operation finishes. You can check for a fence's current status by calling
+/// `is_signaled`, `wait` or `await` on it. If the fence is found to be signaled, that means that
+/// the queue has completed the operation that is associated with the fence, and all operations that
+/// were submitted before it have been completed as well.
+///
+/// When a queue command accesses a resource, it must be kept alive until the queue command has
+/// finished executing, and you may not be allowed to perform certain other operations (or even any)
+/// while the resource is in use. By calling `is_signaled`, `wait` or `await`, the queue will be
+/// notified when the fence is signaled, so that all resources of the associated queue operation and
+/// preceding operations can be released.
///
-/// When a command buffer accesses a resource, you have to ensure that the CPU doesn't access
-/// the same resource simultaneously (except for concurrent reads). Therefore in order to know
-/// when the CPU can access a resource again, a fence has to be used.
+/// Because of this, it is highly recommended to call `is_signaled`, `wait` or `await` on your fences.
+/// Otherwise, the queue will hold onto resources indefinitely (using up memory)
+/// and resource locks will not be released, which may cause errors when submitting future
+/// queue operations. It is not strictly necessary to wait for *every* fence, as a fence
+/// that was signaled later in the queue will automatically clean up resources associated with
+/// earlier fences too.
#[derive(Debug)]
-pub struct Fence<D = Arc<Device>>
-where
- D: SafeDeref<Target = Device>,
-{
- fence: ash::vk::Fence,
-
- device: D,
+pub struct Fence {
+ handle: ash::vk::Fence,
+ device: Arc<Device>,
+ id: NonZeroU64,
+ must_put_in_pool: bool,
- // If true, we know that the `Fence` is signaled. If false, we don't know.
- // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
- // multiple times.
- signaled: AtomicBool,
+ export_handle_types: ExternalFenceHandleTypes,
- // 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,
+ state: Mutex<FenceState>,
}
-impl<D> Fence<D>
-where
- D: SafeDeref<Target = Device>,
-{
+impl Fence {
+ /// Creates a new `Fence`.
+ #[inline]
+ pub fn new(device: Arc<Device>, create_info: FenceCreateInfo) -> Result<Fence, FenceError> {
+ Self::validate_new(&device, &create_info)?;
+
+ unsafe { Ok(Self::new_unchecked(device, create_info)?) }
+ }
+
+ fn validate_new(device: &Device, create_info: &FenceCreateInfo) -> Result<(), FenceError> {
+ let &FenceCreateInfo {
+ signaled: _,
+ export_handle_types,
+ _ne: _,
+ } = create_info;
+
+ if !export_handle_types.is_empty() {
+ if !(device.api_version() >= Version::V1_1
+ || device.enabled_extensions().khr_external_fence)
+ {
+ return Err(FenceError::RequirementNotMet {
+ required_for: "`create_info.export_handle_types` is not empty",
+ requires_one_of: RequiresOneOf {
+ api_version: Some(Version::V1_1),
+ device_extensions: &["khr_external_fence"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkExportFenceCreateInfo-handleTypes-01446
+ export_handle_types.validate_device(device)?;
+
+ // VUID-VkExportFenceCreateInfo-handleTypes-01446
+ for handle_type in export_handle_types.into_iter() {
+ let external_fence_properties = unsafe {
+ device
+ .physical_device()
+ .external_fence_properties_unchecked(ExternalFenceInfo::handle_type(
+ handle_type,
+ ))
+ };
+
+ if !external_fence_properties.exportable {
+ return Err(FenceError::HandleTypeNotExportable { handle_type });
+ }
+
+ if !external_fence_properties
+ .compatible_handle_types
+ .contains(export_handle_types)
+ {
+ return Err(FenceError::ExportHandleTypesNotCompatible);
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ #[inline]
+ pub unsafe fn new_unchecked(
+ device: Arc<Device>,
+ create_info: FenceCreateInfo,
+ ) -> Result<Fence, VulkanError> {
+ let FenceCreateInfo {
+ signaled,
+ export_handle_types,
+ _ne: _,
+ } = create_info;
+
+ let mut flags = ash::vk::FenceCreateFlags::empty();
+
+ if signaled {
+ flags |= ash::vk::FenceCreateFlags::SIGNALED;
+ }
+
+ let mut create_info_vk = ash::vk::FenceCreateInfo {
+ flags,
+ ..Default::default()
+ };
+ let mut export_fence_create_info_vk = None;
+
+ if !export_handle_types.is_empty() {
+ let _ = export_fence_create_info_vk.insert(ash::vk::ExportFenceCreateInfo {
+ handle_types: export_handle_types.into(),
+ ..Default::default()
+ });
+ }
+
+ if let Some(info) = export_fence_create_info_vk.as_mut() {
+ info.p_next = create_info_vk.p_next;
+ create_info_vk.p_next = info as *const _ as *const _;
+ }
+
+ let handle = {
+ let fns = device.fns();
+ let mut output = MaybeUninit::uninit();
+ (fns.v1_0.create_fence)(
+ device.handle(),
+ &create_info_vk,
+ ptr::null(),
+ output.as_mut_ptr(),
+ )
+ .result()
+ .map_err(VulkanError::from)?;
+
+ output.assume_init()
+ };
+
+ Ok(Fence {
+ handle,
+ device,
+ id: Self::next_id(),
+ must_put_in_pool: false,
+ export_handle_types,
+ state: Mutex::new(FenceState {
+ is_signaled: signaled,
+ ..Default::default()
+ }),
+ })
+ }
+
/// Takes a fence from the vulkano-provided fence pool.
- /// If the pool is empty, a new fence will be allocated.
+ /// If the pool is empty, a new fence will be created.
/// Upon `drop`, the fence is put back into the pool.
///
/// For most applications, using the fence pool should be preferred,
/// in order to avoid creating new fences every frame.
- pub fn from_pool(device: D) -> Result<Fence<D>, OomError> {
- let maybe_raw_fence = device.fence_pool().lock().unwrap().pop();
- match maybe_raw_fence {
- Some(raw_fence) => {
+ #[inline]
+ pub fn from_pool(device: Arc<Device>) -> Result<Fence, FenceError> {
+ let handle = device.fence_pool().lock().pop();
+ let fence = match handle {
+ Some(handle) => {
unsafe {
// Make sure the fence isn't signaled
let fns = device.fns();
- check_errors(
- fns.v1_0
- .reset_fences(device.internal_object(), 1, &raw_fence),
- )?;
+ (fns.v1_0.reset_fences)(device.handle(), 1, &handle)
+ .result()
+ .map_err(VulkanError::from)?;
}
- Ok(Fence {
- fence: raw_fence,
- device: device,
- signaled: AtomicBool::new(false),
+
+ Fence {
+ handle,
+ device,
+ id: Self::next_id(),
must_put_in_pool: true,
- })
+ export_handle_types: ExternalFenceHandleTypes::empty(),
+ state: Mutex::new(Default::default()),
+ }
}
None => {
// Pool is empty, alloc new fence
- Fence::alloc_impl(device, false, true)
+ let mut fence = Fence::new(device, FenceCreateInfo::default())?;
+ fence.must_put_in_pool = true;
+ fence
}
- }
- }
+ };
- /// Builds a new fence.
- #[inline]
- pub fn alloc(device: D) -> Result<Fence<D>, OomError> {
- Fence::alloc_impl(device, false, false)
+ Ok(fence)
}
- /// Builds a new fence in signaled state.
+ /// Creates a new `Fence` 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 fn alloc_signaled(device: D) -> Result<Fence<D>, OomError> {
- Fence::alloc_impl(device, true, false)
- }
+ pub unsafe fn from_handle(
+ device: Arc<Device>,
+ handle: ash::vk::Fence,
+ create_info: FenceCreateInfo,
+ ) -> Fence {
+ let FenceCreateInfo {
+ signaled,
+ export_handle_types,
+ _ne: _,
+ } = create_info;
- fn alloc_impl(device: D, signaled: bool, must_put_in_pool: bool) -> Result<Fence<D>, OomError> {
- let fence = unsafe {
- let infos = ash::vk::FenceCreateInfo {
- flags: if signaled {
- ash::vk::FenceCreateFlags::SIGNALED
- } else {
- ash::vk::FenceCreateFlags::empty()
- },
+ Fence {
+ handle,
+ device,
+ id: Self::next_id(),
+ must_put_in_pool: false,
+ export_handle_types,
+ state: Mutex::new(FenceState {
+ is_signaled: signaled,
..Default::default()
- };
-
- let fns = device.fns();
- let mut output = MaybeUninit::uninit();
- check_errors(fns.v1_0.create_fence(
- device.internal_object(),
- &infos,
- ptr::null(),
- output.as_mut_ptr(),
- ))?;
- output.assume_init()
- };
-
- Ok(Fence {
- fence: fence,
- device: device,
- signaled: AtomicBool::new(signaled),
- must_put_in_pool: must_put_in_pool,
- })
+ }),
+ }
}
/// Returns true if the fence is signaled.
#[inline]
- pub fn ready(&self) -> Result<bool, OomError> {
- unsafe {
- if self.signaled.load(Ordering::Relaxed) {
- return Ok(true);
+ pub fn is_signaled(&self) -> Result<bool, OomError> {
+ let queue_to_signal = {
+ let mut state = self.state();
+
+ // If the fence is already signaled, or it's unsignaled but there's no queue that
+ // could signal it, return the currently known value.
+ if let Some(is_signaled) = state.is_signaled() {
+ return Ok(is_signaled);
}
- let fns = self.device.fns();
- let result = check_errors(
- fns.v1_0
- .get_fence_status(self.device.internal_object(), self.fence),
- )?;
+ // We must ask Vulkan for the state.
+ let result = unsafe {
+ let fns = self.device.fns();
+ (fns.v1_0.get_fence_status)(self.device.handle(), self.handle)
+ };
+
match result {
- Success::Success => {
- self.signaled.store(true, Ordering::Relaxed);
- Ok(true)
- }
- Success::NotReady => Ok(false),
- _ => unreachable!(),
+ ash::vk::Result::SUCCESS => unsafe { state.set_signaled() },
+ ash::vk::Result::NOT_READY => return Ok(false),
+ err => return Err(VulkanError::from(err).into()),
+ }
+ };
+
+ // If we have a queue that we need to signal our status to,
+ // do so now after the state lock is dropped, to avoid deadlocks.
+ if let Some(queue) = queue_to_signal {
+ unsafe {
+ queue.with(|mut q| q.fence_signaled(self));
}
}
+
+ Ok(true)
}
/// Waits until the fence is signaled, or at least until the timeout duration has elapsed.
@@ -156,281 +301,1356 @@ where
/// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead.
///
/// If you pass a duration of 0, then the function will return without blocking.
- pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceWaitError> {
- unsafe {
- if self.signaled.load(Ordering::Relaxed) {
+ pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceError> {
+ let queue_to_signal = {
+ let mut state = self.state.lock();
+
+ // If the fence is already signaled, we don't need to wait.
+ if state.is_signaled().unwrap_or(false) {
return Ok(());
}
- let timeout_ns = if let Some(timeout) = timeout {
+ let timeout_ns = timeout.map_or(u64::MAX, |timeout| {
timeout
.as_secs()
.saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
- } else {
- u64::MAX
+ });
+
+ let result = unsafe {
+ let fns = self.device.fns();
+ (fns.v1_0.wait_for_fences)(
+ self.device.handle(),
+ 1,
+ &self.handle,
+ ash::vk::TRUE,
+ timeout_ns,
+ )
};
- let fns = self.device.fns();
- let r = check_errors(fns.v1_0.wait_for_fences(
- self.device.internal_object(),
- 1,
- &self.fence,
- ash::vk::TRUE,
- timeout_ns,
- ))?;
-
- match r {
- Success::Success => {
- self.signaled.store(true, Ordering::Relaxed);
- Ok(())
- }
- Success::Timeout => Err(FenceWaitError::Timeout),
- _ => unreachable!(),
+ match result {
+ ash::vk::Result::SUCCESS => unsafe { state.set_signaled() },
+ ash::vk::Result::TIMEOUT => return Err(FenceError::Timeout),
+ err => return Err(VulkanError::from(err).into()),
+ }
+ };
+
+ // If we have a queue that we need to signal our status to,
+ // do so now after the state lock is dropped, to avoid deadlocks.
+ if let Some(queue) = queue_to_signal {
+ unsafe {
+ queue.with(|mut q| q.fence_signaled(self));
}
}
+
+ Ok(())
}
/// Waits for multiple fences at once.
///
- /// # Panic
+ /// # Panics
///
- /// Panics if not all fences belong to the same device.
- pub fn multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceWaitError>
- where
- I: IntoIterator<Item = &'a Fence<D>>,
- D: 'a,
- {
- let mut device: Option<&Device> = None;
+ /// - Panics if not all fences belong to the same device.
+ pub fn multi_wait<'a>(
+ fences: impl IntoIterator<Item = &'a Fence>,
+ timeout: Option<Duration>,
+ ) -> Result<(), FenceError> {
+ let fences: SmallVec<[_; 8]> = fences.into_iter().collect();
+ Self::validate_multi_wait(&fences, timeout)?;
- let fences: SmallVec<[ash::vk::Fence; 8]> = iter
- .into_iter()
- .filter_map(|fence| {
- match &mut device {
- dev @ &mut None => *dev = Some(&*fence.device),
- &mut Some(ref dev)
- if &**dev as *const Device == &*fence.device as *const Device => {}
- _ => panic!(
- "Tried to wait for multiple fences that didn't belong to the \
- same device"
- ),
- };
+ unsafe { Self::multi_wait_unchecked(fences, timeout) }
+ }
+
+ fn validate_multi_wait(
+ fences: &[&Fence],
+ _timeout: Option<Duration>,
+ ) -> Result<(), FenceError> {
+ if fences.is_empty() {
+ return Ok(());
+ }
+
+ let device = &fences[0].device;
+
+ for fence in fences {
+ // VUID-vkWaitForFences-pFences-parent
+ assert_eq!(device, &fence.device);
+ }
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn multi_wait_unchecked<'a>(
+ fences: impl IntoIterator<Item = &'a Fence>,
+ timeout: Option<Duration>,
+ ) -> Result<(), FenceError> {
+ let queues_to_signal: SmallVec<[_; 8]> = {
+ let iter = fences.into_iter();
+ let mut fences_vk: SmallVec<[_; 8]> = SmallVec::new();
+ let mut fences: SmallVec<[_; 8]> = SmallVec::new();
+ let mut states: SmallVec<[_; 8]> = SmallVec::new();
+
+ for fence in iter {
+ let state = fence.state.lock();
- if fence.signaled.load(Ordering::Relaxed) {
- None
- } else {
- Some(fence.fence)
+ // Skip the fences that are already signaled.
+ if !state.is_signaled().unwrap_or(false) {
+ fences_vk.push(fence.handle);
+ fences.push(fence);
+ states.push(state);
}
- })
- .collect();
+ }
- let timeout_ns = if let Some(timeout) = timeout {
- timeout
- .as_secs()
- .saturating_mul(1_000_000_000)
- .saturating_add(timeout.subsec_nanos() as u64)
- } else {
- u64::MAX
- };
+ // VUID-vkWaitForFences-fenceCount-arraylength
+ // If there are no fences, or all the fences are signaled, we don't need to wait.
+ if fences_vk.is_empty() {
+ return Ok(());
+ }
- let r = if let Some(device) = device {
- unsafe {
+ let device = &fences[0].device;
+ let timeout_ns = timeout.map_or(u64::MAX, |timeout| {
+ timeout
+ .as_secs()
+ .saturating_mul(1_000_000_000)
+ .saturating_add(timeout.subsec_nanos() as u64)
+ });
+
+ let result = {
let fns = device.fns();
- check_errors(fns.v1_0.wait_for_fences(
- device.internal_object(),
- fences.len() as u32,
- fences.as_ptr(),
- ash::vk::TRUE,
+ (fns.v1_0.wait_for_fences)(
+ device.handle(),
+ fences_vk.len() as u32,
+ fences_vk.as_ptr(),
+ ash::vk::TRUE, // TODO: let the user choose false here?
timeout_ns,
- ))?
+ )
+ };
+
+ match result {
+ ash::vk::Result::SUCCESS => fences
+ .into_iter()
+ .zip(&mut states)
+ .filter_map(|(fence, state)| state.set_signaled().map(|state| (state, fence)))
+ .collect(),
+ ash::vk::Result::TIMEOUT => return Err(FenceError::Timeout),
+ err => return Err(VulkanError::from(err).into()),
}
- } else {
- return Ok(());
};
- match r {
- Success::Success => Ok(()),
- Success::Timeout => Err(FenceWaitError::Timeout),
- _ => unreachable!(),
+ // If we have queues that we need to signal our status to,
+ // do so now after the state locks are dropped, to avoid deadlocks.
+ for (queue, fence) in queues_to_signal {
+ queue.with(|mut q| q.fence_signaled(fence));
}
+
+ Ok(())
}
/// Resets the fence.
- // This function takes a `&mut self` because the Vulkan API requires that the fence be
- // externally synchronized.
+ ///
+ /// The fence must not be in use by a queue operation.
#[inline]
- pub fn reset(&mut self) -> Result<(), OomError> {
- unsafe {
- let fns = self.device.fns();
- check_errors(
- fns.v1_0
- .reset_fences(self.device.internal_object(), 1, &self.fence),
- )?;
- self.signaled.store(false, Ordering::Relaxed);
- Ok(())
+ pub fn reset(&self) -> Result<(), FenceError> {
+ let mut state = self.state.lock();
+ self.validate_reset(&state)?;
+
+ unsafe { Ok(self.reset_unchecked_locked(&mut state)?) }
+ }
+
+ fn validate_reset(&self, state: &FenceState) -> Result<(), FenceError> {
+ // VUID-vkResetFences-pFences-01123
+ if state.is_in_queue() {
+ return Err(FenceError::InQueue);
}
+
+ Ok(())
+ }
+
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ #[inline]
+ pub unsafe fn reset_unchecked(&self) -> Result<(), VulkanError> {
+ let mut state = self.state.lock();
+
+ self.reset_unchecked_locked(&mut state)
+ }
+
+ unsafe fn reset_unchecked_locked(&self, state: &mut FenceState) -> Result<(), VulkanError> {
+ let fns = self.device.fns();
+ (fns.v1_0.reset_fences)(self.device.handle(), 1, &self.handle)
+ .result()
+ .map_err(VulkanError::from)?;
+
+ state.reset();
+
+ Ok(())
}
/// Resets multiple fences at once.
///
- /// # Panic
+ /// The fences must not be in use by a queue operation.
///
- /// - Panics if not all fences belong to the same device.
+ /// # Panics
///
- pub fn multi_reset<'a, I>(iter: I) -> Result<(), OomError>
- where
- I: IntoIterator<Item = &'a mut Fence<D>>,
- D: 'a,
- {
- let mut device: Option<&Device> = None;
-
- let fences: SmallVec<[ash::vk::Fence; 8]> = iter
+ /// - Panics if not all fences belong to the same device.
+ pub fn multi_reset<'a>(fences: impl IntoIterator<Item = &'a Fence>) -> Result<(), FenceError> {
+ let (fences, mut states): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = fences
.into_iter()
.map(|fence| {
- match &mut device {
- dev @ &mut None => *dev = Some(&*fence.device),
- &mut Some(ref dev)
- if &**dev as *const Device == &*fence.device as *const Device => {}
- _ => panic!(
- "Tried to reset multiple fences that didn't belong to the same \
- device"
- ),
- };
+ let state = fence.state.lock();
+ (fence, state)
+ })
+ .unzip();
+ Self::validate_multi_reset(&fences, &states)?;
+
+ unsafe { Ok(Self::multi_reset_unchecked_locked(&fences, &mut states)?) }
+ }
+
+ fn validate_multi_reset(
+ fences: &[&Fence],
+ states: &[MutexGuard<'_, FenceState>],
+ ) -> Result<(), FenceError> {
+ if fences.is_empty() {
+ return Ok(());
+ }
+
+ let device = &fences[0].device;
+
+ for (fence, state) in fences.iter().zip(states) {
+ // VUID-vkResetFences-pFences-parent
+ assert_eq!(device, &fence.device);
+
+ // VUID-vkResetFences-pFences-01123
+ if state.is_in_queue() {
+ return Err(FenceError::InQueue);
+ }
+ }
+
+ Ok(())
+ }
- fence.signaled.store(false, Ordering::Relaxed);
- fence.fence
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn multi_reset_unchecked<'a>(
+ fences: impl IntoIterator<Item = &'a Fence>,
+ ) -> Result<(), VulkanError> {
+ let (fences, mut states): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = fences
+ .into_iter()
+ .map(|fence| {
+ let state = fence.state.lock();
+ (fence, state)
})
- .collect();
+ .unzip();
- if let Some(device) = device {
- unsafe {
- let fns = device.fns();
- check_errors(fns.v1_0.reset_fences(
- device.internal_object(),
- fences.len() as u32,
- fences.as_ptr(),
- ))?;
+ Self::multi_reset_unchecked_locked(&fences, &mut states)
+ }
+
+ unsafe fn multi_reset_unchecked_locked(
+ fences: &[&Fence],
+ states: &mut [MutexGuard<'_, FenceState>],
+ ) -> Result<(), VulkanError> {
+ if fences.is_empty() {
+ return Ok(());
+ }
+
+ let device = &fences[0].device;
+ let fences_vk: SmallVec<[_; 8]> = fences.iter().map(|fence| fence.handle).collect();
+
+ let fns = device.fns();
+ (fns.v1_0.reset_fences)(device.handle(), fences_vk.len() as u32, fences_vk.as_ptr())
+ .result()
+ .map_err(VulkanError::from)?;
+
+ for state in states {
+ state.reset();
+ }
+
+ Ok(())
+ }
+
+ /// Exports the fence into a POSIX file descriptor. The caller owns the returned `File`.
+ ///
+ /// The [`khr_external_fence_fd`](crate::device::DeviceExtensions::khr_external_fence_fd)
+ /// extension must be enabled on the device.
+ #[cfg(unix)]
+ #[inline]
+ pub fn export_fd(&self, handle_type: ExternalFenceHandleType) -> Result<File, FenceError> {
+ 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: ExternalFenceHandleType,
+ state: &FenceState,
+ ) -> Result<(), FenceError> {
+ if !self.device.enabled_extensions().khr_external_fence_fd {
+ return Err(FenceError::RequirementNotMet {
+ required_for: "`Fence::export_fd`",
+ requires_one_of: RequiresOneOf {
+ device_extensions: &["khr_external_fence_fd"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkFenceGetFdInfoKHR-handleType-parameter
+ handle_type.validate_device(&self.device)?;
+
+ // VUID-VkFenceGetFdInfoKHR-handleType-01453
+ if !self.export_handle_types.intersects(handle_type.into()) {
+ return Err(FenceError::HandleTypeNotEnabled);
+ }
+
+ // VUID-VkFenceGetFdInfoKHR-handleType-01454
+ if handle_type.has_copy_transference()
+ && !(state.is_signaled().unwrap_or(false) || state.is_in_queue())
+ {
+ return Err(FenceError::HandleTypeCopyNotSignaled);
+ }
+
+ // VUID-VkFenceGetFdInfoKHR-fence-01455
+ if let Some(imported_handle_type) = state.current_import {
+ match imported_handle_type {
+ ImportType::SwapchainAcquire => {
+ return Err(FenceError::ImportedForSwapchainAcquire)
+ }
+ ImportType::ExternalFence(imported_handle_type) => {
+ let external_fence_properties = unsafe {
+ self.device
+ .physical_device()
+ .external_fence_properties_unchecked(ExternalFenceInfo::handle_type(
+ handle_type,
+ ))
+ };
+
+ if !external_fence_properties
+ .export_from_imported_handle_types
+ .intersects(imported_handle_type.into())
+ {
+ return Err(FenceError::ExportFromImportedNotSupported {
+ imported_handle_type,
+ });
+ }
+ }
}
}
+
+ // VUID-VkFenceGetFdInfoKHR-handleType-01456
+ if !matches!(
+ handle_type,
+ ExternalFenceHandleType::OpaqueFd | ExternalFenceHandleType::SyncFd
+ ) {
+ return Err(FenceError::HandleTypeNotFd);
+ }
+
Ok(())
}
-}
-unsafe impl DeviceOwned for Fence {
+ #[cfg(unix)]
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
- fn device(&self) -> &Arc<Device> {
- &self.device
+ pub unsafe fn export_fd_unchecked(
+ &self,
+ handle_type: ExternalFenceHandleType,
+ ) -> Result<File, VulkanError> {
+ 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: ExternalFenceHandleType,
+ state: &mut FenceState,
+ ) -> Result<File, VulkanError> {
+ use std::os::unix::io::FromRawFd;
+
+ let info_vk = ash::vk::FenceGetFdInfoKHR {
+ fence: self.handle,
+ handle_type: handle_type.into(),
+ ..Default::default()
+ };
+
+ let mut output = MaybeUninit::uninit();
+ let fns = self.device.fns();
+ (fns.khr_external_fence_fd.get_fence_fd_khr)(
+ self.device.handle(),
+ &info_vk,
+ output.as_mut_ptr(),
+ )
+ .result()
+ .map_err(VulkanError::from)?;
+
+ state.export(handle_type);
+
+ Ok(File::from_raw_fd(output.assume_init()))
+ }
+
+ /// Exports the fence into a Win32 handle.
+ ///
+ /// The [`khr_external_fence_win32`](crate::device::DeviceExtensions::khr_external_fence_win32)
+ /// extension must be enabled on the device.
+ #[cfg(windows)]
+ #[inline]
+ pub fn export_win32_handle(
+ &self,
+ handle_type: ExternalFenceHandleType,
+ ) -> Result<*mut std::ffi::c_void, FenceError> {
+ 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: ExternalFenceHandleType,
+ state: &FenceState,
+ ) -> Result<(), FenceError> {
+ if !self.device.enabled_extensions().khr_external_fence_win32 {
+ return Err(FenceError::RequirementNotMet {
+ required_for: "`Fence::export_win32_handle`",
+ requires_one_of: RequiresOneOf {
+ device_extensions: &["khr_external_fence_win32"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-VkFenceGetWin32HandleInfoKHR-handleType-parameter
+ handle_type.validate_device(&self.device)?;
+
+ // VUID-VkFenceGetWin32HandleInfoKHR-handleType-01448
+ if !self.export_handle_types.intersects(handle_type.into()) {
+ return Err(FenceError::HandleTypeNotEnabled);
+ }
+
+ // VUID-VkFenceGetWin32HandleInfoKHR-handleType-01449
+ if matches!(handle_type, ExternalFenceHandleType::OpaqueWin32)
+ && state.is_exported(handle_type)
+ {
+ return Err(FenceError::AlreadyExported);
+ }
+
+ // VUID-VkFenceGetWin32HandleInfoKHR-handleType-01451
+ if handle_type.has_copy_transference()
+ && !(state.is_signaled().unwrap_or(false) || state.is_in_queue())
+ {
+ return Err(FenceError::HandleTypeCopyNotSignaled);
+ }
+
+ // VUID-VkFenceGetWin32HandleInfoKHR-fence-01450
+ if let Some(imported_handle_type) = state.current_import {
+ match imported_handle_type {
+ ImportType::SwapchainAcquire => {
+ return Err(FenceError::ImportedForSwapchainAcquire)
+ }
+ ImportType::ExternalFence(imported_handle_type) => {
+ let external_fence_properties = unsafe {
+ self.device
+ .physical_device()
+ .external_fence_properties_unchecked(ExternalFenceInfo::handle_type(
+ handle_type,
+ ))
+ };
+
+ if !external_fence_properties
+ .export_from_imported_handle_types
+ .intersects(imported_handle_type.into())
+ {
+ return Err(FenceError::ExportFromImportedNotSupported {
+ imported_handle_type,
+ });
+ }
+ }
+ }
+ }
+
+ // VUID-VkFenceGetWin32HandleInfoKHR-handleType-01452
+ if !matches!(
+ handle_type,
+ ExternalFenceHandleType::OpaqueWin32 | ExternalFenceHandleType::OpaqueWin32Kmt
+ ) {
+ return Err(FenceError::HandleTypeNotWin32);
+ }
+
+ Ok(())
+ }
+
+ #[cfg(windows)]
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ #[inline]
+ pub unsafe fn export_win32_handle_unchecked(
+ &self,
+ handle_type: ExternalFenceHandleType,
+ ) -> 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: ExternalFenceHandleType,
+ state: &mut FenceState,
+ ) -> Result<*mut std::ffi::c_void, VulkanError> {
+ let info_vk = ash::vk::FenceGetWin32HandleInfoKHR {
+ fence: self.handle,
+ handle_type: handle_type.into(),
+ ..Default::default()
+ };
+
+ let mut output = MaybeUninit::uninit();
+ let fns = self.device.fns();
+ (fns.khr_external_fence_win32.get_fence_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())
+ }
+
+ /// Imports a fence from a POSIX file descriptor.
+ ///
+ /// The [`khr_external_fence_fd`](crate::device::DeviceExtensions::khr_external_fence_fd)
+ /// extension must be enabled on the device.
+ ///
+ /// # Safety
+ ///
+ /// - If in `import_fence_fd_info`, `handle_type` is `ExternalHandleType::OpaqueFd`,
+ /// 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]
+ pub unsafe fn import_fd(
+ &self,
+ import_fence_fd_info: ImportFenceFdInfo,
+ ) -> Result<(), FenceError> {
+ let mut state = self.state.lock();
+ self.validate_import_fd(&import_fence_fd_info, &state)?;
+
+ Ok(self.import_fd_unchecked_locked(import_fence_fd_info, &mut state)?)
}
-}
-unsafe impl<D> VulkanObject for Fence<D>
-where
- D: SafeDeref<Target = Device>,
-{
- type Object = ash::vk::Fence;
+ #[cfg(unix)]
+ fn validate_import_fd(
+ &self,
+ import_fence_fd_info: &ImportFenceFdInfo,
+ state: &FenceState,
+ ) -> Result<(), FenceError> {
+ if !self.device.enabled_extensions().khr_external_fence_fd {
+ return Err(FenceError::RequirementNotMet {
+ required_for: "`Fence::import_fd`",
+ requires_one_of: RequiresOneOf {
+ device_extensions: &["khr_external_fence_fd"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-vkImportFenceFdKHR-fence-01463
+ if state.is_in_queue() {
+ return Err(FenceError::InQueue);
+ }
+
+ let &ImportFenceFdInfo {
+ flags,
+ handle_type,
+ file: _,
+ _ne: _,
+ } = import_fence_fd_info;
+
+ // VUID-VkImportFenceFdInfoKHR-flags-parameter
+ flags.validate_device(&self.device)?;
+
+ // VUID-VkImportFenceFdInfoKHR-handleType-parameter
+ handle_type.validate_device(&self.device)?;
+
+ // VUID-VkImportFenceFdInfoKHR-handleType-01464
+ if !matches!(
+ handle_type,
+ ExternalFenceHandleType::OpaqueFd | ExternalFenceHandleType::SyncFd
+ ) {
+ return Err(FenceError::HandleTypeNotFd);
+ }
+
+ // VUID-VkImportFenceFdInfoKHR-fd-01541
+ // Can't validate, therefore unsafe
+
+ // VUID-VkImportFenceFdInfoKHR-handleType-07306
+ if handle_type.has_copy_transference() && !flags.intersects(FenceImportFlags::TEMPORARY) {
+ return Err(FenceError::HandletypeCopyNotTemporary);
+ }
+ Ok(())
+ }
+
+ #[cfg(unix)]
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
- fn internal_object(&self) -> ash::vk::Fence {
- self.fence
+ pub unsafe fn import_fd_unchecked(
+ &self,
+ import_fence_fd_info: ImportFenceFdInfo,
+ ) -> Result<(), VulkanError> {
+ let mut state = self.state.lock();
+ self.import_fd_unchecked_locked(import_fence_fd_info, &mut state)
+ }
+
+ #[cfg(unix)]
+ unsafe fn import_fd_unchecked_locked(
+ &self,
+ import_fence_fd_info: ImportFenceFdInfo,
+ state: &mut FenceState,
+ ) -> Result<(), VulkanError> {
+ use std::os::unix::io::IntoRawFd;
+
+ let ImportFenceFdInfo {
+ flags,
+ handle_type,
+ file,
+ _ne: _,
+ } = import_fence_fd_info;
+
+ let info_vk = ash::vk::ImportFenceFdInfoKHR {
+ fence: 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_fence_fd.import_fence_fd_khr)(self.device.handle(), &info_vk)
+ .result()
+ .map_err(VulkanError::from)?;
+
+ state.import(handle_type, flags.intersects(FenceImportFlags::TEMPORARY));
+
+ Ok(())
+ }
+
+ /// Imports a fence from a Win32 handle.
+ ///
+ /// The [`khr_external_fence_win32`](crate::device::DeviceExtensions::khr_external_fence_win32)
+ /// extension must be enabled on the device.
+ ///
+ /// # Safety
+ ///
+ /// - 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(
+ &self,
+ import_fence_win32_handle_info: ImportFenceWin32HandleInfo,
+ ) -> Result<(), FenceError> {
+ let mut state = self.state.lock();
+ self.validate_import_win32_handle(&import_fence_win32_handle_info, &state)?;
+
+ Ok(self.import_win32_handle_unchecked_locked(import_fence_win32_handle_info, &mut state)?)
+ }
+
+ #[cfg(windows)]
+ fn validate_import_win32_handle(
+ &self,
+ import_fence_win32_handle_info: &ImportFenceWin32HandleInfo,
+ state: &FenceState,
+ ) -> Result<(), FenceError> {
+ if !self.device.enabled_extensions().khr_external_fence_win32 {
+ return Err(FenceError::RequirementNotMet {
+ required_for: "`Fence::import_win32_handle`",
+ requires_one_of: RequiresOneOf {
+ device_extensions: &["khr_external_fence_win32"],
+ ..Default::default()
+ },
+ });
+ }
+
+ // VUID-vkImportFenceWin32HandleKHR-fence-04448
+ if state.is_in_queue() {
+ return Err(FenceError::InQueue);
+ }
+
+ let &ImportFenceWin32HandleInfo {
+ flags,
+ handle_type,
+ handle: _,
+ _ne: _,
+ } = import_fence_win32_handle_info;
+
+ // VUID-VkImportFenceWin32HandleInfoKHR-flags-parameter
+ flags.validate_device(&self.device)?;
+
+ // VUID-VkImportFenceWin32HandleInfoKHR-handleType-01457
+ handle_type.validate_device(&self.device)?;
+
+ // VUID-VkImportFenceWin32HandleInfoKHR-handleType-01457
+ if !matches!(
+ handle_type,
+ ExternalFenceHandleType::OpaqueWin32 | ExternalFenceHandleType::OpaqueWin32Kmt
+ ) {
+ return Err(FenceError::HandleTypeNotWin32);
+ }
+
+ // VUID-VkImportFenceWin32HandleInfoKHR-handle-01539
+ // Can't validate, therefore unsafe
+
+ // VUID?
+ if handle_type.has_copy_transference() && !flags.intersects(FenceImportFlags::TEMPORARY) {
+ return Err(FenceError::HandletypeCopyNotTemporary);
+ }
+
+ Ok(())
+ }
+
+ #[cfg(windows)]
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ #[inline]
+ pub unsafe fn import_win32_handle_unchecked(
+ &self,
+ import_fence_win32_handle_info: ImportFenceWin32HandleInfo,
+ ) -> Result<(), VulkanError> {
+ let mut state = self.state.lock();
+ self.import_win32_handle_unchecked_locked(import_fence_win32_handle_info, &mut state)
+ }
+
+ #[cfg(windows)]
+ unsafe fn import_win32_handle_unchecked_locked(
+ &self,
+ import_fence_win32_handle_info: ImportFenceWin32HandleInfo,
+ state: &mut FenceState,
+ ) -> Result<(), VulkanError> {
+ let ImportFenceWin32HandleInfo {
+ flags,
+ handle_type,
+ handle,
+ _ne: _,
+ } = import_fence_win32_handle_info;
+
+ let info_vk = ash::vk::ImportFenceWin32HandleInfoKHR {
+ fence: 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_fence_win32.import_fence_win32_handle_khr)(
+ self.device.handle(),
+ &info_vk,
+ )
+ .result()
+ .map_err(VulkanError::from)?;
+
+ state.import(handle_type, flags.intersects(FenceImportFlags::TEMPORARY));
+
+ Ok(())
+ }
+
+ pub(crate) fn state(&self) -> MutexGuard<'_, FenceState> {
+ self.state.lock()
+ }
+
+ // Shared by Fence and FenceSignalFuture
+ pub(crate) fn poll_impl(&self, cx: &mut Context<'_>) -> Poll<Result<(), OomError>> {
+ // Vulkan only allows polling of the fence status, so we have to use a spin future.
+ // This is still better than blocking in async applications, since a smart-enough async engine
+ // can choose to run some other tasks between probing this one.
+
+ // Check if we are done without blocking
+ match self.is_signaled() {
+ Err(e) => return Poll::Ready(Err(e)),
+ Ok(signalled) => {
+ if signalled {
+ return Poll::Ready(Ok(()));
+ }
+ }
+ }
+
+ // Otherwise spin
+ cx.waker().wake_by_ref();
+ Poll::Pending
}
}
-impl<D> Drop for Fence<D>
-where
- D: SafeDeref<Target = Device>,
-{
+impl Drop for Fence {
#[inline]
fn drop(&mut self) {
unsafe {
if self.must_put_in_pool {
- let raw_fence = self.fence;
- self.device.fence_pool().lock().unwrap().push(raw_fence);
+ let raw_fence = self.handle;
+ self.device.fence_pool().lock().push(raw_fence);
} else {
let fns = self.device.fns();
- fns.v1_0
- .destroy_fence(self.device.internal_object(), self.fence, ptr::null());
+ (fns.v1_0.destroy_fence)(self.device.handle(), self.handle, ptr::null());
}
}
}
}
-/// Error that can be returned when waiting on a fence.
+impl Future for Fence {
+ type Output = Result<(), OomError>;
+
+ fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ self.poll_impl(cx)
+ }
+}
+
+unsafe impl VulkanObject for Fence {
+ type Handle = ash::vk::Fence;
+
+ #[inline]
+ fn handle(&self) -> Self::Handle {
+ self.handle
+ }
+}
+
+unsafe impl DeviceOwned for Fence {
+ #[inline]
+ fn device(&self) -> &Arc<Device> {
+ &self.device
+ }
+}
+
+impl_id_counter!(Fence);
+
+#[derive(Debug, Default)]
+pub(crate) struct FenceState {
+ is_signaled: bool,
+ pending_signal: Option<Weak<Queue>>,
+
+ reference_exported: bool,
+ exported_handle_types: ExternalFenceHandleTypes,
+ current_import: Option<ImportType>,
+ permanent_import: Option<ExternalFenceHandleType>,
+}
+
+impl FenceState {
+ /// If the fence is not in a queue and has no external references, returns the current status.
+ #[inline]
+ fn is_signaled(&self) -> Option<bool> {
+ // If either of these is true, we can't be certain of the status.
+ if self.is_in_queue() || self.has_external_reference() {
+ None
+ } else {
+ Some(self.is_signaled)
+ }
+ }
+
+ #[inline]
+ fn is_in_queue(&self) -> bool {
+ self.pending_signal.is_some()
+ }
+
+ /// Returns whether there are any potential external references to the fence payload.
+ /// That is, the fence 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: ExternalFenceHandleType) -> bool {
+ self.exported_handle_types.intersects(handle_type.into())
+ }
+
+ #[inline]
+ pub(crate) unsafe fn add_queue_signal(&mut self, queue: &Arc<Queue>) {
+ self.pending_signal = Some(Arc::downgrade(queue));
+ }
+
+ /// Called when a fence first discovers that it is signaled.
+ /// Returns the queue that should be informed about it.
+ #[inline]
+ unsafe fn set_signaled(&mut self) -> Option<Arc<Queue>> {
+ self.is_signaled = true;
+
+ // Fences with external references can't be used to determine queue completion.
+ if self.has_external_reference() {
+ self.pending_signal = None;
+ None
+ } else {
+ self.pending_signal.take().and_then(|queue| queue.upgrade())
+ }
+ }
+
+ /// Called when a queue is unlocking resources.
+ #[inline]
+ pub(crate) unsafe fn set_signal_finished(&mut self) {
+ self.is_signaled = true;
+ self.pending_signal = None;
+ }
+
+ #[inline]
+ unsafe fn reset(&mut self) {
+ debug_assert!(!self.is_in_queue());
+ self.current_import = self.permanent_import.map(Into::into);
+ self.is_signaled = false;
+ }
+
+ #[allow(dead_code)]
+ #[inline]
+ unsafe fn export(&mut self, handle_type: ExternalFenceHandleType) {
+ self.exported_handle_types |= handle_type.into();
+
+ if handle_type.has_copy_transference() {
+ self.reset();
+ } else {
+ self.reference_exported = true;
+ }
+ }
+
+ #[allow(dead_code)]
+ #[inline]
+ unsafe fn import(&mut self, handle_type: ExternalFenceHandleType, temporary: bool) {
+ debug_assert!(!self.is_in_queue());
+ self.current_import = Some(handle_type.into());
+
+ if !temporary {
+ self.permanent_import = Some(handle_type);
+ }
+ }
+
+ #[inline]
+ pub(crate) unsafe fn import_swapchain_acquire(&mut self) {
+ debug_assert!(!self.is_in_queue());
+ self.current_import = Some(ImportType::SwapchainAcquire);
+ }
+}
+
+#[derive(Clone, Copy, Debug)]
+enum ImportType {
+ SwapchainAcquire,
+ ExternalFence(ExternalFenceHandleType),
+}
+
+impl From<ExternalFenceHandleType> for ImportType {
+ #[inline]
+ fn from(handle_type: ExternalFenceHandleType) -> Self {
+ Self::ExternalFence(handle_type)
+ }
+}
+
+/// Parameters to create a new `Fence`.
+#[derive(Clone, Debug)]
+pub struct FenceCreateInfo {
+ /// Whether the fence should be created in the signaled state.
+ ///
+ /// The default value is `false`.
+ pub signaled: bool,
+
+ /// The handle types that can be exported from the fence.
+ pub export_handle_types: ExternalFenceHandleTypes,
+
+ pub _ne: crate::NonExhaustive,
+}
+
+impl Default for FenceCreateInfo {
+ #[inline]
+ fn default() -> Self {
+ Self {
+ signaled: false,
+ export_handle_types: ExternalFenceHandleTypes::empty(),
+ _ne: crate::NonExhaustive(()),
+ }
+ }
+}
+
+vulkan_bitflags_enum! {
+ #[non_exhaustive]
+ /// A set of [`ExternalFenceHandleType`] values.
+ ExternalFenceHandleTypes,
+
+ /// The handle type used to export or import fences to/from an external source.
+ ExternalFenceHandleType 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 the fence is already signaled, or if there is a fence 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-fence-handletypes-win32
+ // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-fence-handletypes-fd
+ matches!(self, Self::SyncFd)
+ }
+ },
+
+ = ExternalFenceHandleTypeFlags(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 POSIX file descriptor handle to a Linux Sync File or Android Fence object.
+ ///
+ /// This handle type has *copy transference*.
+ SYNC_FD, SyncFd = SYNC_FD,
+}
+
+vulkan_bitflags! {
+ #[non_exhaustive]
+
+ /// Additional parameters for a fence payload import.
+ FenceImportFlags = FenceImportFlags(u32);
+
+ /// The fence payload will be imported only temporarily, regardless of the permanence of the
+ /// imported handle type.
+ TEMPORARY = TEMPORARY,
+}
+
+#[cfg(unix)]
+#[derive(Debug)]
+pub struct ImportFenceFdInfo {
+ /// Additional parameters for the import operation.
+ ///
+ /// If `handle_type` has *copy transference*, this must include the `temporary` flag.
+ ///
+ /// The default value is [`FenceImportFlags::empty()`].
+ pub flags: FenceImportFlags,
+
+ /// The handle type of `file`.
+ ///
+ /// There is no default value.
+ pub handle_type: ExternalFenceHandleType,
+
+ /// The file to import the fence from.
+ ///
+ /// If `handle_type` is `ExternalFenceHandleType::SyncFd`, then `file` can be `None`.
+ /// Instead of an imported file descriptor, a dummy file descriptor `-1` is used,
+ /// which represents a fence that is always signaled.
+ ///
+ /// The default value is `None`, which must be overridden if `handle_type` is not
+ /// `ExternalFenceHandleType::SyncFd`.
+ pub file: Option<File>,
+
+ pub _ne: crate::NonExhaustive,
+}
+
+#[cfg(unix)]
+impl ImportFenceFdInfo {
+ /// Returns an `ImportFenceFdInfo` with the specified `handle_type`.
+ #[inline]
+ pub fn handle_type(handle_type: ExternalFenceHandleType) -> Self {
+ Self {
+ flags: FenceImportFlags::empty(),
+ handle_type,
+ file: None,
+ _ne: crate::NonExhaustive(()),
+ }
+ }
+}
+
+#[cfg(windows)]
+#[derive(Debug)]
+pub struct ImportFenceWin32HandleInfo {
+ /// Additional parameters for the import operation.
+ ///
+ /// If `handle_type` has *copy transference*, this must include the `temporary` flag.
+ ///
+ /// The default value is [`FenceImportFlags::empty()`].
+ pub flags: FenceImportFlags,
+
+ /// The handle type of `handle`.
+ ///
+ /// There is no default value.
+ pub handle_type: ExternalFenceHandleType,
+
+ /// The file to import the fence from.
+ ///
+ /// The default value is `null`, which must be overridden.
+ pub handle: *mut std::ffi::c_void,
+
+ pub _ne: crate::NonExhaustive,
+}
+
+#[cfg(windows)]
+impl ImportFenceWin32HandleInfo {
+ /// Returns an `ImportFenceWin32HandleInfo` with the specified `handle_type`.
+ #[inline]
+ pub fn handle_type(handle_type: ExternalFenceHandleType) -> Self {
+ Self {
+ flags: FenceImportFlags::empty(),
+ handle_type,
+ handle: ptr::null_mut(),
+ _ne: crate::NonExhaustive(()),
+ }
+ }
+}
+
+/// The fence configuration to query in
+/// [`PhysicalDevice::external_fence_properties`](crate::device::physical::PhysicalDevice::external_fence_properties).
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct ExternalFenceInfo {
+ /// The external handle type that will be used with the fence.
+ pub handle_type: ExternalFenceHandleType,
+
+ pub _ne: crate::NonExhaustive,
+}
+
+impl ExternalFenceInfo {
+ /// Returns an `ExternalFenceInfo` with the specified `handle_type`.
+ #[inline]
+ pub fn handle_type(handle_type: ExternalFenceHandleType) -> Self {
+ Self {
+ handle_type,
+ _ne: crate::NonExhaustive(()),
+ }
+ }
+}
+
+/// The properties for exporting or importing external handles, when a fence is created
+/// with a specific configuration.
+#[derive(Clone, Debug)]
+#[non_exhaustive]
+pub struct ExternalFenceProperties {
+ /// Whether a handle can be exported to an external source with the queried
+ /// external handle type.
+ pub exportable: bool,
+
+ /// Whether a handle can be imported from an external source with the queried
+ /// external handle type.
+ pub importable: bool,
+
+ /// Which external handle types can be re-exported after the queried external handle type has
+ /// been imported.
+ pub export_from_imported_handle_types: ExternalFenceHandleTypes,
+
+ /// Which external handle types can be enabled along with the queried external handle type
+ /// when creating the fence.
+ pub compatible_handle_types: ExternalFenceHandleTypes,
+}
+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum FenceWaitError {
- /// Not enough memory to complete the wait.
+pub enum FenceError {
+ /// Not enough memory available.
OomError(OomError),
+ /// The device has been lost.
+ DeviceLost,
+
/// The specified timeout wasn't long enough.
Timeout,
- /// The device has been lost.
- DeviceLostError,
+ RequirementNotMet {
+ required_for: &'static str,
+ requires_one_of: RequiresOneOf,
+ },
+
+ /// 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: ExternalFenceHandleType,
+ },
+
+ /// 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 fence 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
+ /// fence.
+ HandleTypeNotEnabled,
+
+ /// Exporting is not supported for the provided handle type.
+ HandleTypeNotExportable {
+ handle_type: ExternalFenceHandleType,
+ },
+
+ /// The provided handle type is not a POSIX file descriptor handle.
+ HandleTypeNotFd,
+
+ /// The provided handle type is not a Win32 handle.
+ HandleTypeNotWin32,
+
+ /// The fence currently has a temporary import for a swapchain acquire operation.
+ ImportedForSwapchainAcquire,
+
+ /// The fence is currently in use by a queue.
+ InQueue,
}
-impl error::Error for FenceWaitError {
- #[inline]
- fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match *self {
- FenceWaitError::OomError(ref err) => Some(err),
+impl Error for FenceError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ Self::OomError(err) => Some(err),
_ => None,
}
}
}
-impl fmt::Display for FenceWaitError {
- #[inline]
- fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(
- fmt,
- "{}",
- match *self {
- FenceWaitError::OomError(_) => "no memory available",
- FenceWaitError::Timeout => "the timeout has been reached",
- FenceWaitError::DeviceLostError => "the device was lost",
+impl Display for FenceError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
+ match self {
+ Self::OomError(_) => write!(f, "not enough memory available"),
+ Self::DeviceLost => write!(f, "the device was lost"),
+ Self::Timeout => write!(f, "the timeout has been reached"),
+ Self::RequirementNotMet {
+ required_for,
+ requires_one_of,
+ } => write!(
+ f,
+ "a requirement was not met for: {}; requires one of: {}",
+ required_for, requires_one_of,
+ ),
+
+ 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 fence 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 fence",
+ ),
+ 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::ImportedForSwapchainAcquire => write!(
+ f,
+ "the fence currently has a temporary import for a swapchain acquire operation",
+ ),
+ Self::InQueue => write!(f, "the fence is currently in use by a queue"),
+ }
}
}
-impl From<Error> for FenceWaitError {
- #[inline]
- fn from(err: Error) -> FenceWaitError {
+impl From<VulkanError> for FenceError {
+ fn from(err: VulkanError) -> Self {
match err {
- Error::OutOfHostMemory => FenceWaitError::OomError(From::from(err)),
- Error::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)),
- Error::DeviceLost => FenceWaitError::DeviceLostError,
- _ => panic!("Unexpected error value: {}", err as i32),
+ e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => {
+ Self::OomError(e.into())
+ }
+ VulkanError::DeviceLost => Self::DeviceLost,
+ _ => panic!("unexpected error: {:?}", err),
+ }
+ }
+}
+
+impl From<OomError> for FenceError {
+ fn from(err: OomError) -> Self {
+ Self::OomError(err)
+ }
+}
+
+impl From<RequirementNotMet> for FenceError {
+ fn from(err: RequirementNotMet) -> Self {
+ Self::RequirementNotMet {
+ required_for: err.required_for,
+ requires_one_of: err.requires_one_of,
}
}
}
#[cfg(test)]
mod tests {
- use crate::sync::Fence;
- use crate::VulkanObject;
+ use crate::{
+ sync::fence::{Fence, FenceCreateInfo},
+ VulkanObject,
+ };
use std::time::Duration;
#[test]
fn fence_create() {
let (device, _) = gfx_dev_and_queue!();
- let fence = Fence::alloc(device.clone()).unwrap();
- assert!(!fence.ready().unwrap());
+ let fence = Fence::new(device, Default::default()).unwrap();
+ assert!(!fence.is_signaled().unwrap());
}
#[test]
fn fence_create_signaled() {
let (device, _) = gfx_dev_and_queue!();
- let fence = Fence::alloc_signaled(device.clone()).unwrap();
- assert!(fence.ready().unwrap());
+ let fence = Fence::new(
+ device,
+ FenceCreateInfo {
+ signaled: true,
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ assert!(fence.is_signaled().unwrap());
}
#[test]
fn fence_signaled_wait() {
let (device, _) = gfx_dev_and_queue!();
- let fence = Fence::alloc_signaled(device.clone()).unwrap();
+ let fence = Fence::new(
+ device,
+ FenceCreateInfo {
+ signaled: true,
+ ..Default::default()
+ },
+ )
+ .unwrap();
fence.wait(Some(Duration::new(0, 10))).unwrap();
}
@@ -438,9 +1658,16 @@ mod tests {
fn fence_reset() {
let (device, _) = gfx_dev_and_queue!();
- let mut fence = Fence::alloc_signaled(device.clone()).unwrap();
+ let fence = Fence::new(
+ device,
+ FenceCreateInfo {
+ signaled: true,
+ ..Default::default()
+ },
+ )
+ .unwrap();
fence.reset().unwrap();
- assert!(!fence.ready().unwrap());
+ assert!(!fence.is_signaled().unwrap());
}
#[test]
@@ -448,54 +1675,72 @@ mod tests {
let (device1, _) = gfx_dev_and_queue!();
let (device2, _) = gfx_dev_and_queue!();
- assert_should_panic!(
- "Tried to wait for multiple fences that didn't belong \
- to the same device",
- {
- let fence1 = Fence::alloc_signaled(device1.clone()).unwrap();
- let fence2 = Fence::alloc_signaled(device2.clone()).unwrap();
+ assert_should_panic!({
+ let fence1 = Fence::new(
+ device1.clone(),
+ FenceCreateInfo {
+ signaled: true,
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ let fence2 = Fence::new(
+ device2.clone(),
+ FenceCreateInfo {
+ signaled: true,
+ ..Default::default()
+ },
+ )
+ .unwrap();
- let _ = Fence::multi_wait(
- [&fence1, &fence2].iter().cloned(),
- Some(Duration::new(0, 10)),
- );
- }
- );
+ let _ = Fence::multi_wait(
+ [&fence1, &fence2].iter().cloned(),
+ Some(Duration::new(0, 10)),
+ );
+ });
}
#[test]
fn multireset_different_devices() {
- use std::iter::once;
-
let (device1, _) = gfx_dev_and_queue!();
let (device2, _) = gfx_dev_and_queue!();
- assert_should_panic!(
- "Tried to reset multiple fences that didn't belong \
- to the same device",
- {
- let mut fence1 = Fence::alloc_signaled(device1.clone()).unwrap();
- let mut fence2 = Fence::alloc_signaled(device2.clone()).unwrap();
+ assert_should_panic!({
+ let fence1 = Fence::new(
+ device1.clone(),
+ FenceCreateInfo {
+ signaled: true,
+ ..Default::default()
+ },
+ )
+ .unwrap();
+ let fence2 = Fence::new(
+ device2.clone(),
+ FenceCreateInfo {
+ signaled: true,
+ ..Default::default()
+ },
+ )
+ .unwrap();
- let _ = Fence::multi_reset(once(&mut fence1).chain(once(&mut fence2)));
- }
- );
+ let _ = Fence::multi_reset([&fence1, &fence2]);
+ });
}
#[test]
fn fence_pool() {
let (device, _) = gfx_dev_and_queue!();
- assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
+ assert_eq!(device.fence_pool().lock().len(), 0);
let fence1_internal_obj = {
let fence = Fence::from_pool(device.clone()).unwrap();
- assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
- fence.internal_object()
+ assert_eq!(device.fence_pool().lock().len(), 0);
+ fence.handle()
};
- assert_eq!(device.fence_pool().lock().unwrap().len(), 1);
+ assert_eq!(device.fence_pool().lock().len(), 1);
let fence2 = Fence::from_pool(device.clone()).unwrap();
- assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
- assert_eq!(fence2.internal_object(), fence1_internal_obj);
+ assert_eq!(device.fence_pool().lock().len(), 0);
+ assert_eq!(fence2.handle(), fence1_internal_obj);
}
}