summaryrefslogtreecommitdiff
path: root/LambdaCubeWidget.hs
blob: 0adb70c65e8ee6256675c943e3f134182c4a7c61 (plain)
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
{-# LANGUAGE LambdaCase #-}
module LambdaCubeWidget where

import Control.Monad
import Control.Concurrent
import Data.Function
import Data.Int
import GI.Gdk.Objects
import GI.Gtk.Objects (GLArea,widgetGetWindow)
import GLWidget
import LambdaCube.GL as LC
import LambdaCube.IR as LC
import LambdaCube.Gtk
import qualified Data.Aeson as JSON
import qualified Data.ByteString as SB
import System.IO.Error
import Control.Monad.Writer

data LCRealized x = LCRealized GLStorage GLRenderer x

data LCMethods x = LCMethods
    { lcRealized       :: MVar (LCRealized x)
    , lcUploadState    :: GLStorage -> IO x -- implements realize
    , lcDestroyState   :: x -> IO ()
    , lcSetUniforms    :: GLContext -> GLStorage -> x -> IO () -- implements render
    , lcPipeline       :: DynamicPipeline
    }

data DynamicPipeline = DynamicPipeline
    { dynamicPipeline :: Pipeline
    , dynamicSchema   :: PipelineSchema
    }

loadPipeline :: FilePath -> Writer PipelineSchema a -> IO (Either String DynamicPipeline)
loadPipeline fname schema = do
    pipelineDesc <- do
            maybe (Left $ "Unable to parse " ++ fname) Right . JSON.decodeStrict <$> SB.readFile fname
        `catchIOError` \e -> return $ Left (show e)
    return $ do
        p <- pipelineDesc
        Right DynamicPipeline
            { dynamicPipeline = p
            , dynamicSchema   = makeSchema schema
            }

lambdaRender :: (GLArea -> LCMethods x) -> WidgetMethods GLArea -> WidgetMethods GLArea
lambdaRender f m = m
    { glRender    = lcrender . f
    , glUnrealize = lcunrealize . f
    , glRealize   = lcrealize . f
    , glResize    = \glarea -> lcresize glarea (f glarea)
    }

tryWithMVar :: IO b -> MVar a -> (a -> IO b) -> IO b
tryWithMVar failed mvar f = do
    mr <- tryTakeMVar mvar
    maybe failed f mr

lcrender :: LCMethods x -> GLContext -> IO Bool
lcrender lc gl = do
    mr <- tryTakeMVar (lcRealized lc)
    maybe (\_ -> oops "Not realized!") (&) mr $ \realized-> do
        let LCRealized s r0 x = realized
        r <- fixupRenderTarget r0
        lcSetUniforms lc gl s x
        LC.renderFrame r
        putMVar (lcRealized lc) realized
    return True

lcunrealize :: LCMethods x -> IO ()
lcunrealize lc = do
    m <- tryTakeMVar $ lcRealized lc
    forM_ m $ \(LCRealized storage renderer x) -> do
        LC.disposeStorage storage
        LC.disposeRenderer renderer
        lcDestroyState lc x

lcrealize :: LCMethods x -> IO ()
lcrealize lc = do
    lcunrealize lc
    storage <- LC.allocStorage (dynamicSchema $ lcPipeline lc)
    x <- lcUploadState lc storage
    renderer <- LC.allocRenderer (dynamicPipeline $ lcPipeline lc)
    compat <- LC.setStorage renderer storage -- check schema compatibility
    putMVar (lcRealized lc) $ LCRealized storage renderer x

lcresize :: GLArea -> LCMethods x -> Int32 -> Int32 -> IO ()
lcresize glarea lc w h = do
    tryTakeMVar (lcRealized lc) >>= \case
        Nothing -> return ()
        Just r@(LCRealized storage _ _) -> do
            -- Plenty of options here.  I went with the last one.
            -- 1. gLContextGetWindow :: HasCallStack => GLContext -> IO (Maybe Window)
            -- 2. getGLContextWindow ::                 GLContext -> IO (Maybe Window)
            -- 3. widgetGetWindow    :: HasCallStack => GLArea    -> IO (Maybe Window)
            widgetGetWindow glarea >>= mapM_ (\win -> do
                (wd,ht) <- do wd <- windowGetWidth win
                              ht <- windowGetHeight win
                              return (fromIntegral wd,fromIntegral ht)
                LC.setScreenSize storage wd ht)
            putMVar (lcRealized lc) r