1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
use state::StateRef;
use gl::{self, types::*};
use std::time::Duration;

/// A fence can be used to wait until a gpu process
/// has finished
///
/// To use it create a new one and then wait on it
///
/// The msot common pattern is to wait on it on the next frame
/// or even later, for example for double or triple buffering
/// with manual sync
pub struct Fence{
    id: GLsync,
    gl: StateRef,
}

impl Drop for Fence{
    fn drop(&mut self){
        unsafe{ self.gl.DeleteSync(self.id) }
    }
}

impl Fence{
    pub(crate) fn new(gl: &StateRef) -> Fence{
        Fence{
            id: unsafe{ gl.FenceSync(gl::SYNC_GPU_COMMANDS_COMPLETE, 0) },
            gl: gl.clone(),
        }
    }

    /// Wait for all the gl calls before this fence  was created to finish
    ///
    /// Waiting on the fence consumes it so if it's stored in a struct you might need to use an
    /// option with take to replace the current fence with None before replacing it again with
    /// a new one.
    pub fn wait(self) -> ::Result<()>{
        let mut flags = 0;
        let mut duration = 0;
        loop {
            let ret = unsafe{ self.gl.ClientWaitSync(self.id, flags, duration) };
            if ret == gl::ALREADY_SIGNALED || ret == gl::CONDITION_SATISFIED {
                return Ok(())
            }

            if ret  == gl::WAIT_FAILED {
                return Err(::Error::from(::ErrorKind::WaitFailed))
            }

            flags = gl::SYNC_FLUSH_COMMANDS_BIT;
            duration = 10_000;
        }
    }

    /// Try waiting `duration` for all the gl calls before this fence  was created to finish
    pub fn try_wait(&mut self, flags: GLenum, duration: Duration) -> ::Result<()>{
        let duration = duration.as_nanos();
        let ret = unsafe{ self.gl.ClientWaitSync(self.id, flags, duration as u64) };
        if ret == gl::ALREADY_SIGNALED || ret == gl::CONDITION_SATISFIED {
            Ok(())
        }else if ret  == gl::WAIT_FAILED {
            Err(::Error::from(::ErrorKind::WaitFailed))
        }else{
            return Err(::Error::from(::ErrorKind::NotReady))
        }
    }
}