diff options
author | Joe Crayne <joe@jerkface.net> | 2019-05-30 00:09:21 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2019-05-30 00:09:21 -0400 |
commit | 315d96fb6f76985ec9269a434039f3afca983d5a (patch) | |
tree | 8c681a071207f3d7cc8c08a9464eccf9c95a04f1 | |
parent | 8e735080a3f45a90f9952f1ff883260b1230fdf1 (diff) |
Factored gpu-backend out of RingBuffer, wrote vector-backend.
-rw-r--r-- | GPURing.hs | 46 | ||||
-rw-r--r-- | MeshSketch.hs | 9 | ||||
-rw-r--r-- | PointPrimitiveRing.hs | 109 | ||||
-rw-r--r-- | RingBuffer.hs | 70 | ||||
-rw-r--r-- | VectorRing.hs | 17 |
5 files changed, 138 insertions, 113 deletions
diff --git a/GPURing.hs b/GPURing.hs new file mode 100644 index 0000000..d5f46af --- /dev/null +++ b/GPURing.hs | |||
@@ -0,0 +1,46 @@ | |||
1 | module GPURing where | ||
2 | |||
3 | import Control.Monad.Writer | ||
4 | import Data.Data | ||
5 | import Data.Dependent.Sum | ||
6 | import Data.Function | ||
7 | import Data.Typeable | ||
8 | |||
9 | import AttributeData | ||
10 | import RingBuffer | ||
11 | import MaskableStream | ||
12 | |||
13 | import LambdaCube.GL as LC hiding (updateBuffer) | ||
14 | import LambdaCube.GL.Data hiding (updateBuffer) -- (uploadCubeMapToGPU,uploadTextureBufferToGPU,updateTextureBuffer) | ||
15 | import LambdaCube.GL.Mesh as LC | ||
16 | import LambdaCube.GL.Type | ||
17 | import LambdaCube.IR as LC | ||
18 | import LambdaCube.GL.Util | ||
19 | import LambdaCube.GL.Input.Type | ||
20 | import LambdaCube.GL.Input hiding (createObjectCommands) | ||
21 | |||
22 | |||
23 | -- | Typical usage: | ||
24 | -- | ||
25 | -- > ringBuffer <- newRing capacity (VectorRing.new capacity) | ||
26 | type Update keys = (keys -> Writer [DSum AttributeKey GLUniformValue] ()) | ||
27 | |||
28 | new :: Data keys => GLStorage -> (String -> String) -> Int -> IO (TargetBuffer (Update keys)) | ||
29 | new storage toAttr sz = fix $ \retProxy -> do | ||
30 | let paramProxy = paramProxy' retProxy | ||
31 | where paramProxy' :: io (targetbuffer (keys -> writer)) -> Proxy keys | ||
32 | paramProxy' _ = Proxy | ||
33 | let ps = fieldParameters paramProxy toAttr | ||
34 | putStrLn $ "Ring params: " ++ show ps | ||
35 | gd0 <- uploadDynamicBuffer sz ps | ||
36 | let gd = gd0 { dPrimitive = LineStrip } | ||
37 | Just keys <- return $ lookupAttrKeys (lookupAttributeKey gd . toAttr) | ||
38 | obj <- addToObjectArray storage "Points" [] gd | ||
39 | -- readIORef (objCommands obj) >>= mapM_ print | ||
40 | return TargetBuffer | ||
41 | { syncBuffer = \mask -> do updateCommands storage obj mask | ||
42 | return () | ||
43 | , updateBuffer = \i u -> do updateAttributes i $ u keys | ||
44 | return () | ||
45 | } | ||
46 | |||
diff --git a/MeshSketch.hs b/MeshSketch.hs index d598bbd..8660119 100644 --- a/MeshSketch.hs +++ b/MeshSketch.hs | |||
@@ -59,7 +59,8 @@ import LoadMesh | |||
59 | import InfinitePlane | 59 | import InfinitePlane |
60 | import MtlParser (ObjMaterial(..)) | 60 | import MtlParser (ObjMaterial(..)) |
61 | import Matrix | 61 | import Matrix |
62 | import PointPrimitiveRing | 62 | import GPURing as GPU |
63 | import RingBuffer | ||
63 | import MaskableStream (AttributeKey,(@<-)) | 64 | import MaskableStream (AttributeKey,(@<-)) |
64 | import SmallRing | 65 | import SmallRing |
65 | 66 | ||
@@ -103,7 +104,7 @@ data State = State | |||
103 | , stSkybox :: IORef Int | 104 | , stSkybox :: IORef Int |
104 | , stSkyTexture :: IORef TextureCubeData | 105 | , stSkyTexture :: IORef TextureCubeData |
105 | , stDragFrom :: IORef (Maybe (Vector Float,Camera)) | 106 | , stDragFrom :: IORef (Maybe (Vector Float,Camera)) |
106 | , stRingBuffer :: Ring RingPoint | 107 | , stRingBuffer :: RingBuffer (GPU.Update RingPoint) |
107 | , stPenDown :: IORef Bool | 108 | , stPenDown :: IORef Bool |
108 | , stPlane :: IORef (Maybe Plane) | 109 | , stPlane :: IORef (Maybe Plane) |
109 | , stDragPlane :: IORef (Maybe (Vector Float,Plane)) | 110 | , stDragPlane :: IORef (Maybe (Vector Float,Plane)) |
@@ -204,7 +205,7 @@ uploadState obj glarea storage = do | |||
204 | -- grid plane | 205 | -- grid plane |
205 | uploadMeshToGPU xzplane >>= addMeshToObjectArray storage "plane" [] | 206 | uploadMeshToGPU xzplane >>= addMeshToObjectArray storage "plane" [] |
206 | 207 | ||
207 | ring <- newRing storage 100 ringPointAttr | 208 | ring <- newRing 100 (GPU.new storage ringPointAttr 100) |
208 | 209 | ||
209 | -- setup FrameClock | 210 | -- setup FrameClock |
210 | w <- toWidget glarea | 211 | w <- toWidget glarea |
@@ -298,7 +299,7 @@ setUniforms gl storage st = do | |||
298 | "CameraPosition" @= return (pos :: Vector Float) | 299 | "CameraPosition" @= return (pos :: Vector Float) |
299 | "ViewProjection" @= return (mvp :: Matrix Float) | 300 | "ViewProjection" @= return (mvp :: Matrix Float) |
300 | "PlaneModel" @= return planeModel | 301 | "PlaneModel" @= return planeModel |
301 | updateRingUniforms storage (stRingBuffer st) | 302 | -- updateRingUniforms storage (stRingBuffer st) |
302 | 303 | ||
303 | data MeshSketch = MeshSketch | 304 | data MeshSketch = MeshSketch |
304 | { mmWidget :: GLArea | 305 | { mmWidget :: GLArea |
diff --git a/PointPrimitiveRing.hs b/PointPrimitiveRing.hs deleted file mode 100644 index d4fafae..0000000 --- a/PointPrimitiveRing.hs +++ /dev/null | |||
@@ -1,109 +0,0 @@ | |||
1 | {-# LANGUAGE LambdaCase, RecordWildCards, DataKinds #-} | ||
2 | module PointPrimitiveRing where | ||
3 | |||
4 | import Control.Monad | ||
5 | import Control.Monad.Writer | ||
6 | import Data.Data | ||
7 | import Data.Dependent.Sum | ||
8 | import Data.Foldable | ||
9 | import Data.Function | ||
10 | import Data.Int | ||
11 | import Data.IORef | ||
12 | import Data.Maybe | ||
13 | import Data.Some | ||
14 | import Data.Word | ||
15 | import qualified Data.Map.Strict as Map | ||
16 | import qualified Data.Vector as V | ||
17 | ;import Data.Vector as V ((!),(//)) | ||
18 | import Foreign.C.Types (CPtrdiff) | ||
19 | import Foreign.Marshal | ||
20 | import Foreign.Ptr | ||
21 | import Foreign.Storable | ||
22 | |||
23 | import LambdaCube.GL as LC | ||
24 | import LambdaCube.GL.Data -- (uploadCubeMapToGPU,uploadTextureBufferToGPU,updateTextureBuffer) | ||
25 | import LambdaCube.GL.Mesh as LC | ||
26 | import LambdaCube.GL.Type | ||
27 | import LambdaCube.IR as LC | ||
28 | import LambdaCube.GL.Util | ||
29 | import LambdaCube.GL.Input.Type | ||
30 | import LambdaCube.GL.Input hiding (createObjectCommands) | ||
31 | |||
32 | import AttributeData | ||
33 | |||
34 | -- import Graphics.GL.Core33 | ||
35 | |||
36 | import MaskableStream | ||
37 | |||
38 | data Ring keys = Ring | ||
39 | { rBufferObject :: Buffer | ||
40 | , rStorage :: GLStorage | ||
41 | , rObject :: Object | ||
42 | , rSize :: IORef Int -- Current count of vertices in the ring buffer. | ||
43 | , rBack :: IORef Int -- Where next vertex will be added. | ||
44 | , ringCapacity :: Int -- Maximum number of vertices in buffer. | ||
45 | , rKeys :: keys | ||
46 | } | ||
47 | |||
48 | newRing :: Data keys => GLStorage -> Int -> (String -> String) -> IO (Ring keys) | ||
49 | newRing storage sz toAttr = fix $ \retProxy -> do | ||
50 | let paramProxy = paramProxy' retProxy | ||
51 | where paramProxy' :: io (ring keys) -> Proxy keys | ||
52 | paramProxy' _ = Proxy | ||
53 | startRef <- newIORef 0 | ||
54 | sizeRef <- newIORef 0 | ||
55 | let ps = fieldParameters paramProxy toAttr | ||
56 | putStrLn $ "Ring params: " ++ show ps | ||
57 | gd0 <- uploadDynamicBuffer sz ps | ||
58 | let gd = gd0 { dPrimitive = LineStrip } | ||
59 | Just keys <- return $ lookupAttrKeys (lookupAttributeKey gd . toAttr) | ||
60 | obj <- addToObjectArray storage "Points" [] gd | ||
61 | readIORef (objCommands obj) >>= mapM_ print | ||
62 | let r = Ring | ||
63 | { rBufferObject = streamBuffer $ head $ Map.elems (dStreams gd) | ||
64 | , rStorage = storage | ||
65 | , rObject = obj | ||
66 | , rSize = sizeRef | ||
67 | , rBack = startRef | ||
68 | , ringCapacity = sz | ||
69 | , rKeys = keys | ||
70 | } | ||
71 | updateRingCommands r | ||
72 | return r | ||
73 | |||
74 | clearRing :: Ring keys -> IO () | ||
75 | clearRing r = do | ||
76 | writeIORef (rBack r) 0 | ||
77 | writeIORef (rSize r) 0 | ||
78 | updateRingCommands r | ||
79 | |||
80 | updateRingCommands :: Ring keys -> IO () | ||
81 | updateRingCommands r = do | ||
82 | back <- fromIntegral <$> readIORef (rBack r) | ||
83 | size <- fromIntegral <$> readIORef (rSize r) | ||
84 | let mask 0 = [] | ||
85 | mask cnt | ||
86 | | cnt==size = [(0,cnt)] | ||
87 | | otherwise = case cnt + back - size of | ||
88 | front | front > cnt -> [(front - cnt,size)] | ||
89 | | otherwise -> [(0,back), (front,cnt - front)] | ||
90 | updateCommands (rStorage r) (rObject r) mask | ||
91 | -- readIORef (objCommands $ rObject r) >>= mapM_ print | ||
92 | return () | ||
93 | |||
94 | pushBack :: Ring keys -> (keys -> Writer [DSum AttributeKey GLUniformValue] a) -> IO () | ||
95 | pushBack r attrs = do | ||
96 | back <- readIORef $ rBack r | ||
97 | writeIORef (rBack r) (mod (back + 1) (ringCapacity r)) | ||
98 | updateAttributes back $ attrs (rKeys r) | ||
99 | sz <- readIORef (rSize r) | ||
100 | when (sz < ringCapacity r) $ writeIORef (rSize r) (sz + 1) | ||
101 | updateRingCommands r | ||
102 | |||
103 | updateBack :: Ring keys -> (keys -> Writer [DSum AttributeKey GLUniformValue] a) -> IO () | ||
104 | updateBack r attrs = do | ||
105 | back <- readIORef $ rBack r | ||
106 | updateAttributes (mod (back - 1) (ringCapacity r)) $ attrs (rKeys r) | ||
107 | |||
108 | updateRingUniforms :: GLStorage -> Ring keys -> IO () | ||
109 | updateRingUniforms _ _ = return () | ||
diff --git a/RingBuffer.hs b/RingBuffer.hs new file mode 100644 index 0000000..b2779d7 --- /dev/null +++ b/RingBuffer.hs | |||
@@ -0,0 +1,70 @@ | |||
1 | module RingBuffer where | ||
2 | |||
3 | import Control.Monad | ||
4 | import Data.Int | ||
5 | import Data.IORef | ||
6 | |||
7 | data TargetBuffer u = TargetBuffer | ||
8 | { -- | Called whenever the rBack or rSize changes. The purpose is so that | ||
9 | -- some other state, such as an OpenGL vertex buffer object, can remain in | ||
10 | -- sync. | ||
11 | syncBuffer :: (Int32 -> [(Int32,Int32)]) -> IO () | ||
12 | -- | This updates the data associated with a particular index (usually | ||
13 | -- rBack). The update specification /u/ may be a value or a set of | ||
14 | -- key-value pairs or whatever is most convenient way to instruct the | ||
15 | -- target representation to update itself. | ||
16 | , updateBuffer :: Int -> u -> IO () | ||
17 | } | ||
18 | |||
19 | data RingBuffer u = RingBuffer | ||
20 | { rBack :: IORef Int | ||
21 | , rSize :: IORef Int | ||
22 | , ringCapacity :: Int | ||
23 | , rBuffer :: TargetBuffer u | ||
24 | } | ||
25 | |||
26 | newRing :: Int -> IO (TargetBuffer u) -> IO (RingBuffer u) | ||
27 | newRing sz newBuffer = do | ||
28 | backRef <- newIORef 0 | ||
29 | sizeRef <- newIORef 0 | ||
30 | b <- newBuffer | ||
31 | let r = RingBuffer | ||
32 | { rBack = backRef | ||
33 | , rSize = sizeRef | ||
34 | , ringCapacity = sz | ||
35 | , rBuffer = b | ||
36 | } | ||
37 | syncRing r | ||
38 | return r | ||
39 | |||
40 | syncRing :: RingBuffer u -> IO () | ||
41 | syncRing r = do | ||
42 | size <- fromIntegral <$> readIORef (rSize r) | ||
43 | back <- fromIntegral <$> readIORef (rBack r) | ||
44 | let mask 0 = [] | ||
45 | mask cnt | ||
46 | | cnt==size = [(0,cnt)] | ||
47 | | otherwise = case cnt + back - size of | ||
48 | front | front > cnt -> [(front - cnt,size)] | ||
49 | | otherwise -> [(0,back), (front,cnt - front)] | ||
50 | syncBuffer (rBuffer r) mask | ||
51 | |||
52 | clearRing :: RingBuffer u -> IO () | ||
53 | clearRing r = do | ||
54 | writeIORef (rBack r) 0 | ||
55 | writeIORef (rSize r) 0 | ||
56 | syncRing r | ||
57 | |||
58 | pushBack :: RingBuffer u -> u -> IO () | ||
59 | pushBack r upd = do | ||
60 | back <- readIORef $ rBack r | ||
61 | writeIORef (rBack r) (mod (back + 1) (ringCapacity r)) | ||
62 | updateBuffer (rBuffer r) back upd | ||
63 | sz <- readIORef (rSize r) | ||
64 | when (sz < ringCapacity r) $ writeIORef (rSize r) (sz + 1) | ||
65 | syncRing r | ||
66 | |||
67 | updateBack :: RingBuffer u -> u -> IO () | ||
68 | updateBack r upd = do | ||
69 | back <- readIORef $ rBack r | ||
70 | updateBuffer (rBuffer r) back upd | ||
diff --git a/VectorRing.hs b/VectorRing.hs new file mode 100644 index 0000000..2ddac72 --- /dev/null +++ b/VectorRing.hs | |||
@@ -0,0 +1,17 @@ | |||
1 | module VectorRing where | ||
2 | |||
3 | import Data.Vector.Unboxed.Mutable | ||
4 | import RingBuffer | ||
5 | |||
6 | -- | Typical usage: | ||
7 | -- | ||
8 | -- > ringBuffer <- newRing capacity (VectorRing.new capacity) | ||
9 | new :: Unbox a => Int -> IO (TargetBuffer a) | ||
10 | new sz = do | ||
11 | v <- unsafeNew sz | ||
12 | return () :: IO () | ||
13 | return TargetBuffer | ||
14 | { syncBuffer = \_ -> return () | ||
15 | , updateBuffer = \i u -> write v i u | ||
16 | } | ||
17 | |||