smithay/backend/renderer/sync/
mod.rs

1//! Helper for synchronizing rendering operations
2use std::{error::Error, fmt, os::unix::io::OwnedFd, sync::Arc};
3
4use downcast_rs::{impl_downcast, Downcast};
5
6#[cfg(feature = "backend_egl")]
7mod egl;
8
9/// Waiting for the fence was interrupted for an unknown reason.
10///
11/// This does not mean that the fence is signalled or not, neither that
12/// any timeout was reached. Waiting should be attempted again.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct Interrupted;
15
16impl fmt::Display for Interrupted {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        f.write_str("Wait for Fence was interrupted")
19    }
20}
21impl Error for Interrupted {}
22
23/// A fence that will be signaled in finite time
24pub trait Fence: std::fmt::Debug + Send + Sync + Downcast {
25    /// Queries the state of the fence
26    fn is_signaled(&self) -> bool;
27
28    /// Blocks the current thread until the fence is signaled
29    fn wait(&self) -> Result<(), Interrupted>;
30
31    /// Returns whether this fence can be exported
32    /// as a native fence fd
33    fn is_exportable(&self) -> bool;
34
35    /// Export this fence as a native fence fd
36    fn export(&self) -> Option<OwnedFd>;
37}
38impl_downcast!(Fence);
39
40/// A sync point the will be signaled in finite time
41#[derive(Debug, Clone)]
42#[must_use = "this `SyncPoint` may contain a fence that should be awaited, failing to do so may result in unexpected rendering artifacts"]
43pub struct SyncPoint {
44    fence: Option<Arc<dyn Fence>>,
45}
46
47impl Default for SyncPoint {
48    fn default() -> Self {
49        Self::signaled()
50    }
51}
52
53impl SyncPoint {
54    /// Create an already signaled sync point
55    pub fn signaled() -> Self {
56        Self {
57            fence: Default::default(),
58        }
59    }
60
61    /// Returns `true` if `SyncPoint` contains a [`Fence`]
62    pub fn contains_fence(&self) -> bool {
63        self.fence.is_some()
64    }
65
66    /// Get a reference to the underlying [`Fence`] if any
67    ///
68    /// Returns `None` if the sync point does not contain a fence
69    /// or contains a different type of fence
70    pub fn get<F: Fence + 'static>(&self) -> Option<&F> {
71        self.fence.as_ref().and_then(|f| f.downcast_ref())
72    }
73
74    /// Queries the state of the sync point
75    ///
76    /// Will always return `true` in case the sync point does not contain a fence
77    pub fn is_reached(&self) -> bool {
78        self.fence.as_ref().map(|f| f.is_signaled()).unwrap_or(true)
79    }
80
81    /// Blocks the current thread until the sync point is signaled
82    ///
83    /// If the sync point does not contain a fence this will never block.
84    #[profiling::function]
85    pub fn wait(&self) -> Result<(), Interrupted> {
86        if let Some(fence) = self.fence.as_ref() {
87            fence.wait()
88        } else {
89            Ok(())
90        }
91    }
92
93    /// Returns whether this sync point can be exported as a native fence fd
94    ///
95    /// Will always return `false` in case the sync point does not contain a fence
96    pub fn is_exportable(&self) -> bool {
97        self.fence.as_ref().map(|f| f.is_exportable()).unwrap_or(false)
98    }
99
100    /// Export this [`SyncPoint`] as a native fence fd
101    ///
102    /// Will always return `None` in case the sync point does not contain a fence
103    #[profiling::function]
104    pub fn export(&self) -> Option<OwnedFd> {
105        self.fence.as_ref().and_then(|f| f.export())
106    }
107}
108
109impl<T: Fence + 'static> From<T> for SyncPoint {
110    #[inline]
111    fn from(value: T) -> Self {
112        SyncPoint {
113            fence: Some(Arc::new(value)),
114        }
115    }
116}