wgpu_hal/gles/
fence.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::sync::atomic::Ordering;
3use parking_lot::RwLock;
4
5use glow::HasContext;
6
7use crate::AtomicFenceValue;
8
9#[derive(Debug)]
10struct GLFence {
11    // Since a fence can be `Copy`ed, there can exist some
12    // cases where a fence could be destroyed while something
13    // else is still using it. Therefore, while a function is
14    // using this fence (and doesn't keep pending read locked),
15    // it should clone the `Arc` to show it needs this to
16    // stay alive.
17    //
18    // The arc should not be kept after a function has finished
19    sync: Arc<glow::Fence>,
20    value: crate::FenceValue,
21}
22
23#[derive(Debug)]
24pub struct Fence {
25    last_completed: AtomicFenceValue,
26    pending: RwLock<Vec<GLFence>>,
27    fence_behavior: wgt::GlFenceBehavior,
28}
29
30impl crate::DynFence for Fence {}
31
32#[cfg(send_sync)]
33unsafe impl Send for Fence {}
34#[cfg(send_sync)]
35unsafe impl Sync for Fence {}
36
37impl Fence {
38    pub fn new(options: &wgt::GlBackendOptions) -> Self {
39        Self {
40            last_completed: AtomicFenceValue::new(0),
41            pending: RwLock::new(Vec::new()),
42            fence_behavior: options.fence_behavior,
43        }
44    }
45
46    pub fn signal(
47        &self,
48        gl: &glow::Context,
49        value: crate::FenceValue,
50    ) -> Result<(), crate::DeviceError> {
51        if self.fence_behavior.is_auto_finish() {
52            self.last_completed.store(value, Ordering::Release);
53            return Ok(());
54        }
55
56        let sync = unsafe { gl.fence_sync(glow::SYNC_GPU_COMMANDS_COMPLETE, 0) }
57            .map_err(|_| crate::DeviceError::OutOfMemory)?;
58        self.pending.write().push(GLFence {
59            sync: Arc::new(sync),
60            value,
61        });
62
63        Ok(())
64    }
65
66    pub fn satisfied(&self, value: crate::FenceValue) -> bool {
67        self.last_completed.load(Ordering::Acquire) >= value
68    }
69
70    pub fn get_latest(&self, gl: &glow::Context) -> crate::FenceValue {
71        let mut max_value = self.last_completed.load(Ordering::Acquire);
72
73        if self.fence_behavior.is_auto_finish() {
74            return max_value;
75        }
76
77        let pending = self.pending.read();
78
79        for gl_fence in pending.iter() {
80            if gl_fence.value <= max_value {
81                // We already know this was good, no need to check again
82                continue;
83            }
84            // We have pending `read` locked, so we shouldn't have to clone it.
85            let status = unsafe { gl.get_sync_status(*gl_fence.sync) };
86            if status == glow::SIGNALED {
87                max_value = gl_fence.value;
88            } else {
89                // Anything after the first unsignalled is guaranteed to also be unsignalled
90                break;
91            }
92        }
93
94        // Track the latest value, to save ourselves some querying later
95        self.last_completed.fetch_max(max_value, Ordering::AcqRel);
96
97        max_value
98    }
99
100    pub fn maintain(&self, gl: &glow::Context) {
101        if self.fence_behavior.is_auto_finish() {
102            return;
103        }
104
105        let latest = self.get_latest(gl);
106        let mut pending = self.pending.write();
107        pending.retain_mut(|gl_fence| {
108            if gl_fence.value > latest {
109                true
110            } else if let Some(fence) = Arc::get_mut(&mut gl_fence.sync) {
111                unsafe {
112                    gl.delete_sync(*fence);
113                }
114                false
115            } else {
116                // Another function is currently using this value. In general, these should finish
117                // very quickly (for wait because the fence should already be signaled, an all
118                // others are just fast), but submit should be very fast, so we shouldn't block on
119                // this.
120                true
121            }
122        });
123    }
124
125    pub fn wait(
126        &self,
127        gl: &glow::Context,
128        wait_value: crate::FenceValue,
129        timeout_ns: u32,
130    ) -> Result<bool, crate::DeviceError> {
131        let last_completed = self.last_completed.load(Ordering::Acquire);
132
133        if self.fence_behavior.is_auto_finish() {
134            return Ok(last_completed >= wait_value);
135        }
136
137        // We already know this fence has been signalled to that value. Return signalled.
138        if last_completed >= wait_value {
139            return Ok(true);
140        }
141
142        let pending = self.pending.read();
143
144        // Find a matching fence
145        let gl_fence = pending.iter().find(|gl_fence| gl_fence.value >= wait_value);
146
147        let Some(gl_fence) = gl_fence else {
148            log::warn!("Tried to wait for {wait_value} but that value has not been signalled yet");
149            return Ok(false);
150        };
151
152        // clone to show we're using the fence
153        let sync = gl_fence.sync.clone();
154        let fence_value = gl_fence.value;
155
156        drop(pending);
157
158        let status = unsafe {
159            gl.client_wait_sync(
160                *sync,
161                glow::SYNC_FLUSH_COMMANDS_BIT,
162                timeout_ns.min(i32::MAX as u32) as i32,
163            )
164        };
165
166        drop(sync);
167
168        let signalled = match status {
169            glow::ALREADY_SIGNALED | glow::CONDITION_SATISFIED => true,
170            glow::TIMEOUT_EXPIRED | glow::WAIT_FAILED => false,
171            _ => {
172                log::warn!("Unexpected result from client_wait_sync: {status}");
173                false
174            }
175        };
176
177        if signalled {
178            self.last_completed.fetch_max(fence_value, Ordering::AcqRel);
179        }
180
181        Ok(signalled)
182    }
183
184    pub fn destroy(self, gl: &glow::Context) {
185        if self.fence_behavior.is_auto_finish() {
186            return;
187        }
188
189        for gl_fence in self.pending.into_inner() {
190            unsafe {
191                gl.delete_sync(
192                    Arc::into_inner(gl_fence.sync)
193                        .expect("A function has failed to drop all its references to this"),
194                );
195            }
196        }
197    }
198}