module Graphics.OpenGLES.Sync where import Control.Applicative import Control.Monad import Data.IORef import Foreign import Graphics.OpenGLES.Base import Graphics.OpenGLES.Internal data Sync = Sync Word64 (IORef GLsync) -- | Obtain new 'Sync' with or without timeout in nanoseconds. -- Fence sync objects are used to wait for partial completion of the GL -- command stream, as a more flexible form of glFinish. -- -- > -- Sync objects can be used many times -- > sync1 <- getSync (Just 16000) -- > sync2 <- getSync Nothing -- -- For each frame: -- -- > glFence sync1 $ \isTimedOut -> do -- > {- modify buffers, textures, etc -} -- > glFence sync2 $ \isTimedOut -> do -- > {- modify buffers, textures, etc -} -- > endFrameGL getSync :: Maybe Word64 -> IO Sync getSync timeout_ns = Sync timeout <$> newIORef nullPtr where timeout = maybe timeoutIgnored id timeout_ns -- GL_TIMEOUT_IGNORED timeoutIgnored = 0xFFFFFFFFFFFFFFFF -- | Block and wait for GPU commands issued here complete. -- Better glFinish for /ES 3+/. -- Block and wait for a 'Sync' object to become signaled, then run specified block. glFence :: Sync -> (Bool -> GL a) -> GL a glFence sync io = do isExpired <- waitFence False sync result <- io isExpired createFence sync return result -- | Blocks on GPU until GL commands issued here complete. -- Better glFlush for /ES 3+/. Sync timeout is ignored. -- Instruct the GL server to block (on the GPU) until the previous call of -- glFence* with specified 'Sync' object becomes finished on the GL server, -- then run specified block. glFenceInGpu :: Sync -> GL a -> GL a glFenceInGpu sync io = do waitFenceAtGpu sync result <- io createFence sync return result waitFence :: Bool -> Sync -> GL Bool waitFence flushCmdQ (Sync timeout ref) = do sync <- readIORef ref if sync /= nullPtr then do result <- glClientWaitSync sync flushBit timeout glDeleteSync sync writeIORef ref nullPtr return $ case result of -- GL_ALREADY_SIGNALED 0x911A -> False -- error "glFence: AlreadySignaled" -- GL_CONDITION_SATISFIED 0x911C -> False -- GL_TIMEOUT_EXPIRED 0x911B -> True -- GL_WAIT_FAILED 0x911D -> error "glFence: WaitFailed" -- GL_INVALID_VALUE _ {-0x0501-} -> error "glFence: InvalidValue" else return False where -- GL_SYNC_FLUSH_COMMANDS_BIT flushBit = if flushCmdQ then 1 else 0 createFence :: Sync -> GL () createFence (Sync _ ref) = do -- GL_SYNC_GPU_COMMANDS_COMPLETE sync <- glFenceSync 0x9117 0 writeIORef ref sync waitFenceAtGpu :: Sync -> GL () waitFenceAtGpu (Sync _ ref) = do sync <- readIORef ref when (sync /= nullPtr) $ do result <- glWaitSync sync 0 0xFFFFFFFFFFFFFFFF glDeleteSync sync writeIORef ref nullPtr -- | Same as glFlush. This operation is expensive, so frequent use should be avoided as far as possible. glFlushCommandQ :: GL () glFlushCommandQ = glFlush -- | Same as glFinish. This operation is expensive, so frequent use should be avoided as far as possible. glWaitComplete :: GL () glWaitComplete = glFinish