rendy_command/
fence.rs

1use {
2    crate::{
3        core::{device_owned, Device, DeviceId},
4        family::QueueId,
5    },
6    rendy_core::hal::{device::Device as _, Backend},
7};
8
9/// Queue epoch is the point in particluar queue timeline when fence is submitted.
10#[derive(Clone, Copy, Debug)]
11pub struct FenceEpoch {
12    /// Queue that signals fence.
13    pub queue: QueueId,
14
15    /// Queue epoch counter.
16    pub epoch: u64,
17}
18
19#[derive(Clone, Copy, Debug)]
20enum FenceState {
21    Unsignaled,
22    Signaled,
23    Submitted(FenceEpoch),
24}
25
26/// Fence wrapper.
27#[derive(Debug)]
28pub struct Fence<B: Backend> {
29    device: DeviceId,
30    raw: B::Fence,
31    state: FenceState,
32}
33
34device_owned!(Fence<B>);
35
36impl<B> Fence<B>
37where
38    B: Backend,
39{
40    /// Create new fence in signaled or unsignaled state.
41    pub fn new(
42        device: &Device<B>,
43        signaled: bool,
44    ) -> Result<Self, rendy_core::hal::device::OutOfMemory> {
45        let raw = device.raw().create_fence(false)?;
46        Ok(Fence {
47            device: device.id(),
48            raw,
49            state: if signaled {
50                FenceState::Signaled
51            } else {
52                FenceState::Unsignaled
53            },
54        })
55    }
56
57    /// Check if fence was submitted.
58    pub fn is_submitted(&self) -> bool {
59        match self.state {
60            FenceState::Submitted(_) => true,
61            _ => false,
62        }
63    }
64
65    /// Check if fence is signaled.
66    pub fn is_signaled(&self) -> bool {
67        match self.state {
68            FenceState::Signaled => true,
69            _ => false,
70        }
71    }
72
73    /// Check if fence is unsignaled.
74    /// It can be submitted as well.
75    pub fn is_unsignaled(&self) -> bool {
76        !self.is_signaled()
77    }
78
79    /// Panics if signaled or submitted.
80    /// Becomes `Submitted` after.
81    pub(crate) fn mark_submitted(&mut self, epoch: FenceEpoch) {
82        match self.state {
83            FenceState::Unsignaled => {
84                self.state = FenceState::Submitted(epoch);
85            }
86            _ => panic!("Must be Unsignaled"),
87        }
88    }
89
90    /// Reset signaled fence.
91    /// Panics if not signaled.
92    /// Becomes unsigneled.
93    pub fn reset(
94        &mut self,
95        device: &Device<B>,
96    ) -> Result<(), rendy_core::hal::device::OutOfMemory> {
97        self.assert_device_owner(device);
98        match self.state {
99            FenceState::Signaled => {
100                unsafe { device.reset_fence(&self.raw) }?;
101                self.state = FenceState::Unsignaled;
102                Ok(())
103            }
104            _ => panic!("Must be signaled"),
105        }
106    }
107
108    /// Mark signaled fence as reset.
109    /// Panics if not signaled.
110    /// Becomes unsigneled.
111    /// Fence must be reset using raw fence value.
112    pub unsafe fn mark_reset(&mut self) {
113        match self.state {
114            FenceState::Signaled => {
115                self.state = FenceState::Unsignaled;
116            }
117            _ => panic!("Must be signaled"),
118        }
119    }
120
121    /// Mark fence as signaled.
122    /// Panics if not submitted.
123    /// Fence must be checked to be signaled using raw fence value.
124    pub unsafe fn mark_signaled(&mut self) -> FenceEpoch {
125        match self.state {
126            FenceState::Submitted(epoch) => {
127                self.state = FenceState::Signaled;
128                epoch
129            }
130            _ => panic!("Must be submitted"),
131        }
132    }
133
134    /// Wait for fence to become signaled.
135    /// Panics if not submitted.
136    /// Returns submission epoch on success.
137    pub fn wait_signaled(
138        &mut self,
139        device: &Device<B>,
140        timeout_ns: u64,
141    ) -> Result<Option<FenceEpoch>, rendy_core::hal::device::OomOrDeviceLost> {
142        self.assert_device_owner(device);
143
144        match self.state {
145            FenceState::Submitted(epoch) => {
146                if unsafe { device.wait_for_fence(&self.raw, timeout_ns) }? {
147                    self.state = FenceState::Signaled;
148                    Ok(Some(epoch))
149                } else {
150                    Ok(None)
151                }
152            }
153            _ => panic!("Must be submitted"),
154        }
155    }
156
157    /// Check if fence has became signaled.
158    /// Panics if not submitted.
159    /// Returns submission epoch on success.
160    pub fn check_signaled(
161        &mut self,
162        device: &Device<B>,
163    ) -> Result<Option<FenceEpoch>, rendy_core::hal::device::DeviceLost> {
164        self.assert_device_owner(device);
165
166        match self.state {
167            FenceState::Submitted(epoch) => {
168                if unsafe { device.get_fence_status(&self.raw) }? {
169                    self.state = FenceState::Signaled;
170                    Ok(Some(epoch))
171                } else {
172                    Ok(None)
173                }
174            }
175            _ => panic!("Must be submitted"),
176        }
177    }
178
179    /// Get raw fence reference.
180    /// Use `mark_*` functions to reflect stage changes.
181    pub fn raw(&self) -> &B::Fence {
182        &self.raw
183    }
184
185    /// Get submission epoch.
186    /// Panics if not submitted.
187    pub fn epoch(&self) -> FenceEpoch {
188        match self.state {
189            FenceState::Submitted(epoch) => epoch,
190            _ => panic!("Must be submitted"),
191        }
192    }
193
194    /// Unwrap raw fence value.
195    /// Panics if submitted.
196    pub fn into_inner(self) -> B::Fence {
197        match self.state {
198            FenceState::Signaled | FenceState::Unsignaled => self.raw,
199            _ => panic!("Submitted fence must be waited upon before destroying"),
200        }
201    }
202}