• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use crate::check_errors;
11 use crate::device::Device;
12 use crate::device::DeviceOwned;
13 use crate::Error;
14 use crate::OomError;
15 use crate::SafeDeref;
16 use crate::Success;
17 use crate::VulkanObject;
18 use smallvec::SmallVec;
19 use std::error;
20 use std::fmt;
21 use std::mem::MaybeUninit;
22 use std::ptr;
23 use std::sync::atomic::AtomicBool;
24 use std::sync::atomic::Ordering;
25 use std::sync::Arc;
26 use std::time::Duration;
27 
28 /// A fence is used to know when a command buffer submission has finished its execution.
29 ///
30 /// When a command buffer accesses a resource, you have to ensure that the CPU doesn't access
31 /// the same resource simultaneously (except for concurrent reads). Therefore in order to know
32 /// when the CPU can access a resource again, a fence has to be used.
33 #[derive(Debug)]
34 pub struct Fence<D = Arc<Device>>
35 where
36     D: SafeDeref<Target = Device>,
37 {
38     fence: ash::vk::Fence,
39 
40     device: D,
41 
42     // If true, we know that the `Fence` is signaled. If false, we don't know.
43     // This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
44     // multiple times.
45     signaled: AtomicBool,
46 
47     // Indicates whether this fence was taken from the fence pool.
48     // If true, will be put back into fence pool on drop.
49     must_put_in_pool: bool,
50 }
51 
52 impl<D> Fence<D>
53 where
54     D: SafeDeref<Target = Device>,
55 {
56     /// Takes a fence from the vulkano-provided fence pool.
57     /// If the pool is empty, a new fence will be allocated.
58     /// Upon `drop`, the fence is put back into the pool.
59     ///
60     /// For most applications, using the fence pool should be preferred,
61     /// in order to avoid creating new fences every frame.
from_pool(device: D) -> Result<Fence<D>, OomError>62     pub fn from_pool(device: D) -> Result<Fence<D>, OomError> {
63         let maybe_raw_fence = device.fence_pool().lock().unwrap().pop();
64         match maybe_raw_fence {
65             Some(raw_fence) => {
66                 unsafe {
67                     // Make sure the fence isn't signaled
68                     let fns = device.fns();
69                     check_errors(
70                         fns.v1_0
71                             .reset_fences(device.internal_object(), 1, &raw_fence),
72                     )?;
73                 }
74                 Ok(Fence {
75                     fence: raw_fence,
76                     device: device,
77                     signaled: AtomicBool::new(false),
78                     must_put_in_pool: true,
79                 })
80             }
81             None => {
82                 // Pool is empty, alloc new fence
83                 Fence::alloc_impl(device, false, true)
84             }
85         }
86     }
87 
88     /// Builds a new fence.
89     #[inline]
alloc(device: D) -> Result<Fence<D>, OomError>90     pub fn alloc(device: D) -> Result<Fence<D>, OomError> {
91         Fence::alloc_impl(device, false, false)
92     }
93 
94     /// Builds a new fence in signaled state.
95     #[inline]
alloc_signaled(device: D) -> Result<Fence<D>, OomError>96     pub fn alloc_signaled(device: D) -> Result<Fence<D>, OomError> {
97         Fence::alloc_impl(device, true, false)
98     }
99 
alloc_impl(device: D, signaled: bool, must_put_in_pool: bool) -> Result<Fence<D>, OomError>100     fn alloc_impl(device: D, signaled: bool, must_put_in_pool: bool) -> Result<Fence<D>, OomError> {
101         let fence = unsafe {
102             let infos = ash::vk::FenceCreateInfo {
103                 flags: if signaled {
104                     ash::vk::FenceCreateFlags::SIGNALED
105                 } else {
106                     ash::vk::FenceCreateFlags::empty()
107                 },
108                 ..Default::default()
109             };
110 
111             let fns = device.fns();
112             let mut output = MaybeUninit::uninit();
113             check_errors(fns.v1_0.create_fence(
114                 device.internal_object(),
115                 &infos,
116                 ptr::null(),
117                 output.as_mut_ptr(),
118             ))?;
119             output.assume_init()
120         };
121 
122         Ok(Fence {
123             fence: fence,
124             device: device,
125             signaled: AtomicBool::new(signaled),
126             must_put_in_pool: must_put_in_pool,
127         })
128     }
129 
130     /// Returns true if the fence is signaled.
131     #[inline]
ready(&self) -> Result<bool, OomError>132     pub fn ready(&self) -> Result<bool, OomError> {
133         unsafe {
134             if self.signaled.load(Ordering::Relaxed) {
135                 return Ok(true);
136             }
137 
138             let fns = self.device.fns();
139             let result = check_errors(
140                 fns.v1_0
141                     .get_fence_status(self.device.internal_object(), self.fence),
142             )?;
143             match result {
144                 Success::Success => {
145                     self.signaled.store(true, Ordering::Relaxed);
146                     Ok(true)
147                 }
148                 Success::NotReady => Ok(false),
149                 _ => unreachable!(),
150             }
151         }
152     }
153 
154     /// Waits until the fence is signaled, or at least until the timeout duration has elapsed.
155     ///
156     /// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead.
157     ///
158     /// If you pass a duration of 0, then the function will return without blocking.
wait(&self, timeout: Option<Duration>) -> Result<(), FenceWaitError>159     pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceWaitError> {
160         unsafe {
161             if self.signaled.load(Ordering::Relaxed) {
162                 return Ok(());
163             }
164 
165             let timeout_ns = if let Some(timeout) = timeout {
166                 timeout
167                     .as_secs()
168                     .saturating_mul(1_000_000_000)
169                     .saturating_add(timeout.subsec_nanos() as u64)
170             } else {
171                 u64::MAX
172             };
173 
174             let fns = self.device.fns();
175             let r = check_errors(fns.v1_0.wait_for_fences(
176                 self.device.internal_object(),
177                 1,
178                 &self.fence,
179                 ash::vk::TRUE,
180                 timeout_ns,
181             ))?;
182 
183             match r {
184                 Success::Success => {
185                     self.signaled.store(true, Ordering::Relaxed);
186                     Ok(())
187                 }
188                 Success::Timeout => Err(FenceWaitError::Timeout),
189                 _ => unreachable!(),
190             }
191         }
192     }
193 
194     /// Waits for multiple fences at once.
195     ///
196     /// # Panic
197     ///
198     /// Panics if not all fences belong to the same device.
multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceWaitError> where I: IntoIterator<Item = &'a Fence<D>>, D: 'a,199     pub fn multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceWaitError>
200     where
201         I: IntoIterator<Item = &'a Fence<D>>,
202         D: 'a,
203     {
204         let mut device: Option<&Device> = None;
205 
206         let fences: SmallVec<[ash::vk::Fence; 8]> = iter
207             .into_iter()
208             .filter_map(|fence| {
209                 match &mut device {
210                     dev @ &mut None => *dev = Some(&*fence.device),
211                     &mut Some(ref dev)
212                         if &**dev as *const Device == &*fence.device as *const Device => {}
213                     _ => panic!(
214                         "Tried to wait for multiple fences that didn't belong to the \
215                                  same device"
216                     ),
217                 };
218 
219                 if fence.signaled.load(Ordering::Relaxed) {
220                     None
221                 } else {
222                     Some(fence.fence)
223                 }
224             })
225             .collect();
226 
227         let timeout_ns = if let Some(timeout) = timeout {
228             timeout
229                 .as_secs()
230                 .saturating_mul(1_000_000_000)
231                 .saturating_add(timeout.subsec_nanos() as u64)
232         } else {
233             u64::MAX
234         };
235 
236         let r = if let Some(device) = device {
237             unsafe {
238                 let fns = device.fns();
239                 check_errors(fns.v1_0.wait_for_fences(
240                     device.internal_object(),
241                     fences.len() as u32,
242                     fences.as_ptr(),
243                     ash::vk::TRUE,
244                     timeout_ns,
245                 ))?
246             }
247         } else {
248             return Ok(());
249         };
250 
251         match r {
252             Success::Success => Ok(()),
253             Success::Timeout => Err(FenceWaitError::Timeout),
254             _ => unreachable!(),
255         }
256     }
257 
258     /// Resets the fence.
259     // This function takes a `&mut self` because the Vulkan API requires that the fence be
260     // externally synchronized.
261     #[inline]
reset(&mut self) -> Result<(), OomError>262     pub fn reset(&mut self) -> Result<(), OomError> {
263         unsafe {
264             let fns = self.device.fns();
265             check_errors(
266                 fns.v1_0
267                     .reset_fences(self.device.internal_object(), 1, &self.fence),
268             )?;
269             self.signaled.store(false, Ordering::Relaxed);
270             Ok(())
271         }
272     }
273 
274     /// Resets multiple fences at once.
275     ///
276     /// # Panic
277     ///
278     /// - Panics if not all fences belong to the same device.
279     ///
multi_reset<'a, I>(iter: I) -> Result<(), OomError> where I: IntoIterator<Item = &'a mut Fence<D>>, D: 'a,280     pub fn multi_reset<'a, I>(iter: I) -> Result<(), OomError>
281     where
282         I: IntoIterator<Item = &'a mut Fence<D>>,
283         D: 'a,
284     {
285         let mut device: Option<&Device> = None;
286 
287         let fences: SmallVec<[ash::vk::Fence; 8]> = iter
288             .into_iter()
289             .map(|fence| {
290                 match &mut device {
291                     dev @ &mut None => *dev = Some(&*fence.device),
292                     &mut Some(ref dev)
293                         if &**dev as *const Device == &*fence.device as *const Device => {}
294                     _ => panic!(
295                         "Tried to reset multiple fences that didn't belong to the same \
296                                  device"
297                     ),
298                 };
299 
300                 fence.signaled.store(false, Ordering::Relaxed);
301                 fence.fence
302             })
303             .collect();
304 
305         if let Some(device) = device {
306             unsafe {
307                 let fns = device.fns();
308                 check_errors(fns.v1_0.reset_fences(
309                     device.internal_object(),
310                     fences.len() as u32,
311                     fences.as_ptr(),
312                 ))?;
313             }
314         }
315         Ok(())
316     }
317 }
318 
319 unsafe impl DeviceOwned for Fence {
320     #[inline]
device(&self) -> &Arc<Device>321     fn device(&self) -> &Arc<Device> {
322         &self.device
323     }
324 }
325 
326 unsafe impl<D> VulkanObject for Fence<D>
327 where
328     D: SafeDeref<Target = Device>,
329 {
330     type Object = ash::vk::Fence;
331 
332     #[inline]
internal_object(&self) -> ash::vk::Fence333     fn internal_object(&self) -> ash::vk::Fence {
334         self.fence
335     }
336 }
337 
338 impl<D> Drop for Fence<D>
339 where
340     D: SafeDeref<Target = Device>,
341 {
342     #[inline]
drop(&mut self)343     fn drop(&mut self) {
344         unsafe {
345             if self.must_put_in_pool {
346                 let raw_fence = self.fence;
347                 self.device.fence_pool().lock().unwrap().push(raw_fence);
348             } else {
349                 let fns = self.device.fns();
350                 fns.v1_0
351                     .destroy_fence(self.device.internal_object(), self.fence, ptr::null());
352             }
353         }
354     }
355 }
356 
357 /// Error that can be returned when waiting on a fence.
358 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
359 pub enum FenceWaitError {
360     /// Not enough memory to complete the wait.
361     OomError(OomError),
362 
363     /// The specified timeout wasn't long enough.
364     Timeout,
365 
366     /// The device has been lost.
367     DeviceLostError,
368 }
369 
370 impl error::Error for FenceWaitError {
371     #[inline]
source(&self) -> Option<&(dyn error::Error + 'static)>372     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
373         match *self {
374             FenceWaitError::OomError(ref err) => Some(err),
375             _ => None,
376         }
377     }
378 }
379 
380 impl fmt::Display for FenceWaitError {
381     #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>382     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
383         write!(
384             fmt,
385             "{}",
386             match *self {
387                 FenceWaitError::OomError(_) => "no memory available",
388                 FenceWaitError::Timeout => "the timeout has been reached",
389                 FenceWaitError::DeviceLostError => "the device was lost",
390             }
391         )
392     }
393 }
394 
395 impl From<Error> for FenceWaitError {
396     #[inline]
from(err: Error) -> FenceWaitError397     fn from(err: Error) -> FenceWaitError {
398         match err {
399             Error::OutOfHostMemory => FenceWaitError::OomError(From::from(err)),
400             Error::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)),
401             Error::DeviceLost => FenceWaitError::DeviceLostError,
402             _ => panic!("Unexpected error value: {}", err as i32),
403         }
404     }
405 }
406 
407 #[cfg(test)]
408 mod tests {
409     use crate::sync::Fence;
410     use crate::VulkanObject;
411     use std::time::Duration;
412 
413     #[test]
fence_create()414     fn fence_create() {
415         let (device, _) = gfx_dev_and_queue!();
416 
417         let fence = Fence::alloc(device.clone()).unwrap();
418         assert!(!fence.ready().unwrap());
419     }
420 
421     #[test]
fence_create_signaled()422     fn fence_create_signaled() {
423         let (device, _) = gfx_dev_and_queue!();
424 
425         let fence = Fence::alloc_signaled(device.clone()).unwrap();
426         assert!(fence.ready().unwrap());
427     }
428 
429     #[test]
fence_signaled_wait()430     fn fence_signaled_wait() {
431         let (device, _) = gfx_dev_and_queue!();
432 
433         let fence = Fence::alloc_signaled(device.clone()).unwrap();
434         fence.wait(Some(Duration::new(0, 10))).unwrap();
435     }
436 
437     #[test]
fence_reset()438     fn fence_reset() {
439         let (device, _) = gfx_dev_and_queue!();
440 
441         let mut fence = Fence::alloc_signaled(device.clone()).unwrap();
442         fence.reset().unwrap();
443         assert!(!fence.ready().unwrap());
444     }
445 
446     #[test]
multiwait_different_devices()447     fn multiwait_different_devices() {
448         let (device1, _) = gfx_dev_and_queue!();
449         let (device2, _) = gfx_dev_and_queue!();
450 
451         assert_should_panic!(
452             "Tried to wait for multiple fences that didn't belong \
453                               to the same device",
454             {
455                 let fence1 = Fence::alloc_signaled(device1.clone()).unwrap();
456                 let fence2 = Fence::alloc_signaled(device2.clone()).unwrap();
457 
458                 let _ = Fence::multi_wait(
459                     [&fence1, &fence2].iter().cloned(),
460                     Some(Duration::new(0, 10)),
461                 );
462             }
463         );
464     }
465 
466     #[test]
multireset_different_devices()467     fn multireset_different_devices() {
468         use std::iter::once;
469 
470         let (device1, _) = gfx_dev_and_queue!();
471         let (device2, _) = gfx_dev_and_queue!();
472 
473         assert_should_panic!(
474             "Tried to reset multiple fences that didn't belong \
475                               to the same device",
476             {
477                 let mut fence1 = Fence::alloc_signaled(device1.clone()).unwrap();
478                 let mut fence2 = Fence::alloc_signaled(device2.clone()).unwrap();
479 
480                 let _ = Fence::multi_reset(once(&mut fence1).chain(once(&mut fence2)));
481             }
482         );
483     }
484 
485     #[test]
fence_pool()486     fn fence_pool() {
487         let (device, _) = gfx_dev_and_queue!();
488 
489         assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
490         let fence1_internal_obj = {
491             let fence = Fence::from_pool(device.clone()).unwrap();
492             assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
493             fence.internal_object()
494         };
495 
496         assert_eq!(device.fence_pool().lock().unwrap().len(), 1);
497         let fence2 = Fence::from_pool(device.clone()).unwrap();
498         assert_eq!(device.fence_pool().lock().unwrap().len(), 0);
499         assert_eq!(fence2.internal_object(), fence1_internal_obj);
500     }
501 }
502