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 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 break;
76 }
77 }
78
79 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 if last_completed >= wait_value {
115 return Ok(true);
116 }
117
118 let gl_fence = self
120 .pending
121 .iter()
122 .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 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}