glium/
sync.rs

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/// Error that happens when sync functionalities are not supported.
14#[derive(Copy, Clone, Debug)]
15pub struct SyncNotSupportedError;
16
17/// Provides a way to wait for a server-side operation to be finished.
18///
19/// Creating a `SyncFence` injects an element in the commands queue of the backend.
20/// When this element is reached, the fence becomes signaled.
21///
22/// ## Example
23///
24/// ```no_run
25/// # use glutin::surface::{ResizeableSurface, SurfaceTypeTrait};
26/// # fn example<T>(display: glium::Display<T>) where T: SurfaceTypeTrait + ResizeableSurface {
27/// # fn do_something<T>(_: &T) {}
28/// let fence = glium::SyncFence::new(&display).unwrap();
29/// do_something(&display);
30/// fence.wait();   // blocks until the previous operations have finished
31/// # }
32/// ```
33pub struct SyncFence {
34    context: Rc<Context>,
35    id: Option<gl::types::GLsync>,
36}
37
38impl SyncFence {
39    /// Builds a new `SyncFence` that is injected in the server.
40    #[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    /// Blocks until the operation has finished on the server.
47    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,     // fence has already been deleted
66            Some(s) => s
67        };
68
69        let mut ctxt = self.context.make_current();
70        unsafe { delete_fence(&mut ctxt, sync) };
71    }
72}
73
74/// Prototype for a `SyncFence`.
75///
76/// The fence must be consumed with either `into_sync_fence`, otherwise
77/// the destructor will panic.
78#[must_use]
79pub struct LinearSyncFence {
80    id: Option<gl::types::GLsync>,
81}
82
83unsafe impl Send for LinearSyncFence {}
84
85impl LinearSyncFence {
86    /// Turns the prototype into a real fence.
87    #[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/// Waits for this fence and destroys it, from within the commands context.
126#[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/// Destroys a fence, from within the commands context.
136#[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
142/// Calls `glClientWaitSync` and returns the result.
143///
144/// Tries without flushing first, then with flushing.
145///
146/// # Unsafety
147///
148/// The fence object must exist.
149///
150unsafe fn client_wait(ctxt: &mut CommandContext<'_>, fence: gl::types::GLsync) -> gl::types::GLenum {
151    // trying without flushing first
152    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    // waiting with a deadline of one year
170    // the reason why the deadline is so long is because if you attach a GL debugger,
171    // the wait can be blocked during a breaking point of the debugger
172    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/// Deletes a fence.
186///
187/// # Unsafety
188///
189/// The fence object must exist.
190///
191#[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}