aboutsummaryrefslogtreecommitdiff
path: root/Graphics/Glyph/Shaders.hs
blob: b87129cbcf35139b6a800c3d04e698fd6653aa5d (plain) (blame)
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
module Graphics.Glyph.Shaders where

import Control.Monad
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.List as List
import Data.Maybe
import Graphics.Glyph.Util
import Graphics.Rendering.OpenGL

{- Load a shader from a file giving the type of the shader
 - to load.
 - This function returns the shader log as a string and
 - a shader as a maybe. Nothing if the shader didn't complie
 - and Just if the shader did compile
 -}
class IsShaderSource a where
  loadShader :: ShaderType -> a -> IO (String, Maybe Shader)

instance IsShaderSource FilePath where
  loadShader typ path = loadShaderBS path typ =<< BS.readFile path

instance IsShaderSource BS.ByteString where
  loadShader = loadShaderBS "Inlined"

instance IsShaderSource BSL.ByteString where
  loadShader typ = loadShader typ . toStrict
    where
      toStrict = BS.concat . BSL.toChunks

noShader :: Maybe String
noShader = Nothing

loadShaderBS :: String -> ShaderType -> BS.ByteString -> IO (String, Maybe Shader)
loadShaderBS ctx typ src = do
  shader <- createShader typ
  shaderSourceBS shader $= src
  compileShader shader

  ok <- get (compileStatus shader)
  infoLog <- get (shaderInfoLog shader)

  unless ok $
    deleteObjectNames [shader]

  if not ok
    then return (unlines $ map ((ctx ++ " " ++ show typ ++ ": ") ++) $ lines infoLog, Nothing)
    else return (infoLog, Just shader)

{- Load multiple shaders -}
loadShaders :: (IsShaderSource a) => [(ShaderType, a)] -> IO [(String, Maybe Shader)]
loadShaders = mapM (uncurry loadShader)

{- Return the sucessfully complied shaders
 - as a new array of working shaders -}
workingShaders :: [(a, Maybe Shader)] -> [Shader]
workingShaders = mapMaybe snd

{- Create a program from a list of working shaders -}
createShaderProgram :: [Shader] -> IO (String, Maybe Program)
createShaderProgram shaders = do
  p <- createProgram
  mapM_ (attachShader p) shaders
  linkProgram p

  ok <- get $ linkStatus p
  info <- get $ programInfoLog p

  unless ok $
    deleteObjectNames [p]

  return (info, not ok ? Nothing $ Just p)

{- Creates a shader program, but will only build the program if all the
 - shaders compiled correctly -}
createShaderProgramSafe :: [(String, Maybe Shader)] -> IO (String, Maybe Program)
createShaderProgramSafe shaders =
  not (List.all (isJust . snd) shaders)
    ? return (concatMap fst shaders, Nothing)
    $ createShaderProgram $ workingShaders shaders

getUniformLocationsSafe :: Program -> [String] -> IO [Maybe UniformLocation]
getUniformLocationsSafe prog uniforms =
  forM uniforms $ \uniform -> do
    tmp <- get $ uniformLocation prog uniform
    case tmp of
      UniformLocation (-1) -> return $ Nothing
      _ -> return $ Just tmp

loadProgramFullSafe ::
  ( IsShaderSource tc,
    IsShaderSource te,
    IsShaderSource g,
    IsShaderSource v,
    IsShaderSource f
  ) =>
  Maybe (tc, te) ->
  Maybe g ->
  v ->
  f ->
  IO (Maybe Program)
loadProgramFullSafe tess geometry vert frag = do
  let (ts1, ts2) = distribMaybe tess
  shaders <-
    sequence $
      catMaybes
        [ Just $ loadShader VertexShader vert,
          Just $ loadShader FragmentShader frag,
          liftM (loadShader GeometryShader) geometry,
          liftM (loadShader TessControlShader) ts1,
          liftM (loadShader TessEvaluationShader) ts2
        ]
  (linklog, maybeProg) <- createShaderProgramSafe shaders
  if isNothing maybeProg
    then do
      putStrLn "Failed to link program"
      putStrLn linklog
      return Nothing
    else return maybeProg

loadProgramSafe ::
  ( IsShaderSource a,
    IsShaderSource b,
    IsShaderSource c
  ) =>
  a ->
  b ->
  Maybe c ->
  IO (Maybe Program)
loadProgramSafe vert frag geom = loadProgramFullSafe (Nothing :: Maybe (String, String)) geom vert frag