Skip to main content

vk_graph/driver/
fence.rs

1//! Fence types.
2
3use {
4    super::{DriverError, device::Device},
5    ash::vk,
6    log::{error, trace},
7    std::{fmt::Debug, thread::panicking},
8};
9
10/// Represents a Vulkan fence used to track queue submission completion.
11///
12/// See [`VkFence`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkFence.html).
13#[derive(Debug)]
14#[read_only::cast]
15pub struct Fence {
16    /// The device which owns this fence resource.
17    ///
18    /// _Note:_ This field is read-only.
19    #[readonly]
20    pub device: Device,
21
22    /// The native Vulkan fence handle.
23    ///
24    /// _Note:_ This field is read-only.
25    #[readonly]
26    pub handle: vk::Fence,
27
28    pub(crate) queued: bool,
29    droppables: Vec<Box<dyn Debug + Send + 'static>>,
30}
31
32impl Fence {
33    /// Creates a Vulkan fence owned by `device`.
34    ///
35    /// See [`vkCreateFence`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCreateFence.html).
36    pub fn create(device: &Device, signaled: bool) -> Result<Self, DriverError> {
37        Ok(Self {
38            device: device.clone(),
39            handle: Device::create_fence(device, signaled)?,
40            queued: signaled,
41            droppables: Vec::new(),
42        })
43    }
44
45    /// Drops an item after this fence signals.
46    pub(crate) fn drop_when_signaled(&mut self, x: impl Debug + Send + 'static) {
47        self.droppables.push(Box::new(x));
48    }
49
50    #[profiling::function]
51    fn drop_signaled(&mut self) {
52        if !self.droppables.is_empty() {
53            trace!("dropping {} shared references", self.droppables.len());
54        }
55
56        self.droppables.clear();
57    }
58
59    /// Returns `true` if this fence is signaled.
60    ///
61    /// See [`vkGetFenceStatus`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkGetFenceStatus.html).
62    #[profiling::function]
63    pub fn is_signaled(&self) -> Result<bool, DriverError> {
64        let res = unsafe { self.device.get_fence_status(self.handle) };
65
66        match res {
67            Ok(status) => Ok(status),
68            Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
69                error!("invalid device state: lost");
70
71                Err(DriverError::InvalidData)
72            }
73            Err(err) => {
74                error!("unable to get fence status: {err}");
75
76                Err(DriverError::InvalidData)
77            }
78        }
79    }
80
81    /// Returns `true` if work has been queued against this fence.
82    pub fn is_queued(&self) -> bool {
83        self.queued
84    }
85
86    /// Marks this fence as having work queued against it.
87    pub(crate) fn mark_queued(&mut self) {
88        self.queued = true;
89    }
90
91    /// Resets this fence to the unsignaled state.
92    ///
93    /// See [`vkResetFences`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkResetFences.html).
94    pub fn reset(&mut self) -> Result<&mut Self, DriverError> {
95        #[cfg(feature = "checked")]
96        if !self.queued {
97            return Ok(self);
98        }
99
100        Device::reset_fences(&self.device, std::slice::from_ref(&self.handle))?;
101        self.queued = false;
102
103        Ok(self)
104    }
105
106    /// Waits for this fence to signal, then drops any deferred payloads.
107    ///
108    /// See [`vkWaitForFences`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkWaitForFences.html).
109    #[profiling::function]
110    pub fn wait_signaled(&mut self) -> Result<&mut Self, DriverError> {
111        #[cfg(feature = "checked")]
112        if !self.queued {
113            return Ok(self);
114        }
115
116        Device::wait_for_fence(&self.device, &self.handle)?;
117        self.drop_signaled();
118
119        Ok(self)
120    }
121}
122
123impl Drop for Fence {
124    #[profiling::function]
125    fn drop(&mut self) {
126        if panicking() {
127            return;
128        }
129
130        if self.queued && self.wait_signaled().is_err() {
131            return;
132        }
133
134        unsafe {
135            self.device.destroy_fence(self.handle, None);
136        }
137    }
138}