1use crate::context::CommandContext;
2use crate::version::Api;
3use crate::version::Version;
4use crate::gl;
5
6use crate::backend::Facade;
7use crate::context::Context;
8use crate::ContextExt;
9use std::rc::Rc;
10
11use std::thread;
12
13#[derive(Copy, Clone, Debug)]
15pub struct SyncNotSupportedError;
16
17pub struct SyncFence {
34 context: Rc<Context>,
35 id: Option<gl::types::GLsync>,
36}
37
38impl SyncFence {
39 #[inline]
41 pub fn new<F: ?Sized>(facade: &F) -> Result<SyncFence, SyncNotSupportedError> where F: Facade {
42 let mut ctxt = facade.get_context().make_current();
43 unsafe { new_linear_sync_fence(&mut ctxt) }.map(|f| f.into_sync_fence(facade))
44 }
45
46 pub fn wait(mut self) {
48 let sync = self.id.take().unwrap();
49
50 let mut ctxt = self.context.make_current();
51 let result = unsafe { client_wait(&mut ctxt, sync) };
52 unsafe { delete_fence(&mut ctxt, sync) };
53
54 match result {
55 gl::ALREADY_SIGNALED | gl::CONDITION_SATISFIED => (),
56 _ => panic!("Could not wait for the fence")
57 };
58 }
59}
60
61impl Drop for SyncFence {
62 #[inline]
63 fn drop(&mut self) {
64 let sync = match self.id {
65 None => return, Some(s) => s
67 };
68
69 let mut ctxt = self.context.make_current();
70 unsafe { delete_fence(&mut ctxt, sync) };
71 }
72}
73
74#[must_use]
79pub struct LinearSyncFence {
80 id: Option<gl::types::GLsync>,
81}
82
83unsafe impl Send for LinearSyncFence {}
84
85impl LinearSyncFence {
86 #[inline]
88 pub fn into_sync_fence<F: ?Sized>(mut self, facade: &F) -> SyncFence where F: Facade {
89 SyncFence {
90 context: facade.get_context().clone(),
91 id: self.id.take()
92 }
93 }
94}
95
96impl Drop for LinearSyncFence {
97 #[inline]
98 fn drop(&mut self) {
99 if !thread::panicking() {
100 assert!(self.id.is_none());
101 }
102 }
103}
104
105pub unsafe fn new_linear_sync_fence(ctxt: &mut CommandContext<'_>)
106 -> Result<LinearSyncFence, SyncNotSupportedError>
107{
108 if ctxt.version >= &Version(Api::Gl, 3, 2) ||
109 ctxt.version >= &Version(Api::GlEs, 3, 0) || ctxt.extensions.gl_arb_sync
110 {
111 Ok(LinearSyncFence {
112 id: Some(ctxt.gl.FenceSync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0)),
113 })
114
115 } else if ctxt.extensions.gl_apple_sync {
116 Ok(LinearSyncFence {
117 id: Some(ctxt.gl.FenceSyncAPPLE(gl::SYNC_GPU_COMMANDS_COMPLETE_APPLE, 0)),
118 })
119
120 } else {
121 Err(SyncNotSupportedError)
122 }
123}
124
125#[inline]
127pub unsafe fn wait_linear_sync_fence_and_drop(mut fence: LinearSyncFence,
128 ctxt: &mut CommandContext<'_>)
129{
130 let fence = fence.id.take().unwrap();
131 client_wait(ctxt, fence);
132 delete_fence(ctxt, fence);
133}
134
135#[inline]
137pub unsafe fn destroy_linear_sync_fence(ctxt: &mut CommandContext<'_>, mut fence: LinearSyncFence) {
138 let fence = fence.id.take().unwrap();
139 delete_fence(ctxt, fence);
140}
141
142unsafe fn client_wait(ctxt: &mut CommandContext<'_>, fence: gl::types::GLsync) -> gl::types::GLenum {
151 let result = if ctxt.version >= &Version(Api::Gl, 3, 2) ||
153 ctxt.version >= &Version(Api::GlEs, 3, 0) || ctxt.extensions.gl_arb_sync
154 {
155 ctxt.gl.ClientWaitSync(fence, 0, 0)
156 } else if ctxt.extensions.gl_apple_sync {
157 ctxt.gl.ClientWaitSyncAPPLE(fence, 0, 0)
158 } else {
159 unreachable!();
160 };
161
162 match result {
163 val @ gl::ALREADY_SIGNALED | val @ gl::CONDITION_SATISFIED => return val,
164 gl::TIMEOUT_EXPIRED => (),
165 gl::WAIT_FAILED => (),
166 _ => unreachable!()
167 };
168
169 if ctxt.version >= &Version(Api::Gl, 3, 2) ||
173 ctxt.version >= &Version(Api::GlEs, 3, 0) || ctxt.extensions.gl_arb_sync
174 {
175 ctxt.gl.ClientWaitSync(fence, gl::SYNC_FLUSH_COMMANDS_BIT,
176 365 * 24 * 3600 * 1000 * 1000 * 1000)
177 } else if ctxt.extensions.gl_apple_sync {
178 ctxt.gl.ClientWaitSyncAPPLE(fence, gl::SYNC_FLUSH_COMMANDS_BIT_APPLE,
179 365 * 24 * 3600 * 1000 * 1000 * 1000)
180 } else {
181 unreachable!();
182 }
183}
184
185#[inline]
192unsafe fn delete_fence(ctxt: &mut CommandContext<'_>, fence: gl::types::GLsync) {
193 if ctxt.version >= &Version(Api::Gl, 3, 2) ||
194 ctxt.version >= &Version(Api::GlEs, 3, 0) || ctxt.extensions.gl_arb_sync
195 {
196 ctxt.gl.DeleteSync(fence);
197 } else if ctxt.extensions.gl_apple_sync {
198 ctxt.gl.DeleteSyncAPPLE(fence);
199 } else {
200 unreachable!();
201 };
202}