Skip to content

Commit b1189f4

Browse files
committed
Implement experimental type-safe DSL for the GLSL
More of a playground for type-level stuff
1 parent 1a79ce1 commit b1189f4

20 files changed

+689
-318
lines changed

haskell-doom.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ executable haskell-doom
1919
main-is: Main.hs
2020
-- other-modules:
2121
-- other-extensions:
22-
build-depends: base >=4.8 && <4.9, GLFW-b >=1.4 && <1.5, delaunay, AC-Vector, OpenGLRaw >=3.0.0, linear, waddle, containers, transformers, mtl, case-insensitive, array, bytestring
22+
build-depends: base >=4.8 && <4.9, GLFW-b >=1.4 && <1.5, delaunay, AC-Vector, OpenGLRaw >=3.0.0, linear, waddle, containers, transformers, mtl, case-insensitive, array, bytestring, free
2323
hs-source-dirs: src
2424
default-language: Haskell2010

src/Var.hs renamed to src/Data/Var.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{-# LANGUAGE FlexibleInstances #-}
22
{-# LANGUAGE UndecidableInstances #-}
33
{-# LANGUAGE FunctionalDependencies #-}
4-
module Var where
4+
module Data.Var where
55
import Control.Monad.IO.Class
66
import Control.Monad.Reader
77
import Data.IORef

src/Game.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import Linear
1414
import Data.IORef
1515
import Enemy
1616
import Types
17-
import Var
17+
import Data.Var
1818

1919
newtype GameMonad e a = GameMonad { unGame :: ReaderT e IO a }
2020
deriving (Functor, Applicative, Monad, MonadIO, MonadReader e)
Lines changed: 53 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,51 @@
1-
{-# LANGUAGE FlexibleInstances,
2-
ScopedTypeVariables,
3-
FlexibleContexts,
4-
DataKinds,
5-
TypeFamilies,
6-
MultiParamTypeClasses,
7-
ExistentialQuantification #-}
8-
module GLUtils where
9-
import Var
10-
import Graphics.GL.Core33
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE ExistentialQuantification #-}
3+
{-# LANGUAGE FlexibleContexts #-}
4+
{-# LANGUAGE FlexibleInstances #-}
5+
{-# LANGUAGE MultiParamTypeClasses #-}
6+
{-# LANGUAGE ScopedTypeVariables #-}
7+
{-# LANGUAGE TypeFamilies #-}
8+
{-# LANGUAGE TypeOperators #-}
9+
10+
module Graphics.Binding where
11+
import Control.Monad
1112
import Control.Monad.IO.Class
12-
import Foreign.C.String
13+
import Data.Proxy
14+
import Data.Var
1315
import Foreign
16+
import Foreign.C.String
17+
import GHC.TypeLits
18+
import Graphics.GL
19+
import Graphics.Program
20+
import Graphics.Shader
1421
import Linear
15-
import Control.Monad
1622

17-
type ProgId = GLuint
23+
data Bindable (k :: [(Symbol, GLSLType)]) a = Bindable [a]
24+
25+
bindVertexData :: forall a i u m.
26+
(Storable a, GLTypeable a, TypeInfo i, MonadIO m) =>
27+
Program i u -> Bindable i a -> m ()
28+
bindVertexData (Program progId) (Bindable bdata) = liftIO $ do
29+
withArrayLen bdata $ \len vertices ->
30+
glBufferData GL_ARRAY_BUFFER
31+
(fromIntegral $ len * dataSize)
32+
(vertices :: Ptr a)
33+
GL_STATIC_DRAW
34+
foldM_ (\offset (name, size) -> do
35+
attrib <- get $ AttribLocation progId name
36+
glEnableVertexAttribArray attrib
37+
glVertexAttribPointer attrib
38+
size
39+
(glType proxy)
40+
(fromBool False)
41+
(fromIntegral $ totalSize * dataSize)
42+
offset
43+
return (offset `plusPtr` fromIntegral (size * fromIntegral dataSize))
44+
) nullPtr (map (fmap (fromIntegral . glslTypeSize)) extracted)
45+
where extracted = extract (Proxy :: Proxy i)
46+
totalSize = fromIntegral $ sum . map (glslTypeSize . snd) $ extracted
47+
dataSize = sizeOf proxy
48+
proxy = undefined :: a
1849

1950
-- Attribute location
2051
data AttribLocation a = AttribLocation ProgId String
@@ -26,37 +57,37 @@ instance (MonadIO m, Num a) => HasGetter m (AttribLocation a) a where
2657
-- Uniform binding
2758
data Uniform a = Uniform ProgId String
2859

29-
instance (MonadIO m, UniformBinding a, Storable a) => HasSetter m (Uniform a) a where
60+
instance (MonadIO m, HasBinder a) => HasSetter m (Uniform a) a where
3061
(Uniform progId name) $= uniData = liftIO $ do
3162
uniId <- withCString name $ glGetUniformLocation progId
3263
bindUniform uniData (fromIntegral uniId) 1
3364

34-
class Storable v => UniformBinding v where
65+
class Storable v => HasBinder v where
3566
bindFunc :: v -> GLint -> GLsizei -> Ptr a -> IO ()
3667
bindUniform :: MonadIO m => v -> GLint -> GLsizei -> m ()
3768
bindUniform val loc count = liftIO $
3869
with val $ \trans -> bindFunc val loc count trans
3970

40-
instance UniformBinding (M44 GLfloat) where
71+
instance HasBinder (M44 GLfloat) where
4172
bindFunc _ = matrixBinder glUniformMatrix4fv
4273

43-
instance UniformBinding (M33 GLfloat) where
74+
instance HasBinder (M33 GLfloat) where
4475
bindFunc _ = matrixBinder glUniformMatrix3fv
4576

46-
instance UniformBinding (M23 GLfloat) where
77+
instance HasBinder (M23 GLfloat) where
4778
bindFunc _ = matrixBinder glUniformMatrix2x3fv
4879
-- TODO: add other matrices
4980

50-
instance UniformBinding (V4 GLfloat) where
81+
instance HasBinder (V4 GLfloat) where
5182
bindFunc _ = vectorBinder glUniform4fv
5283

53-
instance UniformBinding (V3 GLfloat) where
84+
instance HasBinder (V3 GLfloat) where
5485
bindFunc _ = vectorBinder glUniform3fv
5586

56-
instance UniformBinding (V2 GLfloat) where
87+
instance HasBinder (V2 GLfloat) where
5788
bindFunc _ = vectorBinder glUniform2fv
5889

59-
instance UniformBinding (V1 GLfloat) where
90+
instance HasBinder (V1 GLfloat) where
6091
bindFunc _ = vectorBinder glUniform1fv
6192
-- TODO: add other vectors (GLint)
6293

@@ -72,31 +103,7 @@ vectorBinder :: MonadIO m =>
72103
vectorBinder f loc count val
73104
= liftIO $ f loc count (castPtr val)
74105

75-
-- Misc aux. functions
76-
offsetPtr :: Storable a => Int -> a -> Ptr GLvoid
77-
offsetPtr x s = plusPtr nullPtr (fromIntegral $ x * sizeOf s)
78-
79-
withNewPtr :: Storable a => (Ptr a -> IO b) -> IO a
80-
withNewPtr f = alloca (\p -> f p >> get p)
81-
82-
loadShader :: GLenum -> FilePath -> IO GLuint
83-
loadShader shaderTypeFlag filePath = do
84-
code <- readFile filePath
85-
shader <- glCreateShader shaderTypeFlag
86-
withCString code $ \codePtr ->
87-
with codePtr $ \codePtrPtr ->
88-
glShaderSource shader 1 codePtrPtr nullPtr
89-
glCompileShader shader
90-
status <- toBool <$> withNewPtr (glGetShaderiv shader GL_COMPILE_STATUS)
91-
unless status $
92-
alloca $ \err -> do
93-
glGetShaderInfoLog shader 512 nullPtr err
94-
err' <- peekCString err
95-
error err'
96-
return shader
97-
98106
-- Fragment shader
99-
100107
data FragmentShaderField
101108
= FragDiffuseColor -- 0
102109
| FragMaterialID -- 1
@@ -112,14 +119,3 @@ instance MonadIO m => HasSetter m FragShaderLocation FragmentShaderField where
112119
= liftIO . withCString name $
113120
glBindFragDataLocation progId (fromIntegral $ fromEnum loc)
114121

115-
-- GL typeable stuff
116-
class GLTypeable a where
117-
glType :: a -> Word32
118-
119-
instance GLTypeable GLfloat where
120-
glType _ = GL_FLOAT
121-
122-
instance GLTypeable GLint where
123-
glType _ = GL_INT
124-
125-
-- TODO: etc

src/Graphics/GLUtils.hs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module Graphics.GLUtils where
2+
import Data.Var
3+
import Graphics.GL
4+
import Foreign.C.String
5+
import Foreign
6+
import Control.Monad
7+
8+
-- Misc aux. functions
9+
offsetPtr :: Storable a => Int -> a -> Ptr GLvoid
10+
offsetPtr x s = plusPtr nullPtr (fromIntegral $ x * sizeOf s)
11+
12+
withNewPtr :: Storable a => (Ptr a -> IO b) -> IO a
13+
withNewPtr f = alloca (\p -> f p >> get p)
14+
15+
shaderFromString :: GLenum -> String -> IO GLuint
16+
shaderFromString shaderTypeFlag code = do
17+
shader <- glCreateShader shaderTypeFlag
18+
withCString code $ \codePtr ->
19+
with codePtr $ \codePtrPtr ->
20+
glShaderSource shader 1 codePtrPtr nullPtr
21+
glCompileShader shader
22+
status <- toBool <$> withNewPtr (glGetShaderiv shader GL_COMPILE_STATUS)
23+
unless status $
24+
alloca $ \err -> do
25+
glGetShaderInfoLog shader 512 nullPtr err
26+
err' <- peekCString err
27+
error err'
28+
return shader
29+
30+
31+
loadShader :: GLenum -> FilePath -> IO GLuint
32+
loadShader shaderTypeFlag filePath = do
33+
code <- readFile filePath
34+
shaderFromString shaderTypeFlag code
35+

src/Graphics/Program.hs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{-# LANGUAGE KindSignatures #-}
2+
{-# LANGUAGE DataKinds #-}
3+
module Graphics.Program where
4+
5+
import Graphics.Shader
6+
import GHC.TypeLits
7+
import Graphics.GL
8+
import Graphics.GLUtils
9+
10+
type ProgId = GLuint
11+
12+
data Program (inputs :: [(Symbol, GLSLType)])
13+
(uniforms :: [(Symbol, GLSLType)])
14+
= Program ProgId
15+
16+
mkProgram :: ( KnownNat ver
17+
, TypeInfo i
18+
, TypeInfo o
19+
, TypeInfo u
20+
, TypeInfo o'
21+
, TypeInfo u' ) =>
22+
Shader ver i o u a -> Shader ver o o' u' a' -> IO (Program i (Union u u'))
23+
mkProgram vert frag = do
24+
progId <- glCreateProgram
25+
vertS <- shaderFromString GL_VERTEX_SHADER (showShader vert)
26+
fragS <- shaderFromString GL_FRAGMENT_SHADER (showShader frag)
27+
glAttachShader progId vertS
28+
glAttachShader progId fragS
29+
30+
glLinkProgram progId
31+
glUseProgram progId
32+
glDeleteShader vertS
33+
glDeleteShader fragS
34+
return $ Program progId

src/Graphics/Shader.hs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE PolyKinds #-}
3+
{-# LANGUAGE FlexibleContexts #-}
4+
module Graphics.Shader
5+
( I.Shader(..)
6+
, I.showShader
7+
, T.GLSLType
8+
, T.GLTypeable(..)
9+
, T.TypeInfo(..)
10+
, I.Union
11+
, wallFrag
12+
, wallVert
13+
, spriteFrag
14+
, spriteVert
15+
, floorFrag
16+
, floorVert
17+
, glslTypeSize
18+
, Pos3
19+
, Tex2) where
20+
import Graphics.Shader.Internal as I
21+
import Graphics.Shader.Types as T
22+
import Graphics.Shader.Language as L
23+
24+
-- TODO: swizzling
25+
26+
type Pos3
27+
= '("position", 'Vec3)
28+
pos3 :: SVar Pos3
29+
pos3 = SVar
30+
31+
type Tex2
32+
= '("texcoord", 'Vec2)
33+
tex2 :: SVar Tex2
34+
tex2 = SVar
35+
36+
type Texcoord
37+
= '("Texcoord", 'Vec2)
38+
texcoord :: SVar Texcoord
39+
texcoord = SVar
40+
41+
type GlPos
42+
= '("gl_Position", 'Vec4)
43+
glPos :: SVar GlPos
44+
glPos = SVar
45+
46+
type Model
47+
= '("model", 'Mat4)
48+
model :: SVar Model
49+
model = SVar
50+
51+
type View
52+
= '("view", 'Mat4)
53+
view :: SVar View
54+
view = SVar
55+
56+
type Proj
57+
= '("proj", 'Mat4)
58+
proj :: SVar Proj
59+
proj = SVar
60+
61+
type Outcolor
62+
= '("outColor", 'Vec4)
63+
outcolor :: SVar Outcolor
64+
outcolor = SVar
65+
66+
type TexSampler
67+
= '("tex", 'Sampler2D)
68+
texSampler :: SVar TexSampler
69+
texSampler = SVar
70+
71+
wallVert :: Shader 150 '[Pos3, Tex2] '[Texcoord] '[Model, View, Proj] ()
72+
wallVert = do
73+
out texcoord =: inp tex2
74+
var glPos =: (uni proj *: uni view *: uni model *: (inp pos3 &: float 1.0))
75+
76+
wallFrag :: Shader 150 '[Texcoord] '[Outcolor] '[TexSampler] ()
77+
wallFrag
78+
= out outcolor =: texture (uni texSampler) (inp texcoord)
79+
80+
spriteVert :: Shader 150 '[Pos3, Tex2] '[Texcoord] '[] ()
81+
spriteVert = do
82+
out texcoord =: inp tex2
83+
var glPos =: inp pos3 &: float 1.0
84+
85+
-- TODO: the sprite shader shouldn't be the same as wall shader
86+
spriteFrag :: Shader 150 '[Texcoord] '[Outcolor] '[TexSampler] ()
87+
spriteFrag = wallFrag
88+
89+
floorVert :: Shader 150 '[Pos3] '[] '[Model, View, Proj] ()
90+
floorVert
91+
= var glPos =: uni proj *: uni view *: uni model *: (inp pos3 &: pure (Scalar 1))
92+
93+
floorFrag :: Shader 150 '[] '[Outcolor] '[] ()
94+
floorFrag
95+
= out outcolor =: float 0.2 &: float 0.2 &: float 0.2 &: float 1.0

0 commit comments

Comments
 (0)