wgpu_hal/gles/
fence.rs

1use alloc::vec::Vec;
2use core::sync::atomic::Ordering;
3
4use glow::HasContext;
5
6use crate::AtomicFenceValue;
7
8#[derive(Debug, Copy, Clone)]
9struct GLFence {
10    sync: glow::Fence,
11    value: crate::FenceValue,
12}
13
14#[derive(Debug)]
15pub struct Fence {
16    last_completed: AtomicFenceValue,
17    pending: Vec<GLFence>,
18    fence_behavior: wgt::GlFenceBehavior,
19}
20
21impl crate::DynFence for Fence {}
22
23#[cfg(send_sync)]
24unsafe impl Send for Fence {}
25#[cfg(send_sync)]
26unsafe impl Sync for Fence {}
27
28impl Fence {
29    pub fn new(options: &wgt::GlBackendOptions) -> Self {
30        Self {
31            last_completed: AtomicFenceValue::new(0),
32            pending: Vec::new(),
33            fence_behavior: options.fence_behavior,
34        }
35    }
36
37    pub fn signal(
38        &mut self,
39        gl: &glow::Context,
40        value: crate::FenceValue,
41    ) -> Result<(), crate::DeviceError> {
42        if self.fence_behavior.is_auto_finish() {
43            *self.last_completed.get_mut() = value;
44            return Ok(());
45        }
46
47        let sync = unsafe { gl.fence_sync(glow::SYNC_GPU_COMMANDS_COMPLETE, 0) }
48            .map_err(|_| crate::DeviceError::OutOfMemory)?;
49        self.pending.push(GLFence { sync, value });
50
51        Ok(())
52    }
53
54    pub fn satisfied(&self, value: crate::FenceValue) -> bool {
55        self.last_completed.load(Ordering::Acquire) >= value
56    }
57
58    pub fn get_latest(&self, gl: &glow::Context) -> crate::FenceValue {
59        let mut max_value = self.last_completed.load(Ordering::Acquire);
60
61        if self.fence_behavior.is_auto_finish() {
62            return max_value;
63        }
64
65        for gl_fence in self.pending.iter() {
66            if gl_fence.value <= max_value {
67                // We already know this was good, no need to check again
68                continue;
69            }
70            let status = unsafe { gl.get_sync_status(gl_fence.sync) };
71            if status == glow::SIGNALED {
72                max_value = gl_fence.value;
73            } else {
74                // Anything after the first unsignalled is guaranteed to also be unsignalled
75                break;
76            }
77        }
78
79        // Track the latest value, to save ourselves some querying later
80        self.last_completed.fetch_max(max_value, Ordering::AcqRel);
81
82        max_value
83    }
84
85    pub fn maintain(&mut self, gl: &glow::Context) {
86        if self.fence_behavior.is_auto_finish() {
87            return;
88        }
89
90        let latest = self.get_latest(gl);
91        for &gl_fence in self.pending.iter() {
92            if gl_fence.value <= latest {
93                unsafe {
94                    gl.delete_sync(gl_fence.sync);
95                }
96            }
97        }
98        self.pending.retain(|&gl_fence| gl_fence.value > latest);
99    }
100
101    pub fn wait(
102        &self,
103        gl: &glow::Context,
104        wait_value: crate::FenceValue,
105        timeout_ns: u64,
106    ) -> Result<bool, crate::DeviceError> {
107        let last_completed = self.last_completed.load(Ordering::Acquire);
108
109        if self.fence_behavior.is_auto_finish() {
110            return Ok(last_completed >= wait_value);
111        }
112
113        // We already know this fence has been signalled to that value. Return signalled.
114        if last_completed >= wait_value {
115            return Ok(true);
116        }
117
118        // Find a matching fence
119        let gl_fence = self
120            .pending
121            .iter()
122            // Greater or equal as an abundance of caution, but there should be one fence per value
123            .find(|gl_fence| gl_fence.value >= wait_value);
124
125        let Some(gl_fence) = gl_fence else {
126            log::warn!("Tried to wait for {wait_value} but that value has not been signalled yet");
127            return Ok(false);
128        };
129
130        // We should have found a fence with the exact value.
131        debug_assert_eq!(gl_fence.value, wait_value);
132
133        let status = unsafe {
134            gl.client_wait_sync(
135                gl_fence.sync,
136                glow::SYNC_FLUSH_COMMANDS_BIT,
137                timeout_ns as i32,
138            )
139        };
140
141        let signalled = match status {
142            glow::ALREADY_SIGNALED | glow::CONDITION_SATISFIED => true,
143            glow::TIMEOUT_EXPIRED | glow::WAIT_FAILED => false,
144            _ => {
145                log::warn!("Unexpected result from client_wait_sync: {status}");
146                false
147            }
148        };
149
150        if signalled {
151            self.last_completed.fetch_max(wait_value, Ordering::AcqRel);
152        }
153
154        Ok(signalled)
155    }
156
157    pub fn destroy(self, gl: &glow::Context) {
158        if self.fence_behavior.is_auto_finish() {
159            return;
160        }
161
162        for gl_fence in self.pending {
163            unsafe {
164                gl.delete_sync(gl_fence.sync);
165            }
166        }
167    }
168}