Skip to main content

vk_graph/driver/
fence.rs

1//! Fence types.
2
3use {
4    super::{DriverError, device::Device},
5    crate::submission::TimestampQueryPool,
6    ash::vk,
7    log::{error, trace},
8    std::{
9        cell::{Cell, RefCell},
10        fmt::Debug,
11        thread::panicking,
12    },
13};
14
15pub(crate) trait FenceDroppable: Debug + Send {
16    fn fence_signaled(&mut self, _fence: &Fence) {}
17}
18
19#[derive(Debug)]
20struct DeferredDrop<T>(T);
21
22impl<T> FenceDroppable for DeferredDrop<T> where T: Debug + Send {}
23
24/// Represents a Vulkan fence used to track queue submission completion.
25///
26/// See [`VkFence`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkFence.html).
27#[derive(Debug)]
28#[read_only::cast]
29pub struct Fence {
30    /// The device which owns this fence resource.
31    ///
32    /// _Note:_ This field is read-only.
33    #[readonly]
34    pub device: Device,
35
36    /// The native Vulkan fence handle.
37    ///
38    /// _Note:_ This field is read-only.
39    #[readonly]
40    pub handle: vk::Fence,
41
42    pub(crate) queued: Cell<bool>,
43    droppables: RefCell<Vec<Box<dyn FenceDroppable + 'static>>>,
44
45    /// Timestamp query results for queued work once this fence has signaled.
46    ///
47    /// _Note:_ This field is read-only.
48    #[readonly]
49    pub timestamps: TimestampQueryPool,
50}
51
52impl Fence {
53    /// Creates a Vulkan fence owned by `device`.
54    ///
55    /// See [`vkCreateFence`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCreateFence.html).
56    pub fn create(device: &Device, signaled: bool) -> Result<Self, DriverError> {
57        Ok(Self {
58            device: device.clone(),
59            handle: Device::create_fence(device, signaled)?,
60            queued: Cell::new(signaled),
61            droppables: Default::default(),
62            timestamps: TimestampQueryPool::empty(),
63        })
64    }
65
66    /// Drops an item after this fence signals.
67    pub(crate) fn drop_when_signaled(&self, x: impl Debug + Send + 'static) {
68        self.droppables.borrow_mut().push(Box::new(DeferredDrop(x)));
69    }
70
71    pub(crate) fn drop_fence_droppable(&self, x: impl FenceDroppable + 'static) {
72        self.droppables.borrow_mut().push(Box::new(x));
73    }
74
75    #[profiling::function]
76    fn drop_signaled(&self) {
77        let mut droppables = self.droppables.borrow_mut();
78
79        if !droppables.is_empty() {
80            trace!("dropping {} shared references", droppables.len());
81        }
82
83        for droppable in droppables.iter_mut() {
84            droppable.fence_signaled(self);
85        }
86
87        droppables.clear();
88    }
89
90    #[deprecated = "use status"]
91    #[doc(hidden)]
92    pub fn is_signaled(&self) -> Result<bool, DriverError> {
93        self.status()
94    }
95
96    pub(crate) fn set_timestamps(&mut self, timestamps: TimestampQueryPool) {
97        self.timestamps = timestamps;
98    }
99
100    /// Returns `true` if this fence is signaled.
101    ///
102    /// Signaled deferred payloads are released before this returns `Ok(true)`.
103    ///
104    /// See [`vkGetFenceStatus`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkGetFenceStatus.html).
105    #[profiling::function]
106    pub fn status(&self) -> Result<bool, DriverError> {
107        let res = unsafe { self.device.get_fence_status(self.handle) };
108
109        match res {
110            Ok(status) => {
111                if status {
112                    self.drop_signaled();
113                }
114
115                Ok(status)
116            }
117            Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
118                error!("invalid device state: lost");
119
120                Err(DriverError::InvalidData)
121            }
122            Err(err) => {
123                error!("unable to get fence status: {err}");
124
125                Err(DriverError::InvalidData)
126            }
127        }
128    }
129
130    /// Returns `true` if work has been queued against this fence.
131    pub fn is_queued(&self) -> bool {
132        self.queued.get()
133    }
134
135    /// Marks this fence as having work queued against it.
136    pub(crate) fn mark_queued(&mut self) {
137        self.queued.set(true);
138    }
139
140    /// Resets this fence to the unsignaled state.
141    ///
142    /// If queued work has already signaled, deferred payloads are released before the fence is
143    /// reset.
144    ///
145    /// See [`vkResetFences`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkResetFences.html).
146    pub fn reset(&mut self) -> Result<&mut Self, DriverError> {
147        #[cfg(feature = "checked")]
148        if !self.queued.get() {
149            return Ok(self);
150        }
151
152        if self.status()? {
153            Device::reset_fences(&self.device, std::slice::from_ref(&self.handle))?;
154        }
155
156        self.queued.set(false);
157        self.timestamps = TimestampQueryPool::empty();
158
159        Ok(self)
160    }
161
162    #[deprecated = "use wait"]
163    #[doc(hidden)]
164    pub fn wait_signaled(&mut self) -> Result<&mut Self, DriverError> {
165        self.wait()
166    }
167
168    /// Waits for this fence to signal, then releases deferred payloads.
169    ///
170    /// See [`vkWaitForFences`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkWaitForFences.html).
171    #[profiling::function]
172    pub fn wait(&mut self) -> Result<&mut Self, DriverError> {
173        #[cfg(feature = "checked")]
174        if !self.queued.get() {
175            return Ok(self);
176        }
177
178        Device::wait_for_fence(&self.device, &self.handle)?;
179        self.drop_signaled();
180
181        Ok(self)
182    }
183}
184
185impl Drop for Fence {
186    #[profiling::function]
187    fn drop(&mut self) {
188        if panicking() {
189            return;
190        }
191
192        if self.queued.get() && self.wait().is_err() {
193            return;
194        }
195
196        unsafe {
197            self.device.destroy_fence(self.handle, None);
198        }
199    }
200}