diff options
-rw-r--r-- | Backend/GL.hs | 86 | ||||
-rw-r--r-- | Backend/GL/Backend.hs | 665 | ||||
-rw-r--r-- | Backend/GL/Data.hs | 95 | ||||
-rw-r--r-- | Backend/GL/Input.hs | 381 | ||||
-rw-r--r-- | Backend/GL/Mesh.hs | 232 | ||||
-rw-r--r-- | Backend/GL/Type.hs | 530 | ||||
-rw-r--r-- | Backend/GL/Util.hs | 717 | ||||
-rw-r--r-- | LICENSE | 40 | ||||
-rw-r--r-- | Monkey.lcmesh | bin | 0 -> 371791 bytes | |||
-rw-r--r-- | SampleIR.hs | 241 | ||||
-rw-r--r-- | lambdacube-gl-ir.cabal | 86 | ||||
-rw-r--r-- | tests/sampleIR.hs | 3 |
12 files changed, 3057 insertions, 19 deletions
diff --git a/Backend/GL.hs b/Backend/GL.hs new file mode 100644 index 0000000..04d5dde --- /dev/null +++ b/Backend/GL.hs | |||
@@ -0,0 +1,86 @@ | |||
1 | module Backend.GL ( | ||
2 | -- IR | ||
3 | V2(..),V3(..),V4(..), | ||
4 | -- Array, Buffer, Texture | ||
5 | Array(..), | ||
6 | ArrayType(..), | ||
7 | Buffer, | ||
8 | BufferSetter, | ||
9 | IndexStream(..), | ||
10 | Stream(..), | ||
11 | StreamSetter, | ||
12 | StreamType(..), | ||
13 | Primitive(..), | ||
14 | SetterFun, | ||
15 | TextureData, | ||
16 | InputSetter(..), | ||
17 | fromStreamType, | ||
18 | sizeOfArrayType, | ||
19 | toStreamType, | ||
20 | compileBuffer, | ||
21 | updateBuffer, | ||
22 | bufferSize, | ||
23 | arraySize, | ||
24 | arrayType, | ||
25 | compileTexture2DRGBAF, | ||
26 | |||
27 | -- GL Pipeline Input, Object | ||
28 | GLPipeline, | ||
29 | GLPipelineInput, | ||
30 | Object, | ||
31 | PipelineSchema(..), | ||
32 | SlotSchema(..), | ||
33 | schema, | ||
34 | schemaFromPipeline, | ||
35 | allocPipeline, | ||
36 | disposePipeline, | ||
37 | setPipelineInput, | ||
38 | renderPipeline, | ||
39 | mkGLPipelineInput, | ||
40 | uniformSetter, | ||
41 | addObject, | ||
42 | removeObject, | ||
43 | enableObject, | ||
44 | setObjectOrder, | ||
45 | objectUniformSetter, | ||
46 | setScreenSize, | ||
47 | sortSlotObjects, | ||
48 | |||
49 | uniformBool, | ||
50 | uniformV2B, | ||
51 | uniformV3B, | ||
52 | uniformV4B, | ||
53 | |||
54 | uniformWord, | ||
55 | uniformV2U, | ||
56 | uniformV3U, | ||
57 | uniformV4U, | ||
58 | |||
59 | uniformInt, | ||
60 | uniformV2I, | ||
61 | uniformV3I, | ||
62 | uniformV4I, | ||
63 | |||
64 | uniformFloat, | ||
65 | uniformV2F, | ||
66 | uniformV3F, | ||
67 | uniformV4F, | ||
68 | |||
69 | uniformM22F, | ||
70 | uniformM23F, | ||
71 | uniformM24F, | ||
72 | uniformM32F, | ||
73 | uniformM33F, | ||
74 | uniformM34F, | ||
75 | uniformM42F, | ||
76 | uniformM43F, | ||
77 | uniformM44F, | ||
78 | |||
79 | uniformFTexture2D | ||
80 | ) where | ||
81 | |||
82 | import Backend.GL.Type | ||
83 | import Backend.GL.Backend | ||
84 | import Backend.GL.Data | ||
85 | import Backend.GL.Input | ||
86 | import IR \ No newline at end of file | ||
diff --git a/Backend/GL/Backend.hs b/Backend/GL/Backend.hs new file mode 100644 index 0000000..7e6a9d4 --- /dev/null +++ b/Backend/GL/Backend.hs | |||
@@ -0,0 +1,665 @@ | |||
1 | {-# LANGUAGE TupleSections, MonadComprehensions, ViewPatterns #-} | ||
2 | module Backend.GL.Backend where | ||
3 | |||
4 | import Control.Applicative | ||
5 | import Control.Monad | ||
6 | import Control.Monad.State | ||
7 | import Data.Bits | ||
8 | import Data.ByteString.Char8 (ByteString,pack) | ||
9 | import Data.IORef | ||
10 | import Data.IntMap (IntMap) | ||
11 | import Data.Maybe (isNothing) | ||
12 | import Data.Set (Set) | ||
13 | import Data.Trie as T | ||
14 | import Data.Vector (Vector,(!),(//)) | ||
15 | import qualified Data.ByteString.Char8 as SB | ||
16 | import qualified Data.Foldable as F | ||
17 | import qualified Data.IntMap as IM | ||
18 | import qualified Data.Map as Map | ||
19 | import qualified Data.List as L | ||
20 | import qualified Data.Set as S | ||
21 | import qualified Data.Vector as V | ||
22 | import qualified Data.Vector.Mutable as MV | ||
23 | |||
24 | import Graphics.Rendering.OpenGL.Raw.Core33 | ||
25 | import Foreign | ||
26 | |||
27 | -- LC IR imports | ||
28 | import IR as IR | ||
29 | |||
30 | import Backend.GL.Type | ||
31 | import Backend.GL.Util | ||
32 | |||
33 | import Backend.GL.Input | ||
34 | |||
35 | setupRasterContext :: RasterContext -> IO () | ||
36 | setupRasterContext = cvt | ||
37 | where | ||
38 | cff :: FrontFace -> GLenum | ||
39 | cff CCW = gl_CCW | ||
40 | cff CW = gl_CW | ||
41 | |||
42 | setProvokingVertex :: ProvokingVertex -> IO () | ||
43 | setProvokingVertex pv = glProvokingVertex $ case pv of | ||
44 | FirstVertex -> gl_FIRST_VERTEX_CONVENTION | ||
45 | LastVertex -> gl_LAST_VERTEX_CONVENTION | ||
46 | |||
47 | setPointSize :: PointSize -> IO () | ||
48 | setPointSize ps = case ps of | ||
49 | ProgramPointSize -> glEnable gl_PROGRAM_POINT_SIZE | ||
50 | PointSize s -> do | ||
51 | glDisable gl_PROGRAM_POINT_SIZE | ||
52 | glPointSize $ realToFrac s | ||
53 | |||
54 | cvt :: RasterContext -> IO () | ||
55 | cvt (PointCtx ps fts sc) = do | ||
56 | setPointSize ps | ||
57 | glPointParameterf gl_POINT_FADE_THRESHOLD_SIZE (realToFrac fts) | ||
58 | glPointParameterf gl_POINT_SPRITE_COORD_ORIGIN $ realToFrac $ case sc of | ||
59 | LowerLeft -> gl_LOWER_LEFT | ||
60 | UpperLeft -> gl_UPPER_LEFT | ||
61 | |||
62 | cvt (LineCtx lw pv) = do | ||
63 | glLineWidth (realToFrac lw) | ||
64 | setProvokingVertex pv | ||
65 | |||
66 | cvt (TriangleCtx cm pm po pv) = do | ||
67 | -- cull mode | ||
68 | case cm of | ||
69 | CullNone -> glDisable gl_CULL_FACE | ||
70 | CullFront f -> do | ||
71 | glEnable gl_CULL_FACE | ||
72 | glCullFace gl_FRONT | ||
73 | glFrontFace $ cff f | ||
74 | CullBack f -> do | ||
75 | glEnable gl_CULL_FACE | ||
76 | glCullFace gl_BACK | ||
77 | glFrontFace $ cff f | ||
78 | |||
79 | -- polygon mode | ||
80 | case pm of | ||
81 | PolygonPoint ps -> do | ||
82 | setPointSize ps | ||
83 | glPolygonMode gl_FRONT_AND_BACK gl_POINT | ||
84 | PolygonLine lw -> do | ||
85 | glLineWidth (realToFrac lw) | ||
86 | glPolygonMode gl_FRONT_AND_BACK gl_LINE | ||
87 | PolygonFill -> glPolygonMode gl_FRONT_AND_BACK gl_FILL | ||
88 | |||
89 | -- polygon offset | ||
90 | glDisable gl_POLYGON_OFFSET_POINT | ||
91 | glDisable gl_POLYGON_OFFSET_LINE | ||
92 | glDisable gl_POLYGON_OFFSET_FILL | ||
93 | case po of | ||
94 | NoOffset -> return () | ||
95 | Offset f u -> do | ||
96 | glPolygonOffset (realToFrac f) (realToFrac u) | ||
97 | glEnable $ case pm of | ||
98 | PolygonPoint _ -> gl_POLYGON_OFFSET_POINT | ||
99 | PolygonLine _ -> gl_POLYGON_OFFSET_LINE | ||
100 | PolygonFill -> gl_POLYGON_OFFSET_FILL | ||
101 | |||
102 | -- provoking vertex | ||
103 | setProvokingVertex pv | ||
104 | |||
105 | setupAccumulationContext :: AccumulationContext -> IO () | ||
106 | setupAccumulationContext (AccumulationContext n ops) = cvt ops | ||
107 | where | ||
108 | cvt :: [FragmentOperation] -> IO () | ||
109 | cvt (StencilOp a b c : DepthOp f m : xs) = do | ||
110 | -- TODO | ||
111 | cvtC 0 xs | ||
112 | cvt (StencilOp a b c : xs) = do | ||
113 | -- TODO | ||
114 | cvtC 0 xs | ||
115 | cvt (DepthOp df dm : xs) = do | ||
116 | -- TODO | ||
117 | glDisable gl_STENCIL_TEST | ||
118 | case df == Always && dm == False of | ||
119 | True -> glDisable gl_DEPTH_TEST | ||
120 | False -> do | ||
121 | glEnable gl_DEPTH_TEST | ||
122 | glDepthFunc $! comparisonFunctionToGLType df | ||
123 | glDepthMask (cvtBool dm) | ||
124 | cvtC 0 xs | ||
125 | cvt xs = do | ||
126 | glDisable gl_DEPTH_TEST | ||
127 | glDisable gl_STENCIL_TEST | ||
128 | cvtC 0 xs | ||
129 | |||
130 | cvtC :: Int -> [FragmentOperation] -> IO () | ||
131 | cvtC i (ColorOp b m : xs) = do | ||
132 | -- TODO | ||
133 | case b of | ||
134 | NoBlending -> do | ||
135 | -- FIXME: requires GL 3.1 | ||
136 | --glDisablei gl_BLEND $ fromIntegral gl_DRAW_BUFFER0 + fromIntegral i | ||
137 | glDisable gl_BLEND -- workaround | ||
138 | glDisable gl_COLOR_LOGIC_OP | ||
139 | BlendLogicOp op -> do | ||
140 | glDisable gl_BLEND | ||
141 | glEnable gl_COLOR_LOGIC_OP | ||
142 | glLogicOp $ logicOperationToGLType op | ||
143 | Blend (cEq,aEq) ((scF,dcF),(saF,daF)) (V4 r g b a) -> do | ||
144 | glDisable gl_COLOR_LOGIC_OP | ||
145 | -- FIXME: requires GL 3.1 | ||
146 | --glEnablei gl_BLEND $ fromIntegral gl_DRAW_BUFFER0 + fromIntegral i | ||
147 | glEnable gl_BLEND -- workaround | ||
148 | glBlendEquationSeparate (blendEquationToGLType cEq) (blendEquationToGLType aEq) | ||
149 | glBlendFuncSeparate (blendingFactorToGLType scF) (blendingFactorToGLType dcF) | ||
150 | (blendingFactorToGLType saF) (blendingFactorToGLType daF) | ||
151 | glBlendColor (realToFrac r) (realToFrac g) (realToFrac b) (realToFrac a) | ||
152 | let cvt True = 1 | ||
153 | cvt False = 0 | ||
154 | (mr,mg,mb,ma) = case m of | ||
155 | VBool r -> (cvt r, 1, 1, 1) | ||
156 | VV2B (V2 r g) -> (cvt r, cvt g, 1, 1) | ||
157 | VV3B (V3 r g b) -> (cvt r, cvt g, cvt b, 1) | ||
158 | VV4B (V4 r g b a) -> (cvt r, cvt g, cvt b, cvt a) | ||
159 | _ -> (1,1,1,1) | ||
160 | glColorMask mr mg mb ma | ||
161 | cvtC (i + 1) xs | ||
162 | cvtC _ [] = return () | ||
163 | |||
164 | cvtBool :: Bool -> GLboolean | ||
165 | cvtBool True = 1 | ||
166 | cvtBool False = 0 | ||
167 | |||
168 | clearRenderTarget :: [(ImageSemantic,Value)] -> IO () | ||
169 | clearRenderTarget values = do | ||
170 | let setClearValue (m,i) value = case value of | ||
171 | (Depth, VFloat v) -> do | ||
172 | glDepthMask 1 | ||
173 | glClearDepth $ realToFrac v | ||
174 | return (m .|. gl_DEPTH_BUFFER_BIT, i) | ||
175 | (Stencil, VWord v) -> do | ||
176 | glClearStencil $ fromIntegral v | ||
177 | return (m .|. gl_STENCIL_BUFFER_BIT, i) | ||
178 | (Color, c) -> do | ||
179 | let (r,g,b,a) = case c of | ||
180 | VFloat r -> (realToFrac r, 0, 0, 1) | ||
181 | VV2F (V2 r g) -> (realToFrac r, realToFrac g, 0, 1) | ||
182 | VV3F (V3 r g b) -> (realToFrac r, realToFrac g, realToFrac b, 1) | ||
183 | VV4F (V4 r g b a) -> (realToFrac r, realToFrac g, realToFrac b, realToFrac a) | ||
184 | _ -> (0,0,0,1) | ||
185 | glColorMask 1 1 1 1 | ||
186 | glClearColor r g b a | ||
187 | return (m .|. gl_COLOR_BUFFER_BIT, i+1) | ||
188 | _ -> error "internal error (clearRenderTarget)" | ||
189 | (mask,_) <- foldM setClearValue (0,0) values | ||
190 | glClear $ fromIntegral mask | ||
191 | |||
192 | |||
193 | printGLStatus = checkGL >>= print | ||
194 | printFBOStatus = checkFBO >>= print | ||
195 | |||
196 | compileProgram :: Trie InputType -> Program -> IO GLProgram | ||
197 | compileProgram uniTrie p = do | ||
198 | po <- glCreateProgram | ||
199 | putStrLn $ "compile program: " ++ show po | ||
200 | let createAndAttach src t = do | ||
201 | o <- glCreateShader t | ||
202 | compileShader o $ map pack [src] | ||
203 | glAttachShader po o | ||
204 | putStr " + compile shader source: " >> printGLStatus | ||
205 | return o | ||
206 | |||
207 | objs <- sequence $ createAndAttach (vertexShader p) gl_VERTEX_SHADER : createAndAttach (fragmentShader p) gl_FRAGMENT_SHADER : case geometryShader p of | ||
208 | Nothing -> [] | ||
209 | Just s -> [createAndAttach s gl_GEOMETRY_SHADER] | ||
210 | |||
211 | forM_ (zip (programOutput p) [0..]) $ \((pack -> n,t),i) -> SB.useAsCString n $ \pn -> do | ||
212 | putStrLn ("variable " ++ show n ++ " attached to color number #" ++ show i) | ||
213 | glBindFragDataLocation po i $ castPtr pn | ||
214 | putStr " + setup shader output mapping: " >> printGLStatus | ||
215 | |||
216 | glLinkProgram po | ||
217 | printProgramLog po | ||
218 | |||
219 | -- check link status | ||
220 | status <- glGetProgramiv1 gl_LINK_STATUS po | ||
221 | when (status /= fromIntegral gl_TRUE) $ fail "link program failed!" | ||
222 | |||
223 | -- check program input | ||
224 | (uniforms,uniformsType) <- queryUniforms po | ||
225 | (attributes,attributesType) <- queryStreams po | ||
226 | print uniforms | ||
227 | print attributes | ||
228 | when (uniformsType /= (toTrie $ programUniforms p) `unionL` (toTrie $ programInTextures p)) $ fail "shader program uniform input mismatch!" | ||
229 | when (attributesType /= fmap snd (toTrie $ programStreams p)) $ fail $ "shader program stream input mismatch! " ++ show (attributesType,fmap snd (toTrie $ programStreams p)) | ||
230 | -- the public (user) pipeline and program input is encoded by the slots, therefore the programs does not distinct the render and slot textures input | ||
231 | let inUniNames = toTrie $ programUniforms p | ||
232 | (inUniforms,inTextures) = L.partition (\(n,v) -> T.member n inUniNames) $ T.toList $ uniforms | ||
233 | texUnis = [n | (n,_) <- inTextures, T.member n uniTrie] | ||
234 | return $ GLProgram | ||
235 | { shaderObjects = objs | ||
236 | , programObject = po | ||
237 | , inputUniforms = T.fromList inUniforms | ||
238 | , inputTextures = T.fromList inTextures | ||
239 | , inputTextureUniforms = S.fromList $ texUnis | ||
240 | , inputStreams = T.fromList [(n,(idx,pack attrName)) | ((n,idx),(_,(attrName,_))) <- zip (T.toList $ attributes) (T.toList $ toTrie $ programStreams p)] | ||
241 | } | ||
242 | |||
243 | compileSampler :: SamplerDescriptor -> IO GLSampler | ||
244 | compileSampler s = return $ GLSampler {} | ||
245 | |||
246 | {- | ||
247 | data ImageIndex | ||
248 | = TextureImage TextureName Int (Maybe Int) -- Texture name, mip index, array index | ||
249 | | Framebuffer ImageSemantic | ||
250 | |||
251 | data ImageSemantic | ||
252 | = Depth | ||
253 | | Stencil | ||
254 | | Color | ||
255 | -} | ||
256 | {- | ||
257 | = RenderTarget | ||
258 | { renderTargets :: [(ImageSemantic,Maybe ImageIndex)] -- render texture or default framebuffer (semantic, render texture for the program output) | ||
259 | } | ||
260 | -} | ||
261 | {- | ||
262 | glDrawBuffers | ||
263 | GL_NONE | ||
264 | --GL_FRONT_LEFT | ||
265 | --GL_FRONT_RIGHT | ||
266 | GL_BACK_LEFT | ||
267 | --GL_BACK_RIGHT | ||
268 | GL_COLOR_ATTACHMENTn | ||
269 | -} | ||
270 | compileRenderTarget :: Vector TextureDescriptor -> Vector GLTexture -> RenderTarget -> IO GLRenderTarget | ||
271 | compileRenderTarget texs glTexs (RenderTarget targets) = do | ||
272 | let isFB (Framebuffer _) = True | ||
273 | isFB _ = False | ||
274 | images = [img | (_,Just img) <- targets] | ||
275 | case all isFB images of | ||
276 | True -> do | ||
277 | let bufs = [cvt img | (Color,img) <- targets] | ||
278 | cvt a = case a of | ||
279 | Nothing -> gl_NONE | ||
280 | Just (Framebuffer Color) -> gl_BACK_LEFT | ||
281 | _ -> error "internal error (compileRenderTarget)!" | ||
282 | return $ GLRenderTarget | ||
283 | { framebufferObject = 0 | ||
284 | , framebufferDrawbuffers = Just bufs | ||
285 | } | ||
286 | False -> do | ||
287 | when (any isFB images) $ fail "internal error (compileRenderTarget)!" | ||
288 | fbo <- alloca $! \pbo -> glGenFramebuffers 1 pbo >> peek pbo | ||
289 | glBindFramebuffer gl_DRAW_FRAMEBUFFER fbo | ||
290 | {- | ||
291 | void glFramebufferTexture1D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); | ||
292 | GL_TEXTURE_1D | ||
293 | void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); | ||
294 | GL_TEXTURE_2D | ||
295 | GL_TEXTURE_RECTANGLE | ||
296 | GL_TEXTURE_CUBE_MAP_POSITIVE_X | ||
297 | GL_TEXTURE_CUBE_MAP_POSITIVE_Y | ||
298 | GL_TEXTURE_CUBE_MAP_POSITIVE_Z | ||
299 | GL_TEXTURE_CUBE_MAP_NEGATIVE_X | ||
300 | GL_TEXTURE_CUBE_MAP_NEGATIVE_Y | ||
301 | GL_TEXTURE_CUBE_MAP_NEGATIVE_Z | ||
302 | GL_TEXTURE_2D_MULTISAMPLE | ||
303 | void glFramebufferTextureLayer(GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); | ||
304 | void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); | ||
305 | void glFramebufferTexture(GLenum target, GLenum attachment, GLuint texture, GLint level); | ||
306 | -} | ||
307 | let attach attachment (TextureImage texIdx level (Just layer)) = | ||
308 | glFramebufferTextureLayer gl_DRAW_FRAMEBUFFER attachment (glTextureTarget $ glTexs ! texIdx) (fromIntegral level) (fromIntegral layer) | ||
309 | attach attachment (TextureImage texIdx level Nothing) = do | ||
310 | let glTex = glTexs ! texIdx | ||
311 | tex = texs ! texIdx | ||
312 | txLevel = fromIntegral level | ||
313 | txTarget = glTextureTarget glTex | ||
314 | txObj = glTextureObject glTex | ||
315 | attachArray = glFramebufferTexture gl_DRAW_FRAMEBUFFER attachment txObj txLevel | ||
316 | attach2D = glFramebufferTexture2D gl_DRAW_FRAMEBUFFER attachment txTarget txObj txLevel | ||
317 | case textureType tex of | ||
318 | Texture1D _ n | ||
319 | | n > 1 -> attachArray | ||
320 | | otherwise -> glFramebufferTexture1D gl_DRAW_FRAMEBUFFER attachment txTarget txObj txLevel | ||
321 | Texture2D _ n | ||
322 | | n > 1 -> attachArray | ||
323 | | otherwise -> attach2D | ||
324 | Texture3D _ -> attachArray | ||
325 | TextureCube _ -> attachArray | ||
326 | TextureRect _ -> attach2D | ||
327 | Texture2DMS _ n _ _ | ||
328 | | n > 1 -> attachArray | ||
329 | | otherwise -> attach2D | ||
330 | TextureBuffer _ -> fail "internalError (compileRenderTarget/TextureBuffer)!" | ||
331 | |||
332 | go a (Stencil,Just img) = do | ||
333 | fail "Stencil support is not implemented yet!" | ||
334 | return a | ||
335 | go a (Depth,Just img) = do | ||
336 | attach gl_DEPTH_ATTACHMENT img | ||
337 | return a | ||
338 | go (bufs,colorIdx) (Color,Just img) = do | ||
339 | let attachment = gl_COLOR_ATTACHMENT0 + fromIntegral colorIdx | ||
340 | attach attachment img | ||
341 | return (attachment : bufs, colorIdx + 1) | ||
342 | go (bufs,colorIdx) (Color,Nothing) = return (gl_NONE : bufs, colorIdx + 1) | ||
343 | go a _ = return a | ||
344 | (bufs,_) <- foldM go ([],0) targets | ||
345 | withArray (reverse bufs) $ glDrawBuffers (fromIntegral $ length bufs) | ||
346 | return $ GLRenderTarget | ||
347 | { framebufferObject = fbo | ||
348 | , framebufferDrawbuffers = Nothing | ||
349 | } | ||
350 | |||
351 | allocPipeline :: Pipeline -> IO GLPipeline | ||
352 | allocPipeline p = do | ||
353 | let uniTrie = uniforms $ schemaFromPipeline p | ||
354 | smps <- V.mapM compileSampler $ samplers p | ||
355 | texs <- V.mapM compileTexture $ textures p | ||
356 | trgs <- V.mapM (compileRenderTarget (textures p) texs) $ targets p | ||
357 | prgs <- V.mapM (compileProgram uniTrie) $ programs p | ||
358 | -- texture unit mapping ioref trie | ||
359 | texUnitMapRefs <- T.fromList <$> mapM (\k -> (k,) <$> newIORef 0) (S.toList $ S.fromList $ concat $ V.toList $ V.map (T.keys . toTrie . programInTextures) $ programs p) | ||
360 | let (cmds,st) = runState (mapM (compileCommand texUnitMapRefs smps texs trgs prgs) $ commands p) initCGState | ||
361 | input <- newIORef Nothing | ||
362 | -- default Vertex Array Object | ||
363 | vao <- alloca $! \pvao -> glGenVertexArrays 1 pvao >> peek pvao | ||
364 | return $ GLPipeline | ||
365 | { glPrograms = prgs | ||
366 | , glTextures = texs | ||
367 | , glSamplers = smps | ||
368 | , glTargets = trgs | ||
369 | , glCommands = cmds | ||
370 | , glSlotPrograms = V.map slotPrograms $ IR.slots p | ||
371 | , glInput = input | ||
372 | , glSlotNames = V.map (pack . slotName) $ IR.slots p | ||
373 | , glVAO = vao | ||
374 | , glTexUnitMapping = texUnitMapRefs | ||
375 | } | ||
376 | |||
377 | disposePipeline :: GLPipeline -> IO () | ||
378 | disposePipeline p = do | ||
379 | setPipelineInput p Nothing | ||
380 | V.forM_ (glPrograms p) $ \prg -> do | ||
381 | glDeleteProgram $ programObject prg | ||
382 | mapM_ glDeleteShader $ shaderObjects prg | ||
383 | let targets = glTargets p | ||
384 | withArray (map framebufferObject $ V.toList targets) $ (glDeleteFramebuffers $ fromIntegral $ V.length targets) | ||
385 | let textures = glTextures p | ||
386 | withArray (map glTextureObject $ V.toList textures) $ (glDeleteTextures $ fromIntegral $ V.length textures) | ||
387 | with (glVAO p) $ (glDeleteVertexArrays 1) | ||
388 | |||
389 | {- | ||
390 | data SlotSchema | ||
391 | = SlotSchema | ||
392 | { primitive :: FetchPrimitive | ||
393 | , attributes :: Trie StreamType | ||
394 | } | ||
395 | deriving Show | ||
396 | |||
397 | data PipelineSchema | ||
398 | = PipelineSchema | ||
399 | { slots :: Trie SlotSchema | ||
400 | , uniforms :: Trie InputType | ||
401 | } | ||
402 | deriving Show | ||
403 | -} | ||
404 | isSubTrie :: (a -> a -> Bool) -> Trie a -> Trie a -> Bool | ||
405 | isSubTrie eqFun universe subset = and [isMember a (T.lookup n universe) | (n,a) <- T.toList subset] | ||
406 | where | ||
407 | isMember a Nothing = False | ||
408 | isMember a (Just b) = eqFun a b | ||
409 | |||
410 | -- TODO: if there is a mismatch thow detailed error message in the excoeption, containing the missing attributes and uniforms | ||
411 | {- | ||
412 | let sch = schema input | ||
413 | forM_ uniformNames $ \n -> case T.lookup n (uniforms sch) of | ||
414 | Nothing -> throw $ userError $ "Unknown uniform: " ++ show n | ||
415 | _ -> return () | ||
416 | case T.lookup slotName (slots sch) of | ||
417 | Nothing -> throw $ userError $ "Unknown slot: " ++ show slotName | ||
418 | Just (SlotSchema sPrim sAttrs) -> do | ||
419 | when (sPrim /= (primitiveToFetchPrimitive prim)) $ throw $ userError $ | ||
420 | "Primitive mismatch for slot (" ++ show slotName ++ ") expected " ++ show sPrim ++ " but got " ++ show prim | ||
421 | let sType = fmap streamToStreamType attribs | ||
422 | when (sType /= sAttrs) $ throw $ userError $ unlines $ | ||
423 | [ "Attribute stream mismatch for slot (" ++ show slotName ++ ") expected " | ||
424 | , show sAttrs | ||
425 | , " but got " | ||
426 | , show sType | ||
427 | ] | ||
428 | -} | ||
429 | setPipelineInput :: GLPipeline -> Maybe GLPipelineInput -> IO () | ||
430 | setPipelineInput p input' = do | ||
431 | -- TODO: check matching input schema | ||
432 | {- | ||
433 | case input' of | ||
434 | Nothing -> return () | ||
435 | Just input -> schemaFromPipeline p | ||
436 | -} | ||
437 | {- | ||
438 | deletion: | ||
439 | - remove pipeline's object commands from used slots | ||
440 | - remove pipeline from attached pipelines vector | ||
441 | -} | ||
442 | ic' <- readIORef $ glInput p | ||
443 | case ic' of | ||
444 | Nothing -> return () | ||
445 | Just ic -> do | ||
446 | let idx = icId ic | ||
447 | oldInput = icInput ic | ||
448 | slotMask = icSlotMapPipelineToInput ic | ||
449 | slotRefs = slotVector oldInput | ||
450 | modifyIORef (pipelines oldInput) $ \v -> v // [(idx,Nothing)] | ||
451 | V.forM_ slotMask $ \slotIdx -> do | ||
452 | slot <- readIORef (slotRefs ! slotIdx) | ||
453 | forM_ (IM.elems $ objectMap slot) $ \obj -> do | ||
454 | modifyIORef (objCommands obj) $ \v -> v // [(idx,V.empty)] | ||
455 | {- | ||
456 | addition: | ||
457 | - get an id from pipeline input | ||
458 | - add to attached pipelines | ||
459 | - generate slot mappings | ||
460 | - update used slots, and generate object commands for objects in the related slots | ||
461 | -} | ||
462 | case input' of | ||
463 | Nothing -> writeIORef (glInput p) Nothing | ||
464 | Just input -> do | ||
465 | let pipelinesRef = pipelines input | ||
466 | oldPipelineV <- readIORef pipelinesRef | ||
467 | (idx,shouldExtend) <- case V.findIndex isNothing oldPipelineV of | ||
468 | Nothing -> do | ||
469 | -- we don't have empty space, hence we double the vector size | ||
470 | let len = V.length oldPipelineV | ||
471 | modifyIORef pipelinesRef $ \v -> (V.concat [v,V.replicate len Nothing]) // [(len,Just p)] | ||
472 | return (len,Just len) | ||
473 | Just i -> do | ||
474 | modifyIORef pipelinesRef $ \v -> v // [(i,Just p)] | ||
475 | return (i,Nothing) | ||
476 | -- create input connection | ||
477 | let sm = slotMap input | ||
478 | pToI = [i | n <- glSlotNames p, let Just i = T.lookup n sm] | ||
479 | iToP = V.update (V.replicate (T.size sm) Nothing) (V.imap (\i v -> (v, Just i)) pToI) | ||
480 | writeIORef (glInput p) $ Just $ InputConnection idx input pToI iToP | ||
481 | |||
482 | -- generate object commands for related slots | ||
483 | {- | ||
484 | for each slot in pipeline: | ||
485 | map slot name to input slot name | ||
486 | for each object: | ||
487 | generate command program vector => for each dependent program: | ||
488 | generate object commands | ||
489 | -} | ||
490 | let slotV = slotVector input | ||
491 | progV = glPrograms p | ||
492 | texUnitMap = glTexUnitMapping p | ||
493 | topUnis = uniformSetup input | ||
494 | emptyV = V.replicate (V.length progV) [] | ||
495 | extend v = case shouldExtend of | ||
496 | Nothing -> v | ||
497 | Just l -> V.concat [v,V.replicate l V.empty] | ||
498 | V.forM_ (V.zip pToI (glSlotPrograms p)) $ \(slotIdx,prgs) -> do | ||
499 | slot <- readIORef $ slotV ! slotIdx | ||
500 | forM_ (IM.elems $ objectMap slot) $ \obj -> do | ||
501 | let cmdV = emptyV // [(prgIdx,createObjectCommands texUnitMap topUnis obj (progV ! prgIdx)) | prgIdx <- prgs] | ||
502 | modifyIORef (objCommands obj) $ \v -> extend v // [(idx,cmdV)] | ||
503 | {- | ||
504 | track state: | ||
505 | - render target | ||
506 | - binded textures | ||
507 | -} | ||
508 | |||
509 | {- | ||
510 | render steps: | ||
511 | - update uniforms | ||
512 | - per uniform setup | ||
513 | - buffer setup (one buffer per object, which has per at least one object uniform) | ||
514 | - new command: set uniform buffer (binds uniform buffer to program's buffer slot) | ||
515 | - render slot steps: | ||
516 | - set uniform buffer or set uniforms separately | ||
517 | - set vertex and index array | ||
518 | - call draw command | ||
519 | -} | ||
520 | {- | ||
521 | storage alternatives: | ||
522 | - interleaved / separated | ||
523 | - VAO or VBOs | ||
524 | -} | ||
525 | {- | ||
526 | strategy: | ||
527 | step 1: generate commands for an object | ||
528 | step 2: sort object merge and do optimization by filtering redundant commands | ||
529 | -} | ||
530 | {- | ||
531 | design: | ||
532 | runtime eleminiation of redundant buffer bind commands and redundant texture bind commands | ||
533 | -} | ||
534 | {- | ||
535 | track: | ||
536 | buffer binding on various targets: gl_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER | ||
537 | glEnable/DisableVertexAttribArray | ||
538 | -} | ||
539 | renderSlot :: [GLObjectCommand] -> IO () | ||
540 | renderSlot cmds = forM_ cmds $ \cmd -> do | ||
541 | case cmd of | ||
542 | GLSetVertexAttribArray idx buf size typ ptr -> do | ||
543 | glBindBuffer gl_ARRAY_BUFFER buf | ||
544 | glEnableVertexAttribArray idx | ||
545 | glVertexAttribPointer idx size typ (fromIntegral gl_FALSE) 0 ptr | ||
546 | GLSetVertexAttribIArray idx buf size typ ptr -> do | ||
547 | glBindBuffer gl_ARRAY_BUFFER buf | ||
548 | glEnableVertexAttribArray idx | ||
549 | glVertexAttribIPointer idx size typ 0 ptr | ||
550 | GLDrawArrays mode first count -> glDrawArrays mode first count | ||
551 | GLDrawElements mode count typ buf indicesPtr -> do | ||
552 | glBindBuffer gl_ELEMENT_ARRAY_BUFFER buf | ||
553 | glDrawElements mode count typ indicesPtr | ||
554 | GLSetUniform idx (GLUniform ty ref) -> setUniform idx ty ref | ||
555 | GLBindTexture txTarget tuRef (GLUniform _ ref) -> do | ||
556 | txObjVal <- readIORef ref | ||
557 | -- HINT: ugly and hacky | ||
558 | with txObjVal $ \txObjPtr -> do | ||
559 | txObj <- peek $ castPtr txObjPtr :: IO GLuint | ||
560 | texUnit <- readIORef tuRef | ||
561 | glActiveTexture $ gl_TEXTURE0 + fromIntegral texUnit | ||
562 | glBindTexture txTarget txObj | ||
563 | GLSetVertexAttrib idx val -> do | ||
564 | glDisableVertexAttribArray idx | ||
565 | setVertexAttrib idx val | ||
566 | --isOk <- checkGL | ||
567 | --putStrLn $ SB.unpack isOk ++ " - " ++ show cmd | ||
568 | |||
569 | renderPipeline :: GLPipeline -> IO () | ||
570 | renderPipeline glp = do | ||
571 | glBindVertexArray (glVAO glp) | ||
572 | forM_ (glCommands glp) $ \cmd -> do | ||
573 | case cmd of | ||
574 | GLSetRasterContext rCtx -> setupRasterContext rCtx | ||
575 | GLSetAccumulationContext aCtx -> setupAccumulationContext aCtx | ||
576 | GLSetRenderTarget rt bufs -> do | ||
577 | -- set target viewport | ||
578 | when (rt == 0) $ do -- screen out | ||
579 | ic' <- readIORef $ glInput glp | ||
580 | case ic' of | ||
581 | Nothing -> return () | ||
582 | Just ic -> do | ||
583 | let input = icInput ic | ||
584 | (w,h) <- readIORef $ screenSize input | ||
585 | glViewport 0 0 (fromIntegral w) (fromIntegral h) | ||
586 | -- TODO: set FBO target viewport | ||
587 | glBindFramebuffer gl_DRAW_FRAMEBUFFER rt | ||
588 | case bufs of | ||
589 | Nothing -> return () | ||
590 | Just bl -> withArray bl $ glDrawBuffers (fromIntegral $ length bl) | ||
591 | GLSetProgram p -> glUseProgram p | ||
592 | GLSetSamplerUniform i tu ref -> glUniform1i i tu >> writeIORef ref tu | ||
593 | GLSetTexture tu target tx -> glActiveTexture tu >> glBindTexture target tx | ||
594 | GLClearRenderTarget vals -> clearRenderTarget vals | ||
595 | GLGenerateMipMap tu target -> glActiveTexture tu >> glGenerateMipmap target | ||
596 | GLRenderSlot slotIdx progIdx -> do | ||
597 | input <- readIORef (glInput glp) | ||
598 | case input of | ||
599 | Nothing -> putStrLn "Warning: No pipeline input!" >> return () | ||
600 | Just ic -> do | ||
601 | GLSlot _ objs _ <- readIORef (slotVector (icInput ic) ! (icSlotMapPipelineToInput ic ! slotIdx)) | ||
602 | --putStrLn $ "Rendering " ++ show (V.length objs) ++ " objects" | ||
603 | V.forM_ objs $ \(_,obj) -> do | ||
604 | enabled <- readIORef $ objEnabled obj | ||
605 | when enabled $ do | ||
606 | cmd <- readIORef $ objCommands obj | ||
607 | --putStrLn "Render object" | ||
608 | renderSlot ((cmd ! icId ic) ! progIdx) | ||
609 | {- | ||
610 | GLSetSampler | ||
611 | GLSaveImage | ||
612 | GLLoadImage | ||
613 | -} | ||
614 | --isOk <- checkGL | ||
615 | --putStrLn $ SB.unpack isOk ++ " - " ++ show cmd | ||
616 | |||
617 | data CGState | ||
618 | = CGState | ||
619 | { currentProgram :: ProgramName | ||
620 | , textureBinding :: IntMap GLTexture | ||
621 | } | ||
622 | |||
623 | initCGState = CGState | ||
624 | { currentProgram = error "CGState: empty currentProgram" | ||
625 | , textureBinding = IM.empty | ||
626 | } | ||
627 | |||
628 | type CG a = State CGState a | ||
629 | |||
630 | compileCommand :: Trie (IORef GLint) -> Vector GLSampler -> Vector GLTexture -> Vector GLRenderTarget -> Vector GLProgram -> Command -> CG GLCommand | ||
631 | compileCommand texUnitMap samplers textures targets programs cmd = case cmd of | ||
632 | SetRasterContext rCtx -> return $ GLSetRasterContext rCtx | ||
633 | SetAccumulationContext aCtx -> return $ GLSetAccumulationContext aCtx | ||
634 | SetRenderTarget rt -> let GLRenderTarget fbo bufs = targets ! rt in return $ GLSetRenderTarget fbo bufs | ||
635 | SetProgram p -> do | ||
636 | modify (\s -> s {currentProgram = p}) | ||
637 | return $ GLSetProgram $ programObject $ programs ! p | ||
638 | SetSamplerUniform n tu -> do | ||
639 | p <- currentProgram <$> get | ||
640 | case T.lookup (pack n) (inputTextures $ programs ! p) of | ||
641 | Nothing -> fail "internal error (SetSamplerUniform)!" | ||
642 | Just i -> case T.lookup (pack n) texUnitMap of | ||
643 | Nothing -> fail "internal error (SetSamplerUniform - IORef)!" | ||
644 | Just r -> return $ GLSetSamplerUniform i (fromIntegral tu) r | ||
645 | SetTexture tu t -> do | ||
646 | let tex = textures ! t | ||
647 | modify (\s -> s {textureBinding = IM.insert tu tex $ textureBinding s}) | ||
648 | return $ GLSetTexture (gl_TEXTURE0 + fromIntegral tu) (glTextureTarget tex) (glTextureObject tex) | ||
649 | {- | ||
650 | SetSampler tu s -> liftIO $ do | ||
651 | glBindSampler (fromIntegral tu) (samplerObject $ glSamplers glp ! s) | ||
652 | -} | ||
653 | RenderSlot slot -> do | ||
654 | p <- currentProgram <$> get | ||
655 | return $ GLRenderSlot slot p | ||
656 | ClearRenderTarget vals -> return $ GLClearRenderTarget vals | ||
657 | GenerateMipMap tu -> do | ||
658 | tb <- textureBinding <$> get | ||
659 | case IM.lookup tu tb of | ||
660 | Nothing -> fail "internal error (GenerateMipMap)!" | ||
661 | Just tex -> return $ GLGenerateMipMap (gl_TEXTURE0 + fromIntegral tu) (glTextureTarget tex) | ||
662 | {- | ||
663 | SaveImage _ _ -> undefined | ||
664 | LoadImage _ _ -> undefined | ||
665 | -} | ||
diff --git a/Backend/GL/Data.hs b/Backend/GL/Data.hs new file mode 100644 index 0000000..4eb3fa0 --- /dev/null +++ b/Backend/GL/Data.hs | |||
@@ -0,0 +1,95 @@ | |||
1 | module Backend.GL.Data where | ||
2 | |||
3 | import Control.Applicative | ||
4 | import Control.Monad | ||
5 | import Data.ByteString.Char8 (ByteString) | ||
6 | import Data.IORef | ||
7 | import Data.List as L | ||
8 | import Data.Maybe | ||
9 | import Data.Trie as T | ||
10 | import Foreign | ||
11 | --import qualified Data.IntMap as IM | ||
12 | import qualified Data.Map as Map | ||
13 | import qualified Data.Set as Set | ||
14 | import qualified Data.Vector as V | ||
15 | import qualified Data.Vector.Storable as SV | ||
16 | |||
17 | --import Control.DeepSeq | ||
18 | |||
19 | import Graphics.Rendering.OpenGL.Raw.Core33 | ||
20 | import Data.Word | ||
21 | import Codec.Picture | ||
22 | import Codec.Picture.RGBA8 | ||
23 | |||
24 | import Backend.GL.Type | ||
25 | import Backend.GL.Util | ||
26 | |||
27 | -- Buffer | ||
28 | compileBuffer :: [Array] -> IO Buffer | ||
29 | compileBuffer arrs = do | ||
30 | let calcDesc (offset,setters,descs) (Array arrType cnt setter) = | ||
31 | let size = cnt * sizeOfArrayType arrType | ||
32 | in (size + offset, (offset,size,setter):setters, ArrayDesc arrType cnt offset size:descs) | ||
33 | (bufSize,arrSetters,arrDescs) = foldl' calcDesc (0,[],[]) arrs | ||
34 | bo <- alloca $! \pbo -> glGenBuffers 1 pbo >> peek pbo | ||
35 | glBindBuffer gl_ARRAY_BUFFER bo | ||
36 | glBufferData gl_ARRAY_BUFFER (fromIntegral bufSize) nullPtr gl_STATIC_DRAW | ||
37 | forM_ arrSetters $! \(offset,size,setter) -> setter $! glBufferSubData gl_ARRAY_BUFFER (fromIntegral offset) (fromIntegral size) | ||
38 | glBindBuffer gl_ARRAY_BUFFER 0 | ||
39 | return $! Buffer (V.fromList $! reverse arrDescs) bo | ||
40 | |||
41 | updateBuffer :: Buffer -> [(Int,Array)] -> IO () | ||
42 | updateBuffer (Buffer arrDescs bo) arrs = do | ||
43 | glBindBuffer gl_ARRAY_BUFFER bo | ||
44 | forM arrs $ \(i,Array arrType cnt setter) -> do | ||
45 | let ArrayDesc ty len offset size = arrDescs V.! i | ||
46 | when (ty == arrType && cnt == len) $ | ||
47 | setter $! glBufferSubData gl_ARRAY_BUFFER (fromIntegral offset) (fromIntegral size) | ||
48 | glBindBuffer gl_ARRAY_BUFFER 0 | ||
49 | |||
50 | bufferSize :: Buffer -> Int | ||
51 | bufferSize = V.length . bufArrays | ||
52 | |||
53 | arraySize :: Buffer -> Int -> Int | ||
54 | arraySize buf arrIdx = arrLength $! bufArrays buf V.! arrIdx | ||
55 | |||
56 | arrayType :: Buffer -> Int -> ArrayType | ||
57 | arrayType buf arrIdx = arrType $! bufArrays buf V.! arrIdx | ||
58 | |||
59 | -- Texture | ||
60 | |||
61 | -- FIXME: Temporary implemenation | ||
62 | compileTexture2DRGBAF :: Bool -> Bool -> DynamicImage -> IO TextureData | ||
63 | compileTexture2DRGBAF isMip isClamped bitmap' = do | ||
64 | let bitmap = ImageRGBA8 $ fromDynamicImage bitmap' | ||
65 | glPixelStorei gl_UNPACK_ALIGNMENT 1 | ||
66 | to <- alloca $! \pto -> glGenTextures 1 pto >> peek pto | ||
67 | glBindTexture gl_TEXTURE_2D to | ||
68 | let (width,height) = bitmapSize bitmap | ||
69 | bitmapSize (ImageRGB8 (Image w h _)) = (w,h) | ||
70 | bitmapSize (ImageRGBA8 (Image w h _)) = (w,h) | ||
71 | bitmapSize _ = error "unsupported image type :(" | ||
72 | withBitmap (ImageRGB8 (Image w h v)) f = SV.unsafeWith v $ f (w,h) 3 0 | ||
73 | withBitmap (ImageRGBA8 (Image w h v)) f = SV.unsafeWith v $ f (w,h) 4 0 | ||
74 | withBitmap _ _ = error "unsupported image type :(" | ||
75 | wrapMode = case isClamped of | ||
76 | True -> gl_CLAMP_TO_EDGE | ||
77 | False -> gl_REPEAT | ||
78 | (minFilter,maxLevel) = case isMip of | ||
79 | False -> (gl_LINEAR,0) | ||
80 | True -> (gl_LINEAR_MIPMAP_LINEAR, floor $ log (fromIntegral $ max width height) / log 2) | ||
81 | glTexParameteri gl_TEXTURE_2D gl_TEXTURE_WRAP_S $ fromIntegral wrapMode | ||
82 | glTexParameteri gl_TEXTURE_2D gl_TEXTURE_WRAP_T $ fromIntegral wrapMode | ||
83 | glTexParameteri gl_TEXTURE_2D gl_TEXTURE_MIN_FILTER $ fromIntegral minFilter | ||
84 | glTexParameteri gl_TEXTURE_2D gl_TEXTURE_MAG_FILTER $ fromIntegral gl_LINEAR | ||
85 | glTexParameteri gl_TEXTURE_2D gl_TEXTURE_BASE_LEVEL 0 | ||
86 | glTexParameteri gl_TEXTURE_2D gl_TEXTURE_MAX_LEVEL $ fromIntegral maxLevel | ||
87 | withBitmap bitmap $ \(w,h) nchn 0 ptr -> do | ||
88 | let internalFormat = fromIntegral gl_RGBA8 | ||
89 | dataFormat = fromIntegral $ case nchn of | ||
90 | 3 -> gl_RGB | ||
91 | 4 -> gl_RGBA | ||
92 | _ -> error "unsupported texture format!" | ||
93 | glTexImage2D gl_TEXTURE_2D 0 internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat gl_UNSIGNED_BYTE $ castPtr ptr | ||
94 | when isMip $ glGenerateMipmap gl_TEXTURE_2D | ||
95 | return $ TextureData to | ||
diff --git a/Backend/GL/Input.hs b/Backend/GL/Input.hs new file mode 100644 index 0000000..6d4f40f --- /dev/null +++ b/Backend/GL/Input.hs | |||
@@ -0,0 +1,381 @@ | |||
1 | module Backend.GL.Input where | ||
2 | |||
3 | import Control.Applicative | ||
4 | import Control.Exception | ||
5 | import Control.Monad | ||
6 | import Data.ByteString.Char8 (ByteString,pack) | ||
7 | import Data.IORef | ||
8 | import Data.IntMap (IntMap) | ||
9 | import Data.Trie (Trie) | ||
10 | import Data.Trie.Convenience as T | ||
11 | import Data.Vector (Vector,(//),(!)) | ||
12 | import Data.Word | ||
13 | import Foreign | ||
14 | import qualified Data.ByteString.Char8 as SB | ||
15 | import qualified Data.IntMap as IM | ||
16 | import qualified Data.Set as S | ||
17 | import qualified Data.Map as Map | ||
18 | import qualified Data.Trie as T | ||
19 | import qualified Data.Vector as V | ||
20 | import qualified Data.Vector.Algorithms.Intro as I | ||
21 | |||
22 | import Graphics.Rendering.OpenGL.Raw.Core33 | ||
23 | |||
24 | import IR as IR | ||
25 | import Backend.GL.Type as T | ||
26 | import Backend.GL.Util | ||
27 | |||
28 | import qualified IR as IR | ||
29 | |||
30 | schemaFromPipeline :: IR.Pipeline -> PipelineSchema | ||
31 | schemaFromPipeline a = PipelineSchema (T.fromList sl) (foldl T.unionL T.empty ul) | ||
32 | where | ||
33 | (sl,ul) = unzip [((pack n,SlotSchema p (fmap cvt (toTrie s))),toTrie u) | IR.Slot n u s p _ <- V.toList $ IR.slots a] | ||
34 | cvt a = case toStreamType a of | ||
35 | Just v -> v | ||
36 | Nothing -> error "internal error (schemaFromPipeline)" | ||
37 | |||
38 | mkUniform :: [(ByteString,InputType)] -> IO (Trie InputSetter, Trie GLUniform) | ||
39 | mkUniform l = do | ||
40 | unisAndSetters <- forM l $ \(n,t) -> do | ||
41 | (uni, setter) <- mkUniformSetter t | ||
42 | return ((n,uni),(n,setter)) | ||
43 | let (unis,setters) = unzip unisAndSetters | ||
44 | return (T.fromList setters, T.fromList unis) | ||
45 | |||
46 | mkGLPipelineInput :: PipelineSchema -> IO GLPipelineInput | ||
47 | mkGLPipelineInput sch = do | ||
48 | let sm = T.fromList $ zip (T.keys $ T.slots sch) [0..] | ||
49 | len = T.size sm | ||
50 | (setters,unis) <- mkUniform $ T.toList $ uniforms sch | ||
51 | seed <- newIORef 0 | ||
52 | slotV <- V.replicateM len $ newIORef (GLSlot IM.empty V.empty Ordered) | ||
53 | size <- newIORef (0,0) | ||
54 | ppls <- newIORef $ V.singleton Nothing | ||
55 | return $ GLPipelineInput | ||
56 | { schema = sch | ||
57 | , slotMap = sm | ||
58 | , slotVector = slotV | ||
59 | , objSeed = seed | ||
60 | , uniformSetter = setters | ||
61 | , uniformSetup = unis | ||
62 | , screenSize = size | ||
63 | , pipelines = ppls | ||
64 | } | ||
65 | |||
66 | -- object | ||
67 | addObject :: GLPipelineInput -> ByteString -> Primitive -> Maybe (IndexStream Buffer) -> Trie (Stream Buffer) -> [ByteString] -> IO Object | ||
68 | addObject input slotName prim indices attribs uniformNames = do | ||
69 | let sch = schema input | ||
70 | forM_ uniformNames $ \n -> case T.lookup n (uniforms sch) of | ||
71 | Nothing -> throw $ userError $ "Unknown uniform: " ++ show n | ||
72 | _ -> return () | ||
73 | case T.lookup slotName (T.slots sch) of | ||
74 | Nothing -> throw $ userError $ "Unknown slot: " ++ show slotName | ||
75 | Just (SlotSchema sPrim sAttrs) -> do | ||
76 | when (sPrim /= (primitiveToFetchPrimitive prim)) $ throw $ userError $ | ||
77 | "Primitive mismatch for slot (" ++ show slotName ++ ") expected " ++ show sPrim ++ " but got " ++ show prim | ||
78 | let sType = fmap streamToStreamType attribs | ||
79 | when (sType /= sAttrs) $ throw $ userError $ unlines $ | ||
80 | [ "Attribute stream mismatch for slot (" ++ show slotName ++ ") expected " | ||
81 | , show sAttrs | ||
82 | , " but got " | ||
83 | , show sType | ||
84 | ] | ||
85 | |||
86 | let slotIdx = case slotName `T.lookup` slotMap input of | ||
87 | Nothing -> error "internal error (slot index)" | ||
88 | Just i -> i | ||
89 | seed = objSeed input | ||
90 | order <- newIORef 0 | ||
91 | enabled <- newIORef True | ||
92 | index <- readIORef seed | ||
93 | modifyIORef seed (1+) | ||
94 | (setters,unis) <- mkUniform [(n,t) | n <- uniformNames, let Just t = T.lookup n (uniforms sch)] | ||
95 | cmdsRef <- newIORef (V.singleton V.empty) | ||
96 | let obj = Object | ||
97 | { objSlot = slotIdx | ||
98 | , objPrimitive = prim | ||
99 | , objIndices = indices | ||
100 | , objAttributes = attribs | ||
101 | , objUniSetter = setters | ||
102 | , objUniSetup = unis | ||
103 | , objOrder = order | ||
104 | , objEnabled = enabled | ||
105 | , objId = index | ||
106 | , objCommands = cmdsRef | ||
107 | } | ||
108 | |||
109 | modifyIORef (slotVector input ! slotIdx) $ \(GLSlot objs _ _) -> GLSlot (IM.insert index obj objs) V.empty Generate | ||
110 | |||
111 | -- generate GLObjectCommands for the new object | ||
112 | {- | ||
113 | foreach pipeline: | ||
114 | foreach realted program: | ||
115 | generate commands | ||
116 | -} | ||
117 | ppls <- readIORef $ pipelines input | ||
118 | let topUnis = uniformSetup input | ||
119 | cmds <- V.forM ppls $ \mp -> case mp of | ||
120 | Nothing -> return V.empty | ||
121 | Just p -> do | ||
122 | Just ic <- readIORef $ glInput p | ||
123 | case icSlotMapInputToPipeline ic ! slotIdx of | ||
124 | Nothing -> do | ||
125 | putStrLn $ " ** slot is not used!" | ||
126 | return V.empty -- this slot is not used in that pipeline | ||
127 | Just pSlotIdx -> do | ||
128 | putStrLn "slot is used!" | ||
129 | --where | ||
130 | let emptyV = V.replicate (V.length $ glPrograms p) [] | ||
131 | return $ emptyV // [(prgIdx,createObjectCommands (glTexUnitMapping p) topUnis obj (glPrograms p ! prgIdx))| prgIdx <- glSlotPrograms p ! pSlotIdx] | ||
132 | writeIORef cmdsRef cmds | ||
133 | return obj | ||
134 | |||
135 | removeObject :: GLPipelineInput -> Object -> IO () | ||
136 | removeObject p obj = modifyIORef (slotVector p ! objSlot obj) $ \(GLSlot objs _ _) -> GLSlot (IM.delete (objId obj) objs) V.empty Generate | ||
137 | |||
138 | enableObject :: Object -> Bool -> IO () | ||
139 | enableObject obj b = writeIORef (objEnabled obj) b | ||
140 | |||
141 | setObjectOrder :: GLPipelineInput -> Object -> Int -> IO () | ||
142 | setObjectOrder p obj i = do | ||
143 | writeIORef (objOrder obj) i | ||
144 | modifyIORef (slotVector p ! objSlot obj) $ \(GLSlot objs sorted _) -> GLSlot objs sorted Reorder | ||
145 | |||
146 | objectUniformSetter :: Object -> Trie InputSetter | ||
147 | objectUniformSetter = objUniSetter | ||
148 | |||
149 | setScreenSize :: GLPipelineInput -> Word -> Word -> IO () | ||
150 | setScreenSize p w h = writeIORef (screenSize p) (w,h) | ||
151 | |||
152 | sortSlotObjects :: GLPipelineInput -> IO () | ||
153 | sortSlotObjects p = V.forM_ (slotVector p) $ \slotRef -> do | ||
154 | GLSlot objMap sortedV ord <- readIORef slotRef | ||
155 | let cmpFun (a,_) (b,_) = a `compare` b | ||
156 | doSort objs = do | ||
157 | ordObjsM <- V.thaw objs | ||
158 | I.sortBy cmpFun ordObjsM | ||
159 | ordObjs <- V.freeze ordObjsM | ||
160 | writeIORef slotRef (GLSlot objMap ordObjs Ordered) | ||
161 | case ord of | ||
162 | Ordered -> return () | ||
163 | Generate -> do | ||
164 | objs <- V.forM (V.fromList $ IM.elems objMap) $ \obj -> do | ||
165 | ord <- readIORef $ objOrder obj | ||
166 | return (ord,obj) | ||
167 | doSort objs | ||
168 | Reorder -> do | ||
169 | objs <- V.forM sortedV $ \(_,obj) -> do | ||
170 | ord <- readIORef $ objOrder obj | ||
171 | return (ord,obj) | ||
172 | doSort objs | ||
173 | |||
174 | createObjectCommands :: Trie (IORef GLint) -> Trie GLUniform -> Object -> GLProgram -> [GLObjectCommand] | ||
175 | createObjectCommands texUnitMap topUnis obj prg = objUniCmds ++ objStreamCmds ++ [objDrawCmd] | ||
176 | where | ||
177 | -- object draw command | ||
178 | objDrawCmd = case objIndices obj of | ||
179 | Nothing -> GLDrawArrays prim 0 (fromIntegral count) | ||
180 | Just (IndexStream (Buffer arrs bo) arrIdx start idxCount) -> GLDrawElements prim (fromIntegral idxCount) idxType bo ptr | ||
181 | where | ||
182 | ArrayDesc arrType arrLen arrOffs arrSize = arrs ! arrIdx | ||
183 | idxType = arrayTypeToGLType arrType | ||
184 | ptr = intPtrToPtr $! fromIntegral (arrOffs + start * sizeOfArrayType arrType) | ||
185 | where | ||
186 | objAttrs = objAttributes obj | ||
187 | prim = primitiveToGLType $ objPrimitive obj | ||
188 | count = head [c | Stream _ _ _ _ c <- T.elems objAttrs] | ||
189 | |||
190 | -- object uniform commands | ||
191 | -- texture slot setup commands | ||
192 | objUniCmds = uniCmds ++ texCmds | ||
193 | where | ||
194 | uniCmds = [GLSetUniform i u | (n,i) <- uniMap, let u = T.lookupWithDefault (topUni n) n objUnis] | ||
195 | uniMap = T.toList $ inputUniforms prg | ||
196 | topUni n = T.lookupWithDefault (error "internal error (createObjectCommands)!") n topUnis | ||
197 | objUnis = objUniSetup obj | ||
198 | texUnis = S.toList $ inputTextureUniforms prg | ||
199 | texCmds = [ GLBindTexture (inputTypeToTextureTarget $ uniInputType u) texUnit u | ||
200 | | n <- texUnis | ||
201 | , let u = T.lookupWithDefault (topUni n) n objUnis | ||
202 | , let texUnit = T.lookupWithDefault (error "internal error (createObjectCommands - Texture Unit)") n texUnitMap | ||
203 | ] | ||
204 | uniInputType (GLUniform ty _) = ty | ||
205 | |||
206 | -- object attribute stream commands | ||
207 | objStreamCmds = [attrCmd i s | (i,name) <- T.elems attrMap, let Just s = T.lookup name objAttrs] | ||
208 | where | ||
209 | attrMap = inputStreams prg | ||
210 | objAttrs = objAttributes obj | ||
211 | attrCmd i s = case s of | ||
212 | Stream ty (Buffer arrs bo) arrIdx start len -> case ty of | ||
213 | TWord -> setIntAttrib 1 | ||
214 | TV2U -> setIntAttrib 2 | ||
215 | TV3U -> setIntAttrib 3 | ||
216 | TV4U -> setIntAttrib 4 | ||
217 | TInt -> setIntAttrib 1 | ||
218 | TV2I -> setIntAttrib 2 | ||
219 | TV3I -> setIntAttrib 3 | ||
220 | TV4I -> setIntAttrib 4 | ||
221 | TFloat -> setFloatAttrib 1 | ||
222 | TV2F -> setFloatAttrib 2 | ||
223 | TV3F -> setFloatAttrib 3 | ||
224 | TV4F -> setFloatAttrib 4 | ||
225 | TM22F -> setFloatAttrib 4 | ||
226 | TM23F -> setFloatAttrib 6 | ||
227 | TM24F -> setFloatAttrib 8 | ||
228 | TM32F -> setFloatAttrib 6 | ||
229 | TM33F -> setFloatAttrib 9 | ||
230 | TM34F -> setFloatAttrib 12 | ||
231 | TM42F -> setFloatAttrib 8 | ||
232 | TM43F -> setFloatAttrib 12 | ||
233 | TM44F -> setFloatAttrib 16 | ||
234 | where | ||
235 | setFloatAttrib n = GLSetVertexAttribArray i bo n glType (ptr n) | ||
236 | setIntAttrib n = GLSetVertexAttribIArray i bo n glType (ptr n) | ||
237 | ArrayDesc arrType arrLen arrOffs arrSize = arrs ! arrIdx | ||
238 | glType = arrayTypeToGLType arrType | ||
239 | ptr compCnt = intPtrToPtr $! fromIntegral (arrOffs + start * fromIntegral compCnt * sizeOfArrayType arrType) | ||
240 | |||
241 | -- constant generic attribute | ||
242 | constAttr -> GLSetVertexAttrib i constAttr | ||
243 | |||
244 | nullSetter :: ByteString -> String -> a -> IO () | ||
245 | nullSetter n t _ = return () -- Prelude.putStrLn $ "WARNING: unknown uniform: " ++ SB.unpack n ++ " :: " ++ t | ||
246 | |||
247 | uniformBool :: ByteString -> Trie InputSetter -> SetterFun Bool | ||
248 | uniformV2B :: ByteString -> Trie InputSetter -> SetterFun V2B | ||
249 | uniformV3B :: ByteString -> Trie InputSetter -> SetterFun V3B | ||
250 | uniformV4B :: ByteString -> Trie InputSetter -> SetterFun V4B | ||
251 | |||
252 | uniformWord :: ByteString -> Trie InputSetter -> SetterFun Word32 | ||
253 | uniformV2U :: ByteString -> Trie InputSetter -> SetterFun V2U | ||
254 | uniformV3U :: ByteString -> Trie InputSetter -> SetterFun V3U | ||
255 | uniformV4U :: ByteString -> Trie InputSetter -> SetterFun V4U | ||
256 | |||
257 | uniformInt :: ByteString -> Trie InputSetter -> SetterFun Int32 | ||
258 | uniformV2I :: ByteString -> Trie InputSetter -> SetterFun V2I | ||
259 | uniformV3I :: ByteString -> Trie InputSetter -> SetterFun V3I | ||
260 | uniformV4I :: ByteString -> Trie InputSetter -> SetterFun V4I | ||
261 | |||
262 | uniformFloat :: ByteString -> Trie InputSetter -> SetterFun Float | ||
263 | uniformV2F :: ByteString -> Trie InputSetter -> SetterFun V2F | ||
264 | uniformV3F :: ByteString -> Trie InputSetter -> SetterFun V3F | ||
265 | uniformV4F :: ByteString -> Trie InputSetter -> SetterFun V4F | ||
266 | |||
267 | uniformM22F :: ByteString -> Trie InputSetter -> SetterFun M22F | ||
268 | uniformM23F :: ByteString -> Trie InputSetter -> SetterFun M23F | ||
269 | uniformM24F :: ByteString -> Trie InputSetter -> SetterFun M24F | ||
270 | uniformM32F :: ByteString -> Trie InputSetter -> SetterFun M32F | ||
271 | uniformM33F :: ByteString -> Trie InputSetter -> SetterFun M33F | ||
272 | uniformM34F :: ByteString -> Trie InputSetter -> SetterFun M34F | ||
273 | uniformM42F :: ByteString -> Trie InputSetter -> SetterFun M42F | ||
274 | uniformM43F :: ByteString -> Trie InputSetter -> SetterFun M43F | ||
275 | uniformM44F :: ByteString -> Trie InputSetter -> SetterFun M44F | ||
276 | |||
277 | uniformFTexture2D :: ByteString -> Trie InputSetter -> SetterFun TextureData | ||
278 | |||
279 | uniformBool n is = case T.lookup n is of | ||
280 | Just (SBool fun) -> fun | ||
281 | _ -> nullSetter n "Bool" | ||
282 | |||
283 | uniformV2B n is = case T.lookup n is of | ||
284 | Just (SV2B fun) -> fun | ||
285 | _ -> nullSetter n "V2B" | ||
286 | |||
287 | uniformV3B n is = case T.lookup n is of | ||
288 | Just (SV3B fun) -> fun | ||
289 | _ -> nullSetter n "V3B" | ||
290 | |||
291 | uniformV4B n is = case T.lookup n is of | ||
292 | Just (SV4B fun) -> fun | ||
293 | _ -> nullSetter n "V4B" | ||
294 | |||
295 | uniformWord n is = case T.lookup n is of | ||
296 | Just (SWord fun) -> fun | ||
297 | _ -> nullSetter n "Word" | ||
298 | |||
299 | uniformV2U n is = case T.lookup n is of | ||
300 | Just (SV2U fun) -> fun | ||
301 | _ -> nullSetter n "V2U" | ||
302 | |||
303 | uniformV3U n is = case T.lookup n is of | ||
304 | Just (SV3U fun) -> fun | ||
305 | _ -> nullSetter n "V3U" | ||
306 | |||
307 | uniformV4U n is = case T.lookup n is of | ||
308 | Just (SV4U fun) -> fun | ||
309 | _ -> nullSetter n "V4U" | ||
310 | |||
311 | uniformInt n is = case T.lookup n is of | ||
312 | Just (SInt fun) -> fun | ||
313 | _ -> nullSetter n "Int" | ||
314 | |||
315 | uniformV2I n is = case T.lookup n is of | ||
316 | Just (SV2I fun) -> fun | ||
317 | _ -> nullSetter n "V2I" | ||
318 | |||
319 | uniformV3I n is = case T.lookup n is of | ||
320 | Just (SV3I fun) -> fun | ||
321 | _ -> nullSetter n "V3I" | ||
322 | |||
323 | uniformV4I n is = case T.lookup n is of | ||
324 | Just (SV4I fun) -> fun | ||
325 | _ -> nullSetter n "V4I" | ||
326 | |||
327 | uniformFloat n is = case T.lookup n is of | ||
328 | Just (SFloat fun) -> fun | ||
329 | _ -> nullSetter n "Float" | ||
330 | |||
331 | uniformV2F n is = case T.lookup n is of | ||
332 | Just (SV2F fun) -> fun | ||
333 | _ -> nullSetter n "V2F" | ||
334 | |||
335 | uniformV3F n is = case T.lookup n is of | ||
336 | Just (SV3F fun) -> fun | ||
337 | _ -> nullSetter n "V3F" | ||
338 | |||
339 | uniformV4F n is = case T.lookup n is of | ||
340 | Just (SV4F fun) -> fun | ||
341 | _ -> nullSetter n "V4F" | ||
342 | |||
343 | uniformM22F n is = case T.lookup n is of | ||
344 | Just (SM22F fun) -> fun | ||
345 | _ -> nullSetter n "M22F" | ||
346 | |||
347 | uniformM23F n is = case T.lookup n is of | ||
348 | Just (SM23F fun) -> fun | ||
349 | _ -> nullSetter n "M23F" | ||
350 | |||
351 | uniformM24F n is = case T.lookup n is of | ||
352 | Just (SM24F fun) -> fun | ||
353 | _ -> nullSetter n "M24F" | ||
354 | |||
355 | uniformM32F n is = case T.lookup n is of | ||
356 | Just (SM32F fun) -> fun | ||
357 | _ -> nullSetter n "M32F" | ||
358 | |||
359 | uniformM33F n is = case T.lookup n is of | ||
360 | Just (SM33F fun) -> fun | ||
361 | _ -> nullSetter n "M33F" | ||
362 | |||
363 | uniformM34F n is = case T.lookup n is of | ||
364 | Just (SM34F fun) -> fun | ||
365 | _ -> nullSetter n "M34F" | ||
366 | |||
367 | uniformM42F n is = case T.lookup n is of | ||
368 | Just (SM42F fun) -> fun | ||
369 | _ -> nullSetter n "M42F" | ||
370 | |||
371 | uniformM43F n is = case T.lookup n is of | ||
372 | Just (SM43F fun) -> fun | ||
373 | _ -> nullSetter n "M43F" | ||
374 | |||
375 | uniformM44F n is = case T.lookup n is of | ||
376 | Just (SM44F fun) -> fun | ||
377 | _ -> nullSetter n "M44F" | ||
378 | |||
379 | uniformFTexture2D n is = case T.lookup n is of | ||
380 | Just (SFTexture2D fun) -> fun | ||
381 | _ -> nullSetter n "FTexture2D" | ||
diff --git a/Backend/GL/Mesh.hs b/Backend/GL/Mesh.hs new file mode 100644 index 0000000..261e6cc --- /dev/null +++ b/Backend/GL/Mesh.hs | |||
@@ -0,0 +1,232 @@ | |||
1 | {-# LANGUAGE TupleSections #-} | ||
2 | module Backend.GL.Mesh ( | ||
3 | loadMesh, | ||
4 | saveMesh, | ||
5 | addMesh, | ||
6 | compileMesh, | ||
7 | updateMesh, | ||
8 | Mesh(..), | ||
9 | MeshPrimitive(..), | ||
10 | MeshAttribute(..) | ||
11 | ) where | ||
12 | |||
13 | import Control.Applicative | ||
14 | import Control.Monad | ||
15 | import Data.Binary | ||
16 | import Data.ByteString.Char8 (ByteString) | ||
17 | import Foreign.Ptr | ||
18 | import Data.Int | ||
19 | import Foreign.Storable | ||
20 | import Foreign.Marshal.Utils | ||
21 | import System.IO.Unsafe | ||
22 | import qualified Data.ByteString.Char8 as SB | ||
23 | import qualified Data.ByteString.Lazy as LB | ||
24 | import qualified Data.Trie as T | ||
25 | import qualified Data.Vector.Storable as V | ||
26 | import qualified Data.Vector.Storable.Mutable as MV | ||
27 | |||
28 | import Backend.GL | ||
29 | import Backend.GL.Type as T | ||
30 | import IR as IR | ||
31 | |||
32 | fileVersion :: Int32 | ||
33 | fileVersion = 1 | ||
34 | |||
35 | data MeshAttribute | ||
36 | = A_Float (V.Vector Float) | ||
37 | | A_V2F (V.Vector V2F) | ||
38 | | A_V3F (V.Vector V3F) | ||
39 | | A_V4F (V.Vector V4F) | ||
40 | | A_M22F (V.Vector M22F) | ||
41 | | A_M33F (V.Vector M33F) | ||
42 | | A_M44F (V.Vector M44F) | ||
43 | | A_Int (V.Vector Int32) | ||
44 | | A_Word (V.Vector Word32) | ||
45 | |||
46 | data MeshPrimitive | ||
47 | = P_Points | ||
48 | | P_TriangleStrip | ||
49 | | P_Triangles | ||
50 | | P_TriangleStripI (V.Vector Int32) | ||
51 | | P_TrianglesI (V.Vector Int32) | ||
52 | |||
53 | data Mesh | ||
54 | = Mesh | ||
55 | { mAttributes :: T.Trie MeshAttribute | ||
56 | , mPrimitive :: MeshPrimitive | ||
57 | , mGPUData :: Maybe GPUData | ||
58 | } | ||
59 | |||
60 | data GPUData | ||
61 | = GPUData | ||
62 | { dPrimitive :: Primitive | ||
63 | , dStreams :: T.Trie (Stream Buffer) | ||
64 | , dIndices :: Maybe (IndexStream Buffer) | ||
65 | } | ||
66 | |||
67 | loadMesh :: String -> IO Mesh | ||
68 | loadMesh n = compileMesh =<< decode <$> LB.readFile n | ||
69 | |||
70 | saveMesh :: String -> Mesh -> IO () | ||
71 | saveMesh n m = LB.writeFile n (encode m) | ||
72 | |||
73 | addMesh :: GLPipelineInput -> ByteString -> Mesh -> [ByteString] -> IO Object | ||
74 | addMesh input slotName (Mesh _ _ (Just (GPUData prim streams indices))) objUniNames = do | ||
75 | -- select proper attributes | ||
76 | let Just (SlotSchema slotPrim slotStreams) = T.lookup slotName $! T.slots $! T.schema input | ||
77 | filterStream n s | ||
78 | | T.member n slotStreams = Just s | ||
79 | | otherwise = Nothing | ||
80 | addObject input slotName prim indices (T.mapBy filterStream streams) objUniNames | ||
81 | addMesh _ _ _ _ = fail "addMesh: only compiled mesh with GPUData is supported" | ||
82 | |||
83 | withV w a f = w a (\p -> f $ castPtr p) | ||
84 | |||
85 | meshAttrToArray :: MeshAttribute -> Array | ||
86 | meshAttrToArray (A_Float v) = Array ArrFloat (1 * V.length v) $ withV V.unsafeWith v | ||
87 | meshAttrToArray (A_V2F v) = Array ArrFloat (2 * V.length v) $ withV V.unsafeWith v | ||
88 | meshAttrToArray (A_V3F v) = Array ArrFloat (3 * V.length v) $ withV V.unsafeWith v | ||
89 | meshAttrToArray (A_V4F v) = Array ArrFloat (4 * V.length v) $ withV V.unsafeWith v | ||
90 | meshAttrToArray (A_M22F v) = Array ArrFloat (4 * V.length v) $ withV V.unsafeWith v | ||
91 | meshAttrToArray (A_M33F v) = Array ArrFloat (9 * V.length v) $ withV V.unsafeWith v | ||
92 | meshAttrToArray (A_M44F v) = Array ArrFloat (16 * V.length v) $ withV V.unsafeWith v | ||
93 | meshAttrToArray (A_Int v) = Array ArrInt32 (1 * V.length v) $ withV V.unsafeWith v | ||
94 | meshAttrToArray (A_Word v) = Array ArrWord32 (1 * V.length v) $ withV V.unsafeWith v | ||
95 | |||
96 | meshAttrToStream :: Buffer -> Int -> MeshAttribute -> Stream Buffer | ||
97 | meshAttrToStream b i (A_Float v) = Stream TFloat b i 0 (V.length v) | ||
98 | meshAttrToStream b i (A_V2F v) = Stream TV2F b i 0 (V.length v) | ||
99 | meshAttrToStream b i (A_V3F v) = Stream TV3F b i 0 (V.length v) | ||
100 | meshAttrToStream b i (A_V4F v) = Stream TV4F b i 0 (V.length v) | ||
101 | meshAttrToStream b i (A_M22F v) = Stream TM22F b i 0 (V.length v) | ||
102 | meshAttrToStream b i (A_M33F v) = Stream TM33F b i 0 (V.length v) | ||
103 | meshAttrToStream b i (A_M44F v) = Stream TM44F b i 0 (V.length v) | ||
104 | meshAttrToStream b i (A_Int v) = Stream TInt b i 0 (V.length v) | ||
105 | meshAttrToStream b i (A_Word v) = Stream TWord b i 0 (V.length v) | ||
106 | |||
107 | {- | ||
108 | updateBuffer :: Buffer -> [(Int,Array)] -> IO () | ||
109 | |||
110 | | Stream | ||
111 | { streamType :: StreamType | ||
112 | , streamBuffer :: b | ||
113 | , streamArrIdx :: Int | ||
114 | , streamStart :: Int | ||
115 | , streamLength :: Int | ||
116 | } | ||
117 | |||
118 | -- stream of index values (for index buffer) | ||
119 | data IndexStream b | ||
120 | = IndexStream | ||
121 | { indexBuffer :: b | ||
122 | , indexArrIdx :: Int | ||
123 | , indexStart :: Int | ||
124 | , indexLength :: Int | ||
125 | } | ||
126 | -} | ||
127 | updateMesh :: Mesh -> [(ByteString,MeshAttribute)] -> Maybe MeshPrimitive -> IO () | ||
128 | updateMesh (Mesh dMA dMP (Just (GPUData _ dS dI))) al mp = do | ||
129 | -- check type match | ||
130 | let arrayChk (Array t1 s1 _) (Array t2 s2 _) = t1 == t2 && s1 == s2 | ||
131 | ok = and [T.member n dMA && arrayChk (meshAttrToArray a1) (meshAttrToArray a2) | (n,a1) <- al, let Just a2 = T.lookup n dMA] | ||
132 | if not ok then putStrLn "updateMesh: attribute mismatch!" | ||
133 | else do | ||
134 | forM_ al $ \(n,a) -> do | ||
135 | case T.lookup n dS of | ||
136 | Just (Stream _ b i _ _) -> updateBuffer b [(i,meshAttrToArray a)] | ||
137 | _ -> return () | ||
138 | {- | ||
139 | case mp of | ||
140 | Nothing -> return () | ||
141 | Just p -> do | ||
142 | let ok2 = case (dMP,p) of | ||
143 | (Just (P_TriangleStripI v1, P_TriangleStripI v2) -> V.length v1 == V.length v2 | ||
144 | (P_TrianglesI v1, P_TrianglesI v2) -> V.length v1 == V.length v2 | ||
145 | (a,b) -> a == b | ||
146 | -} | ||
147 | |||
148 | compileMesh :: Mesh -> IO Mesh | ||
149 | compileMesh (Mesh attrs mPrim Nothing) = do | ||
150 | let mkIndexBuf v = do | ||
151 | iBuf <- compileBuffer [Array ArrWord32 (V.length v) $ withV V.unsafeWith v] | ||
152 | return $! Just $! IndexStream iBuf 0 0 (V.length v) | ||
153 | vBuf <- compileBuffer [meshAttrToArray a | a <- T.elems attrs] | ||
154 | (indices,prim) <- case mPrim of | ||
155 | P_Points -> return (Nothing,PointList) | ||
156 | P_TriangleStrip -> return (Nothing,TriangleStrip) | ||
157 | P_Triangles -> return (Nothing,TriangleList) | ||
158 | P_TriangleStripI v -> (,TriangleStrip) <$> mkIndexBuf v | ||
159 | P_TrianglesI v -> (,TriangleList) <$> mkIndexBuf v | ||
160 | let streams = T.fromList $! zipWith (\i (n,a) -> (n,meshAttrToStream vBuf i a)) [0..] (T.toList attrs) | ||
161 | gpuData = GPUData prim streams indices | ||
162 | return $! Mesh attrs mPrim (Just gpuData) | ||
163 | |||
164 | compileMesh mesh = return mesh | ||
165 | |||
166 | sblToV :: Storable a => [SB.ByteString] -> V.Vector a | ||
167 | sblToV ls = v | ||
168 | where | ||
169 | offs o (s:xs) = (o,s):offs (o + SB.length s) xs | ||
170 | offs _ [] = [] | ||
171 | cnt = sum (map SB.length ls) `div` (sizeOf $ V.head v) | ||
172 | v = unsafePerformIO $ do | ||
173 | mv <- MV.new cnt | ||
174 | MV.unsafeWith mv $ \dst -> forM_ (offs 0 ls) $ \(o,s) -> | ||
175 | SB.useAsCStringLen s $ \(src,len) -> moveBytes (plusPtr dst o) src len | ||
176 | V.unsafeFreeze mv | ||
177 | |||
178 | vToSB :: Storable a => V.Vector a -> SB.ByteString | ||
179 | vToSB v = unsafePerformIO $ do | ||
180 | let len = V.length v * sizeOf (V.head v) | ||
181 | V.unsafeWith v $ \p -> SB.packCStringLen (castPtr p,len) | ||
182 | |||
183 | instance Storable a => Binary (V.Vector a) where | ||
184 | put v = put $ vToSB v | ||
185 | get = do s <- get ; return $ sblToV [s] | ||
186 | |||
187 | instance Binary MeshAttribute where | ||
188 | put (A_Float a) = putWord8 0 >> put a | ||
189 | put (A_V2F a) = putWord8 1 >> put a | ||
190 | put (A_V3F a) = putWord8 2 >> put a | ||
191 | put (A_V4F a) = putWord8 3 >> put a | ||
192 | put (A_M22F a) = putWord8 4 >> put a | ||
193 | put (A_M33F a) = putWord8 5 >> put a | ||
194 | put (A_M44F a) = putWord8 6 >> put a | ||
195 | put (A_Int a) = putWord8 7 >> put a | ||
196 | put (A_Word a) = putWord8 8 >> put a | ||
197 | get = do | ||
198 | tag_ <- getWord8 | ||
199 | case tag_ of | ||
200 | 0 -> A_Float <$> get | ||
201 | 1 -> A_V2F <$> get | ||
202 | 2 -> A_V3F <$> get | ||
203 | 3 -> A_V4F <$> get | ||
204 | 4 -> A_M22F <$> get | ||
205 | 5 -> A_M33F <$> get | ||
206 | 6 -> A_M44F <$> get | ||
207 | 7 -> A_Int <$> get | ||
208 | 8 -> A_Word <$> get | ||
209 | _ -> fail "no parse" | ||
210 | |||
211 | instance Binary MeshPrimitive where | ||
212 | put P_Points = putWord8 0 | ||
213 | put P_TriangleStrip = putWord8 1 | ||
214 | put P_Triangles = putWord8 2 | ||
215 | put (P_TriangleStripI a) = putWord8 3 >> put a | ||
216 | put (P_TrianglesI a) = putWord8 4 >> put a | ||
217 | get = do | ||
218 | tag_ <- getWord8 | ||
219 | case tag_ of | ||
220 | 0 -> return P_Points | ||
221 | 1 -> return P_TriangleStrip | ||
222 | 2 -> return P_Triangles | ||
223 | 3 -> P_TriangleStripI <$> get | ||
224 | 4 -> P_TrianglesI <$> get | ||
225 | _ -> fail "no parse" | ||
226 | |||
227 | instance Binary Mesh where | ||
228 | put (Mesh a b _) = put (T.toList a) >> put b | ||
229 | get = do | ||
230 | a <- get | ||
231 | b <- get | ||
232 | return $! Mesh (T.fromList a) b Nothing | ||
diff --git a/Backend/GL/Type.hs b/Backend/GL/Type.hs new file mode 100644 index 0000000..80cba6d --- /dev/null +++ b/Backend/GL/Type.hs | |||
@@ -0,0 +1,530 @@ | |||
1 | {-# LANGUAGE ExistentialQuantification, FlexibleInstances, GeneralizedNewtypeDeriving, ScopedTypeVariables #-} | ||
2 | module Backend.GL.Type where | ||
3 | |||
4 | import Data.ByteString.Char8 (ByteString) | ||
5 | import Data.IORef | ||
6 | import Data.Int | ||
7 | import Data.IntMap (IntMap) | ||
8 | import Data.Set (Set) | ||
9 | import Data.Trie (Trie) | ||
10 | import Data.Vector (Vector) | ||
11 | import Data.Word | ||
12 | import Foreign.Ptr | ||
13 | import Foreign.Storable | ||
14 | |||
15 | import Graphics.Rendering.OpenGL.Raw.Core33 | ||
16 | |||
17 | import IR | ||
18 | |||
19 | --------------- | ||
20 | -- Input API -- | ||
21 | --------------- | ||
22 | {- | ||
23 | -- Buffer | ||
24 | compileBuffer :: [Array] -> IO Buffer | ||
25 | bufferSize :: Buffer -> Int | ||
26 | arraySize :: Buffer -> Int -> Int | ||
27 | arrayType :: Buffer -> Int -> ArrayType | ||
28 | |||
29 | -- Object | ||
30 | addObject :: Renderer -> ByteString -> Primitive -> Maybe (IndexStream Buffer) -> Trie (Stream Buffer) -> [ByteString] -> IO Object | ||
31 | removeObject :: Renderer -> Object -> IO () | ||
32 | objectUniformSetter :: Object -> Trie InputSetter | ||
33 | -} | ||
34 | |||
35 | data Buffer -- internal type | ||
36 | = Buffer | ||
37 | { bufArrays :: Vector ArrayDesc | ||
38 | , bufGLObj :: GLuint | ||
39 | } | ||
40 | deriving (Show,Eq) | ||
41 | |||
42 | data ArrayDesc | ||
43 | = ArrayDesc | ||
44 | { arrType :: ArrayType | ||
45 | , arrLength :: Int -- item count | ||
46 | , arrOffset :: Int -- byte position in buffer | ||
47 | , arrSize :: Int -- size in bytes | ||
48 | } | ||
49 | deriving (Show,Eq) | ||
50 | |||
51 | {- | ||
52 | handles: | ||
53 | uniforms | ||
54 | textures | ||
55 | buffers | ||
56 | objects | ||
57 | |||
58 | GLPipelineInput can be attached to GLPipeline | ||
59 | -} | ||
60 | |||
61 | {- | ||
62 | pipeline input: | ||
63 | - independent from pipeline | ||
64 | - per object features: enable/disable visibility, set render ordering | ||
65 | -} | ||
66 | |||
67 | data SlotSchema | ||
68 | = SlotSchema | ||
69 | { primitive :: FetchPrimitive | ||
70 | , attributes :: Trie StreamType | ||
71 | } | ||
72 | deriving Show | ||
73 | |||
74 | data PipelineSchema | ||
75 | = PipelineSchema | ||
76 | { slots :: Trie SlotSchema | ||
77 | , uniforms :: Trie InputType | ||
78 | } | ||
79 | deriving Show | ||
80 | |||
81 | data GLUniform = forall a. Storable a => GLUniform !InputType !(IORef a) | ||
82 | |||
83 | instance Show GLUniform where | ||
84 | show (GLUniform t _) = "GLUniform " ++ show t | ||
85 | |||
86 | data OrderJob | ||
87 | = Generate | ||
88 | | Reorder | ||
89 | | Ordered | ||
90 | |||
91 | data GLSlot | ||
92 | = GLSlot | ||
93 | { objectMap :: IntMap Object | ||
94 | , sortedObjects :: Vector (Int,Object) | ||
95 | , orderJob :: OrderJob | ||
96 | } | ||
97 | |||
98 | data GLPipelineInput | ||
99 | = GLPipelineInput | ||
100 | { schema :: PipelineSchema | ||
101 | , slotMap :: Trie SlotName | ||
102 | , slotVector :: Vector (IORef GLSlot) | ||
103 | , objSeed :: IORef Int | ||
104 | , uniformSetter :: Trie InputSetter | ||
105 | , uniformSetup :: Trie GLUniform | ||
106 | , screenSize :: IORef (Word,Word) | ||
107 | , pipelines :: IORef (Vector (Maybe GLPipeline)) -- attached pipelines | ||
108 | } | ||
109 | |||
110 | data Object -- internal type | ||
111 | = Object | ||
112 | { objSlot :: SlotName | ||
113 | , objPrimitive :: Primitive | ||
114 | , objIndices :: Maybe (IndexStream Buffer) | ||
115 | , objAttributes :: Trie (Stream Buffer) | ||
116 | , objUniSetter :: Trie InputSetter | ||
117 | , objUniSetup :: Trie GLUniform | ||
118 | , objOrder :: IORef Int | ||
119 | , objEnabled :: IORef Bool | ||
120 | , objId :: Int | ||
121 | , objCommands :: IORef (Vector (Vector [GLObjectCommand])) -- pipeline id, program name, commands | ||
122 | } | ||
123 | |||
124 | -------------- | ||
125 | -- Pipeline -- | ||
126 | -------------- | ||
127 | |||
128 | data GLProgram | ||
129 | = GLProgram | ||
130 | { shaderObjects :: [GLuint] | ||
131 | , programObject :: GLuint | ||
132 | , inputUniforms :: Trie GLint | ||
133 | , inputTextures :: Trie GLint -- all input textures (render texture + uniform texture) | ||
134 | , inputTextureUniforms :: Set ByteString | ||
135 | , inputStreams :: Trie (GLuint,ByteString) | ||
136 | } | ||
137 | |||
138 | data GLTexture | ||
139 | = GLTexture | ||
140 | { glTextureObject :: GLuint | ||
141 | , glTextureTarget :: GLenum | ||
142 | } | ||
143 | |||
144 | data InputConnection | ||
145 | = InputConnection | ||
146 | { icId :: Int -- identifier (vector index) for attached pipeline | ||
147 | , icInput :: GLPipelineInput | ||
148 | , icSlotMapPipelineToInput :: Vector SlotName -- GLPipeline to GLPipelineInput slot name mapping | ||
149 | , icSlotMapInputToPipeline :: Vector (Maybe SlotName) -- GLPipelineInput to GLPipeline slot name mapping | ||
150 | } | ||
151 | |||
152 | data GLPipeline | ||
153 | = GLPipeline | ||
154 | { glPrograms :: Vector GLProgram | ||
155 | , glTextures :: Vector GLTexture | ||
156 | , glSamplers :: Vector GLSampler | ||
157 | , glTargets :: Vector GLRenderTarget | ||
158 | , glCommands :: [GLCommand] | ||
159 | , glSlotPrograms :: Vector [ProgramName] -- programs depend on a slot | ||
160 | , glInput :: IORef (Maybe InputConnection) | ||
161 | , glSlotNames :: Vector ByteString | ||
162 | , glVAO :: GLuint | ||
163 | , glTexUnitMapping :: Trie (IORef GLint) -- maps texture uniforms to texture units | ||
164 | } | ||
165 | |||
166 | data GLSampler | ||
167 | = GLSampler | ||
168 | { samplerObject :: GLuint | ||
169 | } | ||
170 | |||
171 | data GLRenderTarget | ||
172 | = GLRenderTarget | ||
173 | { framebufferObject :: GLuint | ||
174 | , framebufferDrawbuffers :: Maybe [GLenum] | ||
175 | } | ||
176 | |||
177 | data GLCommand | ||
178 | = GLSetRasterContext !RasterContext | ||
179 | | GLSetAccumulationContext !AccumulationContext | ||
180 | | GLSetRenderTarget !GLuint !(Maybe [GLenum]) | ||
181 | | GLSetProgram !GLuint | ||
182 | | GLSetSamplerUniform !GLint !GLint (IORef GLint) -- sampler index, texture unit, IORef stores the actual texture unit mapping | ||
183 | | GLSetTexture !GLenum !GLuint !GLuint | ||
184 | | GLSetSampler !GLuint !GLuint | ||
185 | | GLRenderSlot !SlotName !ProgramName | ||
186 | | GLClearRenderTarget [(ImageSemantic,Value)] | ||
187 | | GLGenerateMipMap !GLenum !GLenum | ||
188 | | GLSaveImage FrameBufferComponent ImageRef -- from framebuffer component to texture (image) | ||
189 | | GLLoadImage ImageRef FrameBufferComponent -- from texture (image) to framebuffer component | ||
190 | deriving Show | ||
191 | |||
192 | instance Show (IORef GLint) where | ||
193 | show _ = "(IORef GLint)" | ||
194 | |||
195 | data GLObjectCommand | ||
196 | = GLSetUniform !GLint !GLUniform | ||
197 | | GLBindTexture !GLenum !(IORef GLint) !GLUniform -- binds the texture from the gluniform to the specified texture unit and target | ||
198 | | GLSetVertexAttribArray !GLuint !GLuint !GLint !GLenum !(Ptr ()) -- index buffer size type pointer | ||
199 | | GLSetVertexAttribIArray !GLuint !GLuint !GLint !GLenum !(Ptr ()) -- index buffer size type pointer | ||
200 | | GLSetVertexAttrib !GLuint !(Stream Buffer) -- index value | ||
201 | | GLDrawArrays !GLenum !GLint !GLsizei -- mode first count | ||
202 | | GLDrawElements !GLenum !GLsizei !GLenum !GLuint !(Ptr ()) -- mode count type buffer indicesPtr | ||
203 | deriving Show | ||
204 | |||
205 | type SetterFun a = a -> IO () | ||
206 | |||
207 | -- user will provide scalar input data via this type | ||
208 | data InputSetter | ||
209 | = SBool (SetterFun Bool) | ||
210 | | SV2B (SetterFun V2B) | ||
211 | | SV3B (SetterFun V3B) | ||
212 | | SV4B (SetterFun V4B) | ||
213 | | SWord (SetterFun Word32) | ||
214 | | SV2U (SetterFun V2U) | ||
215 | | SV3U (SetterFun V3U) | ||
216 | | SV4U (SetterFun V4U) | ||
217 | | SInt (SetterFun Int32) | ||
218 | | SV2I (SetterFun V2I) | ||
219 | | SV3I (SetterFun V3I) | ||
220 | | SV4I (SetterFun V4I) | ||
221 | | SFloat (SetterFun Float) | ||
222 | | SV2F (SetterFun V2F) | ||
223 | | SV3F (SetterFun V3F) | ||
224 | | SV4F (SetterFun V4F) | ||
225 | | SM22F (SetterFun M22F) | ||
226 | | SM23F (SetterFun M23F) | ||
227 | | SM24F (SetterFun M24F) | ||
228 | | SM32F (SetterFun M32F) | ||
229 | | SM33F (SetterFun M33F) | ||
230 | | SM34F (SetterFun M34F) | ||
231 | | SM42F (SetterFun M42F) | ||
232 | | SM43F (SetterFun M43F) | ||
233 | | SM44F (SetterFun M44F) | ||
234 | -- shadow textures | ||
235 | | SSTexture1D | ||
236 | | SSTexture2D | ||
237 | | SSTextureCube | ||
238 | | SSTexture1DArray | ||
239 | | SSTexture2DArray | ||
240 | | SSTexture2DRect | ||
241 | -- float textures | ||
242 | | SFTexture1D | ||
243 | | SFTexture2D (SetterFun TextureData) | ||
244 | | SFTexture3D | ||
245 | | SFTextureCube | ||
246 | | SFTexture1DArray | ||
247 | | SFTexture2DArray | ||
248 | | SFTexture2DMS | ||
249 | | SFTexture2DMSArray | ||
250 | | SFTextureBuffer | ||
251 | | SFTexture2DRect | ||
252 | -- int textures | ||
253 | | SITexture1D | ||
254 | | SITexture2D | ||
255 | | SITexture3D | ||
256 | | SITextureCube | ||
257 | | SITexture1DArray | ||
258 | | SITexture2DArray | ||
259 | | SITexture2DMS | ||
260 | | SITexture2DMSArray | ||
261 | | SITextureBuffer | ||
262 | | SITexture2DRect | ||
263 | -- uint textures | ||
264 | | SUTexture1D | ||
265 | | SUTexture2D | ||
266 | | SUTexture3D | ||
267 | | SUTextureCube | ||
268 | | SUTexture1DArray | ||
269 | | SUTexture2DArray | ||
270 | | SUTexture2DMS | ||
271 | | SUTexture2DMSArray | ||
272 | | SUTextureBuffer | ||
273 | | SUTexture2DRect | ||
274 | |||
275 | -- buffer handling | ||
276 | {- | ||
277 | user can fills a buffer (continuous memory region) | ||
278 | each buffer have a data descriptor, what describes the | ||
279 | buffer content. e.g. a buffer can contain more arrays of stream types | ||
280 | -} | ||
281 | |||
282 | -- user will provide stream data using this setup function | ||
283 | type BufferSetter = (Ptr () -> IO ()) -> IO () | ||
284 | |||
285 | -- specifies array component type (stream type in storage side) | ||
286 | -- this type can be overridden in GPU side, e.g ArrWord8 can be seen as TFloat or TWord also | ||
287 | data ArrayType | ||
288 | = ArrWord8 | ||
289 | | ArrWord16 | ||
290 | | ArrWord32 | ||
291 | | ArrInt8 | ||
292 | | ArrInt16 | ||
293 | | ArrInt32 | ||
294 | | ArrFloat | ||
295 | | ArrHalf -- Hint: half float is not supported in haskell | ||
296 | deriving (Show,Eq,Ord) | ||
297 | |||
298 | sizeOfArrayType :: ArrayType -> Int | ||
299 | sizeOfArrayType ArrWord8 = 1 | ||
300 | sizeOfArrayType ArrWord16 = 2 | ||
301 | sizeOfArrayType ArrWord32 = 4 | ||
302 | sizeOfArrayType ArrInt8 = 1 | ||
303 | sizeOfArrayType ArrInt16 = 2 | ||
304 | sizeOfArrayType ArrInt32 = 4 | ||
305 | sizeOfArrayType ArrFloat = 4 | ||
306 | sizeOfArrayType ArrHalf = 2 | ||
307 | |||
308 | -- describes an array in a buffer | ||
309 | data Array -- array type, element count (NOT byte size!), setter | ||
310 | = Array ArrayType Int BufferSetter | ||
311 | |||
312 | -- dev hint: this should be InputType | ||
313 | -- we restrict StreamType using type class | ||
314 | -- subset of InputType, describes a stream type (in GPU side) | ||
315 | data StreamType | ||
316 | = TWord | ||
317 | | TV2U | ||
318 | | TV3U | ||
319 | | TV4U | ||
320 | | TInt | ||
321 | | TV2I | ||
322 | | TV3I | ||
323 | | TV4I | ||
324 | | TFloat | ||
325 | | TV2F | ||
326 | | TV3F | ||
327 | | TV4F | ||
328 | | TM22F | ||
329 | | TM23F | ||
330 | | TM24F | ||
331 | | TM32F | ||
332 | | TM33F | ||
333 | | TM34F | ||
334 | | TM42F | ||
335 | | TM43F | ||
336 | | TM44F | ||
337 | deriving (Show,Eq,Ord) | ||
338 | |||
339 | toStreamType :: InputType -> Maybe StreamType | ||
340 | toStreamType Word = Just TWord | ||
341 | toStreamType V2U = Just TV2U | ||
342 | toStreamType V3U = Just TV3U | ||
343 | toStreamType V4U = Just TV4U | ||
344 | toStreamType Int = Just TInt | ||
345 | toStreamType V2I = Just TV2I | ||
346 | toStreamType V3I = Just TV3I | ||
347 | toStreamType V4I = Just TV4I | ||
348 | toStreamType Float = Just TFloat | ||
349 | toStreamType V2F = Just TV2F | ||
350 | toStreamType V3F = Just TV3F | ||
351 | toStreamType V4F = Just TV4F | ||
352 | toStreamType M22F = Just TM22F | ||
353 | toStreamType M23F = Just TM23F | ||
354 | toStreamType M24F = Just TM24F | ||
355 | toStreamType M32F = Just TM32F | ||
356 | toStreamType M33F = Just TM33F | ||
357 | toStreamType M34F = Just TM34F | ||
358 | toStreamType M42F = Just TM42F | ||
359 | toStreamType M43F = Just TM43F | ||
360 | toStreamType M44F = Just TM44F | ||
361 | toStreamType _ = Nothing | ||
362 | |||
363 | fromStreamType :: StreamType -> InputType | ||
364 | fromStreamType TWord = Word | ||
365 | fromStreamType TV2U = V2U | ||
366 | fromStreamType TV3U = V3U | ||
367 | fromStreamType TV4U = V4U | ||
368 | fromStreamType TInt = Int | ||
369 | fromStreamType TV2I = V2I | ||
370 | fromStreamType TV3I = V3I | ||
371 | fromStreamType TV4I = V4I | ||
372 | fromStreamType TFloat = Float | ||
373 | fromStreamType TV2F = V2F | ||
374 | fromStreamType TV3F = V3F | ||
375 | fromStreamType TV4F = V4F | ||
376 | fromStreamType TM22F = M22F | ||
377 | fromStreamType TM23F = M23F | ||
378 | fromStreamType TM24F = M24F | ||
379 | fromStreamType TM32F = M32F | ||
380 | fromStreamType TM33F = M33F | ||
381 | fromStreamType TM34F = M34F | ||
382 | fromStreamType TM42F = M42F | ||
383 | fromStreamType TM43F = M43F | ||
384 | fromStreamType TM44F = M44F | ||
385 | |||
386 | -- user can specify streams using Stream type | ||
387 | -- a stream can be constant (ConstXXX) or can came from a buffer | ||
388 | data Stream b | ||
389 | = ConstWord Word32 | ||
390 | | ConstV2U V2U | ||
391 | | ConstV3U V3U | ||
392 | | ConstV4U V4U | ||
393 | | ConstInt Int32 | ||
394 | | ConstV2I V2I | ||
395 | | ConstV3I V3I | ||
396 | | ConstV4I V4I | ||
397 | | ConstFloat Float | ||
398 | | ConstV2F V2F | ||
399 | | ConstV3F V3F | ||
400 | | ConstV4F V4F | ||
401 | | ConstM22F M22F | ||
402 | | ConstM23F M23F | ||
403 | | ConstM24F M24F | ||
404 | | ConstM32F M32F | ||
405 | | ConstM33F M33F | ||
406 | | ConstM34F M34F | ||
407 | | ConstM42F M42F | ||
408 | | ConstM43F M43F | ||
409 | | ConstM44F M44F | ||
410 | | Stream | ||
411 | { streamType :: StreamType | ||
412 | , streamBuffer :: b | ||
413 | , streamArrIdx :: Int | ||
414 | , streamStart :: Int | ||
415 | , streamLength :: Int | ||
416 | } | ||
417 | deriving Show | ||
418 | |||
419 | streamToStreamType :: Stream a -> StreamType | ||
420 | streamToStreamType s = case s of | ||
421 | ConstWord _ -> TWord | ||
422 | ConstV2U _ -> TV2U | ||
423 | ConstV3U _ -> TV3U | ||
424 | ConstV4U _ -> TV4U | ||
425 | ConstInt _ -> TInt | ||
426 | ConstV2I _ -> TV2I | ||
427 | ConstV3I _ -> TV3I | ||
428 | ConstV4I _ -> TV4I | ||
429 | ConstFloat _ -> TFloat | ||
430 | ConstV2F _ -> TV2F | ||
431 | ConstV3F _ -> TV3F | ||
432 | ConstV4F _ -> TV4F | ||
433 | ConstM22F _ -> TM22F | ||
434 | ConstM23F _ -> TM23F | ||
435 | ConstM24F _ -> TM24F | ||
436 | ConstM32F _ -> TM32F | ||
437 | ConstM33F _ -> TM33F | ||
438 | ConstM34F _ -> TM34F | ||
439 | ConstM42F _ -> TM42F | ||
440 | ConstM43F _ -> TM43F | ||
441 | ConstM44F _ -> TM44F | ||
442 | Stream t _ _ _ _ -> t | ||
443 | |||
444 | -- stream of index values (for index buffer) | ||
445 | data IndexStream b | ||
446 | = IndexStream | ||
447 | { indexBuffer :: b | ||
448 | , indexArrIdx :: Int | ||
449 | , indexStart :: Int | ||
450 | , indexLength :: Int | ||
451 | } | ||
452 | |||
453 | newtype TextureData | ||
454 | = TextureData | ||
455 | { textureObject :: GLuint | ||
456 | } | ||
457 | deriving Storable | ||
458 | |||
459 | data Primitive | ||
460 | = TriangleStrip | ||
461 | | TriangleList | ||
462 | | TriangleFan | ||
463 | | LineStrip | ||
464 | | LineList | ||
465 | | PointList | ||
466 | | TriangleStripAdjacency | ||
467 | | TriangleListAdjacency | ||
468 | | LineStripAdjacency | ||
469 | | LineListAdjacency | ||
470 | deriving (Eq,Ord,Bounded,Enum,Show) | ||
471 | |||
472 | type StreamSetter = Stream Buffer -> IO () | ||
473 | |||
474 | -- storable instances | ||
475 | instance Storable a => Storable (V2 a) where | ||
476 | sizeOf _ = 2 * sizeOf (undefined :: a) | ||
477 | alignment _ = sizeOf (undefined :: a) | ||
478 | |||
479 | peek q = do | ||
480 | let p = castPtr q :: Ptr a | ||
481 | k = sizeOf (undefined :: a) | ||
482 | x <- peek p | ||
483 | y <- peekByteOff p k | ||
484 | return $! (V2 x y) | ||
485 | |||
486 | poke q (V2 x y) = do | ||
487 | let p = castPtr q :: Ptr a | ||
488 | k = sizeOf (undefined :: a) | ||
489 | poke p x | ||
490 | pokeByteOff p k y | ||
491 | |||
492 | instance Storable a => Storable (V3 a) where | ||
493 | sizeOf _ = 3 * sizeOf (undefined :: a) | ||
494 | alignment _ = sizeOf (undefined :: a) | ||
495 | |||
496 | peek q = do | ||
497 | let p = castPtr q :: Ptr a | ||
498 | k = sizeOf (undefined :: a) | ||
499 | x <- peek p | ||
500 | y <- peekByteOff p k | ||
501 | z <- peekByteOff p (k*2) | ||
502 | return $! (V3 x y z) | ||
503 | |||
504 | poke q (V3 x y z) = do | ||
505 | let p = castPtr q :: Ptr a | ||
506 | k = sizeOf (undefined :: a) | ||
507 | poke p x | ||
508 | pokeByteOff p k y | ||
509 | pokeByteOff p (k*2) z | ||
510 | |||
511 | instance Storable a => Storable (V4 a) where | ||
512 | sizeOf _ = 4 * sizeOf (undefined :: a) | ||
513 | alignment _ = sizeOf (undefined :: a) | ||
514 | |||
515 | peek q = do | ||
516 | let p = castPtr q :: Ptr a | ||
517 | k = sizeOf (undefined :: a) | ||
518 | x <- peek p | ||
519 | y <- peekByteOff p k | ||
520 | z <- peekByteOff p (k*2) | ||
521 | w <- peekByteOff p (k*3) | ||
522 | return $! (V4 x y z w) | ||
523 | |||
524 | poke q (V4 x y z w) = do | ||
525 | let p = castPtr q :: Ptr a | ||
526 | k = sizeOf (undefined :: a) | ||
527 | poke p x | ||
528 | pokeByteOff p k y | ||
529 | pokeByteOff p (k*2) z | ||
530 | pokeByteOff p (k*3) w | ||
diff --git a/Backend/GL/Util.hs b/Backend/GL/Util.hs new file mode 100644 index 0000000..7a36290 --- /dev/null +++ b/Backend/GL/Util.hs | |||
@@ -0,0 +1,717 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | ||
2 | module Backend.GL.Util ( | ||
3 | queryUniforms, | ||
4 | queryStreams, | ||
5 | mkUniformSetter, | ||
6 | setUniform, | ||
7 | setVertexAttrib, | ||
8 | compileShader, | ||
9 | printProgramLog, | ||
10 | glGetShaderiv1, | ||
11 | glGetProgramiv1, | ||
12 | Buffer(..), | ||
13 | ArrayDesc(..), | ||
14 | StreamSetter, | ||
15 | streamToInputType, | ||
16 | arrayTypeToGLType, | ||
17 | comparisonFunctionToGLType, | ||
18 | logicOperationToGLType, | ||
19 | blendEquationToGLType, | ||
20 | blendingFactorToGLType, | ||
21 | checkGL, | ||
22 | textureDataTypeToGLType, | ||
23 | textureDataTypeToGLArityType, | ||
24 | glGetIntegerv1, | ||
25 | setSampler, | ||
26 | checkFBO, | ||
27 | compileTexture, | ||
28 | primitiveToFetchPrimitive, | ||
29 | primitiveToGLType, | ||
30 | inputTypeToTextureTarget, | ||
31 | toTrie | ||
32 | ) where | ||
33 | |||
34 | import Control.Applicative | ||
35 | import Control.Exception | ||
36 | import Control.Monad | ||
37 | import Data.ByteString.Char8 (ByteString,pack,unpack) | ||
38 | import Data.IORef | ||
39 | import Data.List as L | ||
40 | import Data.Trie as T | ||
41 | import Foreign | ||
42 | import qualified Data.ByteString.Char8 as SB | ||
43 | import qualified Data.Vector as V | ||
44 | import Data.Vector.Unboxed.Mutable (IOVector) | ||
45 | import qualified Data.Vector.Unboxed.Mutable as MV | ||
46 | import Data.Map (Map) | ||
47 | import qualified Data.Map as Map | ||
48 | |||
49 | import Graphics.Rendering.OpenGL.Raw.Core33 | ||
50 | import IR | ||
51 | import Backend.GL.Type | ||
52 | |||
53 | toTrie :: Map String a -> Trie a | ||
54 | toTrie m = T.fromList [(pack k,v) | (k,v) <- Map.toList m] | ||
55 | |||
56 | setSampler :: GLint -> Int32 -> IO () | ||
57 | setSampler i v = glUniform1i i $ fromIntegral v | ||
58 | |||
59 | z2 = V2 0 0 :: V2F | ||
60 | z3 = V3 0 0 0 :: V3F | ||
61 | z4 = V4 0 0 0 0 :: V4F | ||
62 | |||
63 | -- uniform functions | ||
64 | queryUniforms :: GLuint -> IO (Trie GLint, Trie InputType) | ||
65 | queryUniforms po = do | ||
66 | ul <- getNameTypeSize po glGetActiveUniform glGetUniformLocation gl_ACTIVE_UNIFORMS gl_ACTIVE_UNIFORM_MAX_LENGTH | ||
67 | let uNames = [n | (n,_,_,_) <- ul] | ||
68 | uTypes = [fromGLType (e,s) | (_,_,e,s) <- ul] | ||
69 | uLocation = [i | (_,i,_,_) <- ul] | ||
70 | return $! (T.fromList $! zip uNames uLocation, T.fromList $! zip uNames uTypes) | ||
71 | |||
72 | b2w :: Bool -> GLuint | ||
73 | b2w True = 1 | ||
74 | b2w False = 0 | ||
75 | |||
76 | mkUniformSetter :: InputType -> IO (GLUniform, InputSetter) | ||
77 | mkUniformSetter t@Bool = do {r <- newIORef 0; return $! (GLUniform t r, SBool $! writeIORef r . b2w)} | ||
78 | mkUniformSetter t@V2B = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2B $! writeIORef r . fmap b2w)} | ||
79 | mkUniformSetter t@V3B = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3B $! writeIORef r . fmap b2w)} | ||
80 | mkUniformSetter t@V4B = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4B $! writeIORef r . fmap b2w)} | ||
81 | mkUniformSetter t@Word = do {r <- newIORef 0; return $! (GLUniform t r, SWord $! writeIORef r)} | ||
82 | mkUniformSetter t@V2U = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2U $! writeIORef r)} | ||
83 | mkUniformSetter t@V3U = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3U $! writeIORef r)} | ||
84 | mkUniformSetter t@V4U = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4U $! writeIORef r)} | ||
85 | mkUniformSetter t@Int = do {r <- newIORef 0; return $! (GLUniform t r, SInt $! writeIORef r)} | ||
86 | mkUniformSetter t@V2I = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2I $! writeIORef r)} | ||
87 | mkUniformSetter t@V3I = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3I $! writeIORef r)} | ||
88 | mkUniformSetter t@V4I = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4I $! writeIORef r)} | ||
89 | mkUniformSetter t@Float = do {r <- newIORef 0; return $! (GLUniform t r, SFloat $! writeIORef r)} | ||
90 | mkUniformSetter t@V2F = do {r <- newIORef (V2 0 0); return $! (GLUniform t r, SV2F $! writeIORef r)} | ||
91 | mkUniformSetter t@V3F = do {r <- newIORef (V3 0 0 0); return $! (GLUniform t r, SV3F $! writeIORef r)} | ||
92 | mkUniformSetter t@V4F = do {r <- newIORef (V4 0 0 0 0); return $! (GLUniform t r, SV4F $! writeIORef r)} | ||
93 | mkUniformSetter t@M22F = do {r <- newIORef (V2 z2 z2); return $! (GLUniform t r, SM22F $! writeIORef r)} | ||
94 | mkUniformSetter t@M23F = do {r <- newIORef (V3 z2 z2 z2); return $! (GLUniform t r, SM23F $! writeIORef r)} | ||
95 | mkUniformSetter t@M24F = do {r <- newIORef (V4 z2 z2 z2 z2); return $! (GLUniform t r, SM24F $! writeIORef r)} | ||
96 | mkUniformSetter t@M32F = do {r <- newIORef (V2 z3 z3); return $! (GLUniform t r, SM32F $! writeIORef r)} | ||
97 | mkUniformSetter t@M33F = do {r <- newIORef (V3 z3 z3 z3); return $! (GLUniform t r, SM33F $! writeIORef r)} | ||
98 | mkUniformSetter t@M34F = do {r <- newIORef (V4 z3 z3 z3 z3); return $! (GLUniform t r, SM34F $! writeIORef r)} | ||
99 | mkUniformSetter t@M42F = do {r <- newIORef (V2 z4 z4); return $! (GLUniform t r, SM42F $! writeIORef r)} | ||
100 | mkUniformSetter t@M43F = do {r <- newIORef (V3 z4 z4 z4); return $! (GLUniform t r, SM43F $! writeIORef r)} | ||
101 | mkUniformSetter t@M44F = do {r <- newIORef (V4 z4 z4 z4 z4); return $! (GLUniform t r, SM44F $! writeIORef r)} | ||
102 | mkUniformSetter t@FTexture2D = do {r <- newIORef (TextureData 0); return $! (GLUniform t r, SFTexture2D $! writeIORef r)} | ||
103 | |||
104 | -- sets value based uniforms only (does not handle textures) | ||
105 | setUniform :: Storable a => GLint -> InputType -> IORef a -> IO () | ||
106 | setUniform i ty ref = do | ||
107 | v <- readIORef ref | ||
108 | let false = fromIntegral gl_FALSE | ||
109 | with v $ \p -> case ty of | ||
110 | Bool -> glUniform1uiv i 1 (castPtr p) | ||
111 | V2B -> glUniform2uiv i 1 (castPtr p) | ||
112 | V3B -> glUniform3uiv i 1 (castPtr p) | ||
113 | V4B -> glUniform4uiv i 1 (castPtr p) | ||
114 | Word -> glUniform1uiv i 1 (castPtr p) | ||
115 | V2U -> glUniform2uiv i 1 (castPtr p) | ||
116 | V3U -> glUniform3uiv i 1 (castPtr p) | ||
117 | V4U -> glUniform4uiv i 1 (castPtr p) | ||
118 | Int -> glUniform1iv i 1 (castPtr p) | ||
119 | V2I -> glUniform2iv i 1 (castPtr p) | ||
120 | V3I -> glUniform3iv i 1 (castPtr p) | ||
121 | V4I -> glUniform4iv i 1 (castPtr p) | ||
122 | Float -> glUniform1fv i 1 (castPtr p) | ||
123 | V2F -> glUniform2fv i 1 (castPtr p) | ||
124 | V3F -> glUniform3fv i 1 (castPtr p) | ||
125 | V4F -> glUniform4fv i 1 (castPtr p) | ||
126 | M22F -> glUniformMatrix2fv i 1 false (castPtr p) | ||
127 | M23F -> glUniformMatrix2x3fv i 1 false (castPtr p) | ||
128 | M24F -> glUniformMatrix2x4fv i 1 false (castPtr p) | ||
129 | M32F -> glUniformMatrix3x2fv i 1 false (castPtr p) | ||
130 | M33F -> glUniformMatrix3fv i 1 false (castPtr p) | ||
131 | M34F -> glUniformMatrix3x4fv i 1 false (castPtr p) | ||
132 | M42F -> glUniformMatrix4x2fv i 1 false (castPtr p) | ||
133 | M43F -> glUniformMatrix4x3fv i 1 false (castPtr p) | ||
134 | M44F -> glUniformMatrix4fv i 1 false (castPtr p) | ||
135 | _ -> fail "internal error (setUniform)!" | ||
136 | |||
137 | -- attribute functions | ||
138 | queryStreams :: GLuint -> IO (Trie GLuint, Trie InputType) | ||
139 | queryStreams po = do | ||
140 | al <- getNameTypeSize po glGetActiveAttrib glGetAttribLocation gl_ACTIVE_ATTRIBUTES gl_ACTIVE_ATTRIBUTE_MAX_LENGTH | ||
141 | let aNames = [n | (n,_,_,_) <- al] | ||
142 | aTypes = [fromGLType (e,s) | (_,_,e,s) <- al] | ||
143 | aLocation = [fromIntegral i | (_,i,_,_) <- al] | ||
144 | return $! (T.fromList $! zip aNames aLocation, T.fromList $! zip aNames aTypes) | ||
145 | |||
146 | arrayTypeToGLType :: ArrayType -> GLenum | ||
147 | arrayTypeToGLType a = case a of | ||
148 | ArrWord8 -> gl_UNSIGNED_BYTE | ||
149 | ArrWord16 -> gl_UNSIGNED_SHORT | ||
150 | ArrWord32 -> gl_UNSIGNED_INT | ||
151 | ArrInt8 -> gl_BYTE | ||
152 | ArrInt16 -> gl_SHORT | ||
153 | ArrInt32 -> gl_INT | ||
154 | ArrFloat -> gl_FLOAT | ||
155 | ArrHalf -> gl_HALF_FLOAT | ||
156 | |||
157 | setVertexAttrib :: GLuint -> Stream Buffer -> IO () | ||
158 | setVertexAttrib i val = case val of | ||
159 | ConstWord v -> with v $! \p -> glVertexAttribI1uiv i $! castPtr p | ||
160 | ConstV2U v -> with v $! \p -> glVertexAttribI2uiv i $! castPtr p | ||
161 | ConstV3U v -> with v $! \p -> glVertexAttribI3uiv i $! castPtr p | ||
162 | ConstV4U v -> with v $! \p -> glVertexAttribI4uiv i $! castPtr p | ||
163 | ConstInt v -> with v $! \p -> glVertexAttribI1iv i $! castPtr p | ||
164 | ConstV2I v -> with v $! \p -> glVertexAttribI2iv i $! castPtr p | ||
165 | ConstV3I v -> with v $! \p -> glVertexAttribI3iv i $! castPtr p | ||
166 | ConstV4I v -> with v $! \p -> glVertexAttribI4iv i $! castPtr p | ||
167 | ConstFloat v -> setAFloat i v | ||
168 | ConstV2F v -> setAV2F i v | ||
169 | ConstV3F v -> setAV3F i v | ||
170 | ConstV4F v -> setAV4F i v | ||
171 | ConstM22F (V2 x y) -> setAV2F i x >> setAV2F (i+1) y | ||
172 | ConstM23F (V3 x y z) -> setAV2F i x >> setAV2F (i+1) y >> setAV2F (i+2) z | ||
173 | ConstM24F (V4 x y z w) -> setAV2F i x >> setAV2F (i+1) y >> setAV2F (i+2) z >> setAV2F (i+3) w | ||
174 | ConstM32F (V2 x y) -> setAV3F i x >> setAV3F (i+1) y | ||
175 | ConstM33F (V3 x y z) -> setAV3F i x >> setAV3F (i+1) y >> setAV3F (i+2) z | ||
176 | ConstM34F (V4 x y z w) -> setAV3F i x >> setAV3F (i+1) y >> setAV3F (i+2) z >> setAV3F (i+3) w | ||
177 | ConstM42F (V2 x y) -> setAV4F i x >> setAV4F (i+1) y | ||
178 | ConstM43F (V3 x y z) -> setAV4F i x >> setAV4F (i+1) y >> setAV4F (i+2) z | ||
179 | ConstM44F (V4 x y z w) -> setAV4F i x >> setAV4F (i+1) y >> setAV4F (i+2) z >> setAV4F (i+3) w | ||
180 | _ -> fail "internal error (setVertexAttrib)!" | ||
181 | |||
182 | setAFloat :: GLuint -> Float -> IO () | ||
183 | setAV2F :: GLuint -> V2F -> IO () | ||
184 | setAV3F :: GLuint -> V3F -> IO () | ||
185 | setAV4F :: GLuint -> V4F -> IO () | ||
186 | setAFloat i v = with v $! \p -> glVertexAttrib1fv i $! castPtr p | ||
187 | setAV2F i v = with v $! \p -> glVertexAttrib2fv i $! castPtr p | ||
188 | setAV3F i v = with v $! \p -> glVertexAttrib3fv i $! castPtr p | ||
189 | setAV4F i v = with v $! \p -> glVertexAttrib4fv i $! castPtr p | ||
190 | |||
191 | -- result list: [(name string,location,gl type,component count)] | ||
192 | getNameTypeSize :: GLuint -> (GLuint -> GLuint -> GLsizei -> Ptr GLsizei -> Ptr GLint -> Ptr GLenum -> Ptr GLchar -> IO ()) | ||
193 | -> (GLuint -> Ptr GLchar -> IO GLint) -> GLenum -> GLenum -> IO [(ByteString,GLint,GLenum,GLint)] | ||
194 | getNameTypeSize o f g enum enumLen = do | ||
195 | nameLen <- glGetProgramiv1 enumLen o | ||
196 | allocaArray (fromIntegral nameLen) $! \namep -> alloca $! \sizep -> alloca $! \typep -> do | ||
197 | n <- glGetProgramiv1 enum o | ||
198 | forM [0..n-1] $! \i -> f o (fromIntegral i) (fromIntegral nameLen) nullPtr sizep typep namep >> | ||
199 | (,,,) <$> SB.packCString (castPtr namep) <*> g o namep <*> peek typep <*> peek sizep | ||
200 | |||
201 | fromGLType :: (GLenum,GLint) -> InputType | ||
202 | fromGLType (t,1) | ||
203 | | t == gl_BOOL = Bool | ||
204 | | t == gl_BOOL_VEC2 = V2B | ||
205 | | t == gl_BOOL_VEC3 = V3B | ||
206 | | t == gl_BOOL_VEC4 = V4B | ||
207 | | t == gl_UNSIGNED_INT = Word | ||
208 | | t == gl_UNSIGNED_INT_VEC2 = V2U | ||
209 | | t == gl_UNSIGNED_INT_VEC3 = V3U | ||
210 | | t == gl_UNSIGNED_INT_VEC4 = V4U | ||
211 | | t == gl_INT = Int | ||
212 | | t == gl_INT_VEC2 = V2I | ||
213 | | t == gl_INT_VEC3 = V3I | ||
214 | | t == gl_INT_VEC4 = V4I | ||
215 | | t == gl_FLOAT = Float | ||
216 | | t == gl_FLOAT_VEC2 = V2F | ||
217 | | t == gl_FLOAT_VEC3 = V3F | ||
218 | | t == gl_FLOAT_VEC4 = V4F | ||
219 | | t == gl_FLOAT_MAT2 = M22F | ||
220 | | t == gl_FLOAT_MAT2x3 = M23F | ||
221 | | t == gl_FLOAT_MAT2x4 = M24F | ||
222 | | t == gl_FLOAT_MAT3x2 = M32F | ||
223 | | t == gl_FLOAT_MAT3 = M33F | ||
224 | | t == gl_FLOAT_MAT3x4 = M34F | ||
225 | | t == gl_FLOAT_MAT4x2 = M42F | ||
226 | | t == gl_FLOAT_MAT4x3 = M43F | ||
227 | | t == gl_FLOAT_MAT4 = M44F | ||
228 | | t == gl_SAMPLER_1D_ARRAY_SHADOW = STexture1DArray | ||
229 | | t == gl_SAMPLER_1D_SHADOW = STexture1D | ||
230 | | t == gl_SAMPLER_2D_ARRAY_SHADOW = STexture2DArray | ||
231 | | t == gl_SAMPLER_2D_RECT_SHADOW = STexture2DRect | ||
232 | | t == gl_SAMPLER_2D_SHADOW = STexture2D | ||
233 | | t == gl_SAMPLER_CUBE_SHADOW = STextureCube | ||
234 | | t == gl_INT_SAMPLER_1D = ITexture1D | ||
235 | | t == gl_INT_SAMPLER_1D_ARRAY = ITexture1DArray | ||
236 | | t == gl_INT_SAMPLER_2D = ITexture2D | ||
237 | | t == gl_INT_SAMPLER_2D_ARRAY = ITexture2DArray | ||
238 | | t == gl_INT_SAMPLER_2D_MULTISAMPLE = ITexture2DMS | ||
239 | | t == gl_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = ITexture2DMSArray | ||
240 | | t == gl_INT_SAMPLER_2D_RECT = ITexture2DRect | ||
241 | | t == gl_INT_SAMPLER_3D = ITexture3D | ||
242 | | t == gl_INT_SAMPLER_BUFFER = ITextureBuffer | ||
243 | | t == gl_INT_SAMPLER_CUBE = ITextureCube | ||
244 | | t == gl_SAMPLER_1D = FTexture1D | ||
245 | | t == gl_SAMPLER_1D_ARRAY = FTexture1DArray | ||
246 | | t == gl_SAMPLER_2D = FTexture2D | ||
247 | | t == gl_SAMPLER_2D_ARRAY = FTexture2DArray | ||
248 | | t == gl_SAMPLER_2D_MULTISAMPLE = FTexture2DMS | ||
249 | | t == gl_SAMPLER_2D_MULTISAMPLE_ARRAY = FTexture2DMSArray | ||
250 | | t == gl_SAMPLER_2D_RECT = FTexture2DRect | ||
251 | | t == gl_SAMPLER_3D = FTexture3D | ||
252 | | t == gl_SAMPLER_BUFFER = FTextureBuffer | ||
253 | | t == gl_SAMPLER_CUBE = FTextureCube | ||
254 | | t == gl_UNSIGNED_INT_SAMPLER_1D = UTexture1D | ||
255 | | t == gl_UNSIGNED_INT_SAMPLER_1D_ARRAY = UTexture1DArray | ||
256 | | t == gl_UNSIGNED_INT_SAMPLER_2D = UTexture2D | ||
257 | | t == gl_UNSIGNED_INT_SAMPLER_2D_ARRAY = UTexture2DArray | ||
258 | | t == gl_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE = UTexture2DMS | ||
259 | | t == gl_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY = UTexture2DMSArray | ||
260 | | t == gl_UNSIGNED_INT_SAMPLER_2D_RECT = UTexture2DRect | ||
261 | | t == gl_UNSIGNED_INT_SAMPLER_3D = UTexture3D | ||
262 | | t == gl_UNSIGNED_INT_SAMPLER_BUFFER = UTextureBuffer | ||
263 | | t == gl_UNSIGNED_INT_SAMPLER_CUBE = UTextureCube | ||
264 | | otherwise = error "Failed fromGLType" | ||
265 | fromGLUniformType _ = error "Failed fromGLType" | ||
266 | |||
267 | printShaderLog :: GLuint -> IO () | ||
268 | printShaderLog o = do | ||
269 | i <- glGetShaderiv1 gl_INFO_LOG_LENGTH o | ||
270 | when (i > 0) $ | ||
271 | alloca $ \sizePtr -> allocaArray (fromIntegral i) $! \ps -> do | ||
272 | glGetShaderInfoLog o (fromIntegral i) sizePtr ps | ||
273 | size <- peek sizePtr | ||
274 | log <- SB.packCStringLen (castPtr ps, fromIntegral size) | ||
275 | SB.putStrLn log | ||
276 | |||
277 | glGetShaderiv1 :: GLenum -> GLuint -> IO GLint | ||
278 | glGetShaderiv1 pname o = alloca $! \pi -> glGetShaderiv o pname pi >> peek pi | ||
279 | |||
280 | glGetProgramiv1 :: GLenum -> GLuint -> IO GLint | ||
281 | glGetProgramiv1 pname o = alloca $! \pi -> glGetProgramiv o pname pi >> peek pi | ||
282 | |||
283 | printProgramLog :: GLuint -> IO () | ||
284 | printProgramLog o = do | ||
285 | i <- glGetProgramiv1 gl_INFO_LOG_LENGTH o | ||
286 | when (i > 0) $ | ||
287 | alloca $ \sizePtr -> allocaArray (fromIntegral i) $! \ps -> do | ||
288 | glGetProgramInfoLog o (fromIntegral i) sizePtr ps | ||
289 | size <- peek sizePtr | ||
290 | log <- SB.packCStringLen (castPtr ps, fromIntegral size) | ||
291 | SB.putStrLn log | ||
292 | |||
293 | compileShader :: GLuint -> [ByteString] -> IO () | ||
294 | compileShader o srcl = withMany SB.useAsCString srcl $! \l -> withArray l $! \p -> do | ||
295 | glShaderSource o (fromIntegral $! length srcl) (castPtr p) nullPtr | ||
296 | glCompileShader o | ||
297 | printShaderLog o | ||
298 | status <- glGetShaderiv1 gl_COMPILE_STATUS o | ||
299 | when (status /= fromIntegral gl_TRUE) $ fail "compileShader failed!" | ||
300 | |||
301 | checkGL :: IO ByteString | ||
302 | checkGL = do | ||
303 | let f e | e == gl_INVALID_ENUM = "INVALID_ENUM" | ||
304 | | e == gl_INVALID_VALUE = "INVALID_VALUE" | ||
305 | | e == gl_INVALID_OPERATION = "INVALID_OPERATION" | ||
306 | | e == gl_INVALID_FRAMEBUFFER_OPERATION = "INVALID_FRAMEBUFFER_OPERATION" | ||
307 | | e == gl_OUT_OF_MEMORY = "OUT_OF_MEMORY" | ||
308 | | e == gl_NO_ERROR = "OK" | ||
309 | | otherwise = "Unknown error" | ||
310 | e <- glGetError | ||
311 | return $ f e | ||
312 | |||
313 | streamToInputType :: Stream Buffer -> InputType | ||
314 | streamToInputType s = case s of | ||
315 | ConstWord _ -> Word | ||
316 | ConstV2U _ -> V2U | ||
317 | ConstV3U _ -> V3U | ||
318 | ConstV4U _ -> V4U | ||
319 | ConstInt _ -> Int | ||
320 | ConstV2I _ -> V2I | ||
321 | ConstV3I _ -> V3I | ||
322 | ConstV4I _ -> V4I | ||
323 | ConstFloat _ -> Float | ||
324 | ConstV2F _ -> V2F | ||
325 | ConstV3F _ -> V3F | ||
326 | ConstV4F _ -> V4F | ||
327 | ConstM22F _ -> M22F | ||
328 | ConstM23F _ -> M23F | ||
329 | ConstM24F _ -> M24F | ||
330 | ConstM32F _ -> M32F | ||
331 | ConstM33F _ -> M33F | ||
332 | ConstM34F _ -> M34F | ||
333 | ConstM42F _ -> M42F | ||
334 | ConstM43F _ -> M43F | ||
335 | ConstM44F _ -> M44F | ||
336 | Stream t (Buffer a _) i _ _ | ||
337 | | 0 <= i && i < V.length a && | ||
338 | if elem t integralTypes then elem at integralArrTypes else True | ||
339 | -> fromStreamType t | ||
340 | | otherwise -> throw $ userError "streamToInputType failed" | ||
341 | where | ||
342 | at = arrType $! (a V.! i) | ||
343 | integralTypes = [TWord, TV2U, TV3U, TV4U, TInt, TV2I, TV3I, TV4I] | ||
344 | integralArrTypes = [ArrWord8, ArrWord16, ArrWord32, ArrInt8, ArrInt16, ArrInt32] | ||
345 | |||
346 | comparisonFunctionToGLType :: ComparisonFunction -> GLenum | ||
347 | comparisonFunctionToGLType a = case a of | ||
348 | Always -> gl_ALWAYS | ||
349 | Equal -> gl_EQUAL | ||
350 | Gequal -> gl_GEQUAL | ||
351 | Greater -> gl_GREATER | ||
352 | Lequal -> gl_LEQUAL | ||
353 | Less -> gl_LESS | ||
354 | Never -> gl_NEVER | ||
355 | Notequal -> gl_NOTEQUAL | ||
356 | |||
357 | logicOperationToGLType :: LogicOperation -> GLenum | ||
358 | logicOperationToGLType a = case a of | ||
359 | And -> gl_AND | ||
360 | AndInverted -> gl_AND_INVERTED | ||
361 | AndReverse -> gl_AND_REVERSE | ||
362 | Clear -> gl_CLEAR | ||
363 | Copy -> gl_COPY | ||
364 | CopyInverted -> gl_COPY_INVERTED | ||
365 | Equiv -> gl_EQUIV | ||
366 | Invert -> gl_INVERT | ||
367 | Nand -> gl_NAND | ||
368 | Noop -> gl_NOOP | ||
369 | Nor -> gl_NOR | ||
370 | Or -> gl_OR | ||
371 | OrInverted -> gl_OR_INVERTED | ||
372 | OrReverse -> gl_OR_REVERSE | ||
373 | Set -> gl_SET | ||
374 | Xor -> gl_XOR | ||
375 | |||
376 | blendEquationToGLType :: BlendEquation -> GLenum | ||
377 | blendEquationToGLType a = case a of | ||
378 | FuncAdd -> gl_FUNC_ADD | ||
379 | FuncReverseSubtract -> gl_FUNC_REVERSE_SUBTRACT | ||
380 | FuncSubtract -> gl_FUNC_SUBTRACT | ||
381 | Max -> gl_MAX | ||
382 | Min -> gl_MIN | ||
383 | |||
384 | blendingFactorToGLType :: BlendingFactor -> GLenum | ||
385 | blendingFactorToGLType a = case a of | ||
386 | ConstantAlpha -> gl_CONSTANT_ALPHA | ||
387 | ConstantColor -> gl_CONSTANT_COLOR | ||
388 | DstAlpha -> gl_DST_ALPHA | ||
389 | DstColor -> gl_DST_COLOR | ||
390 | One -> gl_ONE | ||
391 | OneMinusConstantAlpha -> gl_ONE_MINUS_CONSTANT_ALPHA | ||
392 | OneMinusConstantColor -> gl_ONE_MINUS_CONSTANT_COLOR | ||
393 | OneMinusDstAlpha -> gl_ONE_MINUS_DST_ALPHA | ||
394 | OneMinusDstColor -> gl_ONE_MINUS_DST_COLOR | ||
395 | OneMinusSrcAlpha -> gl_ONE_MINUS_SRC_ALPHA | ||
396 | OneMinusSrcColor -> gl_ONE_MINUS_SRC_COLOR | ||
397 | SrcAlpha -> gl_SRC_ALPHA | ||
398 | SrcAlphaSaturate -> gl_SRC_ALPHA_SATURATE | ||
399 | SrcColor -> gl_SRC_COLOR | ||
400 | Zero -> gl_ZERO | ||
401 | |||
402 | textureDataTypeToGLType :: ImageSemantic -> TextureDataType -> GLenum | ||
403 | textureDataTypeToGLType Color a = case a of | ||
404 | FloatT Red -> gl_R32F | ||
405 | IntT Red -> gl_R32I | ||
406 | WordT Red -> gl_R32UI | ||
407 | FloatT RG -> gl_RG32F | ||
408 | IntT RG -> gl_RG32I | ||
409 | WordT RG -> gl_RG32UI | ||
410 | FloatT RGBA -> gl_RGBA32F | ||
411 | IntT RGBA -> gl_RGBA32I | ||
412 | WordT RGBA -> gl_RGBA32UI | ||
413 | a -> error $ "FIXME: This texture format is not yet supported" ++ show a | ||
414 | textureDataTypeToGLType Depth a = case a of | ||
415 | FloatT Red -> gl_DEPTH_COMPONENT32F | ||
416 | WordT Red -> gl_DEPTH_COMPONENT32 | ||
417 | a -> error $ "FIXME: This texture format is not yet supported" ++ show a | ||
418 | textureDataTypeToGLType Stencil a = case a of | ||
419 | a -> error $ "FIXME: This texture format is not yet supported" ++ show a | ||
420 | |||
421 | textureDataTypeToGLArityType :: ImageSemantic -> TextureDataType -> GLenum | ||
422 | textureDataTypeToGLArityType Color a = case a of | ||
423 | FloatT Red -> gl_RED | ||
424 | IntT Red -> gl_RED | ||
425 | WordT Red -> gl_RED | ||
426 | FloatT RG -> gl_RG | ||
427 | IntT RG -> gl_RG | ||
428 | WordT RG -> gl_RG | ||
429 | FloatT RGBA -> gl_RGBA | ||
430 | IntT RGBA -> gl_RGBA | ||
431 | WordT RGBA -> gl_RGBA | ||
432 | a -> error $ "FIXME: This texture format is not yet supported" ++ show a | ||
433 | textureDataTypeToGLArityType Depth a = case a of | ||
434 | FloatT Red -> gl_DEPTH_COMPONENT | ||
435 | WordT Red -> gl_DEPTH_COMPONENT | ||
436 | a -> error $ "FIXME: This texture format is not yet supported" ++ show a | ||
437 | textureDataTypeToGLArityType Stencil a = case a of | ||
438 | a -> error $ "FIXME: This texture format is not yet supported" ++ show a | ||
439 | {- | ||
440 | Texture and renderbuffer color formats (R): | ||
441 | R11F_G11F_B10F | ||
442 | R16 | ||
443 | R16F | ||
444 | R16I | ||
445 | R16UI | ||
446 | R32F | ||
447 | R32I | ||
448 | R32UI | ||
449 | R8 | ||
450 | R8I | ||
451 | R8UI | ||
452 | RG16 | ||
453 | RG16F | ||
454 | RG16I | ||
455 | RG16UI | ||
456 | RG32F | ||
457 | RG32I | ||
458 | RG32UI | ||
459 | RG8 | ||
460 | RG8I | ||
461 | RG8UI | ||
462 | RGB10_A2 | ||
463 | RGB10_A2UI | ||
464 | RGBA16 | ||
465 | RGBA16F | ||
466 | RGBA16I | ||
467 | RGBA16UI | ||
468 | RGBA32F | ||
469 | RGBA32I | ||
470 | RGBA32UI | ||
471 | RGBA8 | ||
472 | RGBA8I | ||
473 | RGBA8UI | ||
474 | SRGB8_ALPHA8 | ||
475 | -} | ||
476 | |||
477 | glGetIntegerv1 :: GLenum -> IO GLint | ||
478 | glGetIntegerv1 e = alloca $ \pi -> glGetIntegerv e pi >> peek pi | ||
479 | |||
480 | checkFBO :: IO ByteString | ||
481 | checkFBO = do | ||
482 | let f e | e == gl_FRAMEBUFFER_UNDEFINED = "FRAMEBUFFER_UNDEFINED" | ||
483 | | e == gl_FRAMEBUFFER_INCOMPLETE_ATTACHMENT = "FRAMEBUFFER_INCOMPLETE_ATTACHMENT" | ||
484 | | e == gl_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER = "FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER" | ||
485 | | e == gl_FRAMEBUFFER_INCOMPLETE_READ_BUFFER = "FRAMEBUFFER_INCOMPLETE_READ_BUFFER" | ||
486 | | e == gl_FRAMEBUFFER_UNSUPPORTED = "FRAMEBUFFER_UNSUPPORTED" | ||
487 | | e == gl_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE = "FRAMEBUFFER_INCOMPLETE_MULTISAMPLE" | ||
488 | | e == gl_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS = "FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS" | ||
489 | | e == gl_FRAMEBUFFER_COMPLETE = "FRAMEBUFFER_COMPLETE" | ||
490 | | otherwise = "Unknown error" | ||
491 | e <- glCheckFramebufferStatus gl_DRAW_FRAMEBUFFER | ||
492 | return $ f e | ||
493 | |||
494 | filterToGLType :: Filter -> GLenum | ||
495 | filterToGLType a = case a of | ||
496 | Nearest -> gl_NEAREST | ||
497 | Linear -> gl_LINEAR | ||
498 | NearestMipmapNearest -> gl_NEAREST_MIPMAP_NEAREST | ||
499 | NearestMipmapLinear -> gl_NEAREST_MIPMAP_LINEAR | ||
500 | LinearMipmapNearest -> gl_LINEAR_MIPMAP_NEAREST | ||
501 | LinearMipmapLinear -> gl_LINEAR_MIPMAP_LINEAR | ||
502 | |||
503 | edgeModeToGLType :: EdgeMode -> GLenum | ||
504 | edgeModeToGLType a = case a of | ||
505 | Repeat -> gl_REPEAT | ||
506 | MirroredRepeat -> gl_MIRRORED_REPEAT | ||
507 | ClampToEdge -> gl_CLAMP_TO_EDGE | ||
508 | ClampToBorder -> gl_CLAMP_TO_BORDER | ||
509 | |||
510 | setTextureSamplerParameters :: GLenum -> SamplerDescriptor -> IO () | ||
511 | setTextureSamplerParameters t s = do | ||
512 | glTexParameteri t gl_TEXTURE_WRAP_S $ fromIntegral $ edgeModeToGLType $ samplerWrapS s | ||
513 | case samplerWrapT s of | ||
514 | Nothing -> return () | ||
515 | Just a -> glTexParameteri t gl_TEXTURE_WRAP_T $ fromIntegral $ edgeModeToGLType a | ||
516 | case samplerWrapR s of | ||
517 | Nothing -> return () | ||
518 | Just a -> glTexParameteri t gl_TEXTURE_WRAP_R $ fromIntegral $ edgeModeToGLType a | ||
519 | glTexParameteri t gl_TEXTURE_MIN_FILTER $ fromIntegral $ filterToGLType $ samplerMinFilter s | ||
520 | glTexParameteri t gl_TEXTURE_MAG_FILTER $ fromIntegral $ filterToGLType $ samplerMagFilter s | ||
521 | |||
522 | let setBColorV4F a = with a $ \p -> glTexParameterfv t gl_TEXTURE_BORDER_COLOR $ castPtr p | ||
523 | setBColorV4I a = with a $ \p -> glTexParameterIiv t gl_TEXTURE_BORDER_COLOR $ castPtr p | ||
524 | setBColorV4U a = with a $ \p -> glTexParameterIuiv t gl_TEXTURE_BORDER_COLOR $ castPtr p | ||
525 | case samplerBorderColor s of | ||
526 | -- float, word, int, red, rg, rgb, rgba | ||
527 | VFloat a -> setBColorV4F $ V4 a 0 0 0 | ||
528 | VV2F (V2 a b) -> setBColorV4F $ V4 a b 0 0 | ||
529 | VV3F (V3 a b c) -> setBColorV4F $ V4 a b c 0 | ||
530 | VV4F a -> setBColorV4F a | ||
531 | |||
532 | VInt a -> setBColorV4I $ V4 a 0 0 0 | ||
533 | VV2I (V2 a b) -> setBColorV4I $ V4 a b 0 0 | ||
534 | VV3I (V3 a b c) -> setBColorV4I $ V4 a b c 0 | ||
535 | VV4I a -> setBColorV4I a | ||
536 | |||
537 | VWord a -> setBColorV4U $ V4 a 0 0 0 | ||
538 | VV2U (V2 a b) -> setBColorV4U $ V4 a b 0 0 | ||
539 | VV3U (V3 a b c) -> setBColorV4U $ V4 a b c 0 | ||
540 | VV4U a -> setBColorV4U a | ||
541 | _ -> fail "internal error (setTextureSamplerParameters)!" | ||
542 | |||
543 | case samplerMinLod s of | ||
544 | Nothing -> return () | ||
545 | Just a -> glTexParameterf t gl_TEXTURE_MIN_LOD $ realToFrac a | ||
546 | case samplerMaxLod s of | ||
547 | Nothing -> return () | ||
548 | Just a -> glTexParameterf t gl_TEXTURE_MAX_LOD $ realToFrac a | ||
549 | glTexParameterf t gl_TEXTURE_LOD_BIAS $ realToFrac $ samplerLodBias s | ||
550 | case samplerCompareFunc s of | ||
551 | Nothing -> glTexParameteri t gl_TEXTURE_COMPARE_MODE $ fromIntegral gl_NONE | ||
552 | Just a -> do | ||
553 | glTexParameteri t gl_TEXTURE_COMPARE_MODE $ fromIntegral gl_COMPARE_REF_TO_TEXTURE | ||
554 | glTexParameteri t gl_TEXTURE_COMPARE_FUNC $ fromIntegral $ comparisonFunctionToGLType a | ||
555 | |||
556 | compileTexture :: TextureDescriptor -> IO GLTexture | ||
557 | compileTexture txDescriptor = do | ||
558 | to <- alloca $! \pto -> glGenTextures 1 pto >> peek pto | ||
559 | let TextureDescriptor | ||
560 | { textureType = txType | ||
561 | , textureSize = txSize | ||
562 | , textureSemantic = txSemantic | ||
563 | , textureSampler = txSampler | ||
564 | , textureBaseLevel = txBaseLevel | ||
565 | , textureMaxLevel = txMaxLevel | ||
566 | } = txDescriptor | ||
567 | |||
568 | txSetup txTarget dTy = do | ||
569 | let internalFormat = fromIntegral $ textureDataTypeToGLType txSemantic dTy | ||
570 | dataFormat = fromIntegral $ textureDataTypeToGLArityType txSemantic dTy | ||
571 | glBindTexture txTarget to | ||
572 | glTexParameteri txTarget gl_TEXTURE_BASE_LEVEL $ fromIntegral txBaseLevel | ||
573 | glTexParameteri txTarget gl_TEXTURE_MAX_LEVEL $ fromIntegral txMaxLevel | ||
574 | setTextureSamplerParameters txTarget txSampler | ||
575 | return (internalFormat,dataFormat) | ||
576 | |||
577 | mipSize 0 x = [x] | ||
578 | mipSize n x = x : mipSize (n-1) (x `div` 2) | ||
579 | mipS = mipSize (txMaxLevel - txBaseLevel) | ||
580 | levels = [txBaseLevel..txMaxLevel] | ||
581 | target <- case txType of | ||
582 | Texture1D dTy layerCnt -> do | ||
583 | let VWord txW = txSize | ||
584 | txTarget = if layerCnt > 1 then gl_TEXTURE_1D_ARRAY else gl_TEXTURE_1D | ||
585 | (internalFormat,dataFormat) <- txSetup txTarget dTy | ||
586 | forM_ (zip levels (mipS txW)) $ \(l,w) -> case layerCnt > 1 of | ||
587 | True -> glTexImage2D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral layerCnt) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
588 | False -> glTexImage1D txTarget (fromIntegral l) internalFormat (fromIntegral w) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
589 | return txTarget | ||
590 | Texture2D dTy layerCnt -> do | ||
591 | let VV2U (V2 txW txH) = txSize | ||
592 | txTarget = if layerCnt > 1 then gl_TEXTURE_2D_ARRAY else gl_TEXTURE_2D | ||
593 | (internalFormat,dataFormat) <- txSetup txTarget dTy | ||
594 | forM_ (zip3 levels (mipS txW) (mipS txH)) $ \(l,w,h) -> case layerCnt > 1 of | ||
595 | True -> glTexImage3D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) (fromIntegral layerCnt) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
596 | False -> glTexImage2D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
597 | return txTarget | ||
598 | Texture3D dTy -> do | ||
599 | let VV3U (V3 txW txH txD) = txSize | ||
600 | txTarget = gl_TEXTURE_3D | ||
601 | (internalFormat,dataFormat) <- txSetup txTarget dTy | ||
602 | forM_ (zip4 levels (mipS txW) (mipS txH) (mipS txD)) $ \(l,w,h,d) -> | ||
603 | glTexImage3D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) (fromIntegral d) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
604 | return txTarget | ||
605 | TextureCube dTy -> do | ||
606 | let VV2U (V2 txW txH) = txSize | ||
607 | txTarget = gl_TEXTURE_CUBE_MAP | ||
608 | targets = | ||
609 | [ gl_TEXTURE_CUBE_MAP_POSITIVE_X | ||
610 | , gl_TEXTURE_CUBE_MAP_NEGATIVE_X | ||
611 | , gl_TEXTURE_CUBE_MAP_POSITIVE_Y | ||
612 | , gl_TEXTURE_CUBE_MAP_NEGATIVE_Y | ||
613 | , gl_TEXTURE_CUBE_MAP_POSITIVE_Z | ||
614 | , gl_TEXTURE_CUBE_MAP_NEGATIVE_Z | ||
615 | ] | ||
616 | (internalFormat,dataFormat) <- txSetup txTarget dTy | ||
617 | forM_ (zip3 levels (mipS txW) (mipS txH)) $ \(l,w,h) -> | ||
618 | forM_ targets $ \t -> glTexImage2D t (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
619 | return txTarget | ||
620 | TextureRect dTy -> do | ||
621 | let VV2U (V2 txW txH) = txSize | ||
622 | txTarget = gl_TEXTURE_RECTANGLE | ||
623 | (internalFormat,dataFormat) <- txSetup txTarget dTy | ||
624 | forM_ (zip3 levels (mipS txW) (mipS txH)) $ \(l,w,h) -> | ||
625 | glTexImage2D txTarget (fromIntegral l) internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
626 | return txTarget | ||
627 | Texture2DMS dTy layerCnt sampleCount isFixedLocations -> do | ||
628 | let VV2U (V2 w h) = txSize | ||
629 | txTarget = if layerCnt > 1 then gl_TEXTURE_2D_MULTISAMPLE_ARRAY else gl_TEXTURE_2D_MULTISAMPLE | ||
630 | isFixed = fromIntegral $ if isFixedLocations then gl_TRUE else gl_FALSE | ||
631 | (internalFormat,dataFormat) <- txSetup txTarget dTy | ||
632 | case layerCnt > 1 of | ||
633 | True -> glTexImage3DMultisample txTarget (fromIntegral sampleCount) internalFormat (fromIntegral w) (fromIntegral h) (fromIntegral layerCnt) isFixed | ||
634 | False -> glTexImage2DMultisample txTarget (fromIntegral sampleCount) internalFormat (fromIntegral w) (fromIntegral h) isFixed | ||
635 | return txTarget | ||
636 | TextureBuffer dTy -> do | ||
637 | fail "internal error: buffer texture is not supported yet" | ||
638 | -- TODO | ||
639 | let VV2U (V2 w h) = txSize | ||
640 | txTarget = gl_TEXTURE_2D | ||
641 | (internalFormat,dataFormat) <- txSetup txTarget dTy | ||
642 | glTexImage2D gl_TEXTURE_2D 0 internalFormat (fromIntegral w) (fromIntegral h) 0 dataFormat gl_UNSIGNED_BYTE nullPtr | ||
643 | return txTarget | ||
644 | return $ GLTexture | ||
645 | { glTextureObject = to | ||
646 | , glTextureTarget = target | ||
647 | } | ||
648 | |||
649 | primitiveToFetchPrimitive :: Primitive -> FetchPrimitive | ||
650 | primitiveToFetchPrimitive prim = case prim of | ||
651 | TriangleStrip -> Triangles | ||
652 | TriangleList -> Triangles | ||
653 | TriangleFan -> Triangles | ||
654 | LineStrip -> Lines | ||
655 | LineList -> Lines | ||
656 | PointList -> Points | ||
657 | TriangleStripAdjacency -> TrianglesAdjacency | ||
658 | TriangleListAdjacency -> TrianglesAdjacency | ||
659 | LineStripAdjacency -> LinesAdjacency | ||
660 | LineListAdjacency -> LinesAdjacency | ||
661 | |||
662 | primitiveToGLType :: Primitive -> GLenum | ||
663 | primitiveToGLType p = case p of | ||
664 | TriangleStrip -> gl_TRIANGLE_STRIP | ||
665 | TriangleList -> gl_TRIANGLES | ||
666 | TriangleFan -> gl_TRIANGLE_FAN | ||
667 | LineStrip -> gl_LINE_STRIP | ||
668 | LineList -> gl_LINES | ||
669 | PointList -> gl_POINTS | ||
670 | TriangleStripAdjacency -> gl_TRIANGLE_STRIP_ADJACENCY | ||
671 | TriangleListAdjacency -> gl_TRIANGLES_ADJACENCY | ||
672 | LineStripAdjacency -> gl_LINE_STRIP_ADJACENCY | ||
673 | LineListAdjacency -> gl_LINES_ADJACENCY | ||
674 | |||
675 | inputTypeToTextureTarget :: InputType -> GLenum | ||
676 | inputTypeToTextureTarget ty = case ty of | ||
677 | STexture1D -> gl_TEXTURE_1D | ||
678 | STexture2D -> gl_TEXTURE_2D | ||
679 | STextureCube -> gl_TEXTURE_CUBE_MAP | ||
680 | STexture1DArray -> gl_TEXTURE_1D_ARRAY | ||
681 | STexture2DArray -> gl_TEXTURE_2D_ARRAY | ||
682 | STexture2DRect -> gl_TEXTURE_RECTANGLE | ||
683 | |||
684 | FTexture1D -> gl_TEXTURE_1D | ||
685 | FTexture2D -> gl_TEXTURE_2D | ||
686 | FTexture3D -> gl_TEXTURE_3D | ||
687 | FTextureCube -> gl_TEXTURE_CUBE_MAP | ||
688 | FTexture1DArray -> gl_TEXTURE_1D_ARRAY | ||
689 | FTexture2DArray -> gl_TEXTURE_2D_ARRAY | ||
690 | FTexture2DMS -> gl_TEXTURE_2D_MULTISAMPLE | ||
691 | FTexture2DMSArray -> gl_TEXTURE_2D_MULTISAMPLE_ARRAY | ||
692 | FTextureBuffer -> gl_TEXTURE_BUFFER | ||
693 | FTexture2DRect -> gl_TEXTURE_RECTANGLE | ||
694 | |||
695 | ITexture1D -> gl_TEXTURE_1D | ||
696 | ITexture2D -> gl_TEXTURE_2D | ||
697 | ITexture3D -> gl_TEXTURE_3D | ||
698 | ITextureCube -> gl_TEXTURE_CUBE_MAP | ||
699 | ITexture1DArray -> gl_TEXTURE_1D_ARRAY | ||
700 | ITexture2DArray -> gl_TEXTURE_2D_ARRAY | ||
701 | ITexture2DMS -> gl_TEXTURE_2D_MULTISAMPLE | ||
702 | ITexture2DMSArray -> gl_TEXTURE_2D_MULTISAMPLE_ARRAY | ||
703 | ITextureBuffer -> gl_TEXTURE_BUFFER | ||
704 | ITexture2DRect -> gl_TEXTURE_RECTANGLE | ||
705 | |||
706 | UTexture1D -> gl_TEXTURE_1D | ||
707 | UTexture2D -> gl_TEXTURE_2D | ||
708 | UTexture3D -> gl_TEXTURE_3D | ||
709 | UTextureCube -> gl_TEXTURE_CUBE_MAP | ||
710 | UTexture1DArray -> gl_TEXTURE_1D_ARRAY | ||
711 | UTexture2DArray -> gl_TEXTURE_2D_ARRAY | ||
712 | UTexture2DMS -> gl_TEXTURE_2D_MULTISAMPLE | ||
713 | UTexture2DMSArray -> gl_TEXTURE_2D_MULTISAMPLE_ARRAY | ||
714 | UTextureBuffer -> gl_TEXTURE_BUFFER | ||
715 | UTexture2DRect -> gl_TEXTURE_RECTANGLE | ||
716 | |||
717 | _ -> error "internal error (inputTypeToTextureTarget)!" | ||
@@ -1,28 +1,30 @@ | |||
1 | Copyright (c) 2015, LambdaCube 3D | 1 | Copyright (c) 2015, Csaba Hruska, Peter Divianszky |
2 | |||
2 | All rights reserved. | 3 | All rights reserved. |
3 | 4 | ||
4 | Redistribution and use in source and binary forms, with or without | 5 | Redistribution and use in source and binary forms, with or without |
5 | modification, are permitted provided that the following conditions are met: | 6 | modification, are permitted provided that the following conditions are met: |
6 | 7 | ||
7 | * Redistributions of source code must retain the above copyright notice, this | 8 | * Redistributions of source code must retain the above copyright |
8 | list of conditions and the following disclaimer. | 9 | notice, this list of conditions and the following disclaimer. |
9 | 10 | ||
10 | * Redistributions in binary form must reproduce the above copyright notice, | 11 | * Redistributions in binary form must reproduce the above |
11 | this list of conditions and the following disclaimer in the documentation | 12 | copyright notice, this list of conditions and the following |
12 | and/or other materials provided with the distribution. | 13 | disclaimer in the documentation and/or other materials provided |
14 | with the distribution. | ||
13 | 15 | ||
14 | * Neither the name of lambdacube-gl nor the names of its | 16 | * Neither the name of Csaba Hruska, Peter Divianszky nor the names of other |
15 | contributors may be used to endorse or promote products derived from | 17 | contributors may be used to endorse or promote products derived |
16 | this software without specific prior written permission. | 18 | from this software without specific prior written permission. |
17 | 19 | ||
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | |||
diff --git a/Monkey.lcmesh b/Monkey.lcmesh new file mode 100644 index 0000000..b651d61 --- /dev/null +++ b/Monkey.lcmesh | |||
Binary files differ | |||
diff --git a/SampleIR.hs b/SampleIR.hs new file mode 100644 index 0000000..4766d9e --- /dev/null +++ b/SampleIR.hs | |||
@@ -0,0 +1,241 @@ | |||
1 | {-# LANGUAGE OverloadedStrings, PackageImports, MonadComprehensions #-} | ||
2 | module SampleIR where | ||
3 | |||
4 | import "GLFW-b" Graphics.UI.GLFW as GLFW | ||
5 | import Data.Monoid | ||
6 | import Control.Monad | ||
7 | import Control.Applicative | ||
8 | import Data.Vect | ||
9 | import qualified Data.Trie as T | ||
10 | import qualified Data.Vector.Storable as SV | ||
11 | import qualified Data.Vector as V | ||
12 | import Text.Show.Pretty | ||
13 | |||
14 | import Backend.GL as GL | ||
15 | import Backend.GL.Mesh | ||
16 | import IR as IR | ||
17 | |||
18 | import System.Environment | ||
19 | |||
20 | import Driver (compileMain) | ||
21 | |||
22 | -- Our vertices. Tree consecutive floats give a 3D vertex; Three consecutive vertices give a triangle. | ||
23 | -- A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices | ||
24 | g_vertex_buffer_data = | ||
25 | [ ( 1.0, 1.0,-1.0) | ||
26 | , ( 1.0,-1.0,-1.0) | ||
27 | , (-1.0,-1.0,-1.0) | ||
28 | , ( 1.0, 1.0,-1.0) | ||
29 | , (-1.0,-1.0,-1.0) | ||
30 | , (-1.0, 1.0,-1.0) | ||
31 | , ( 1.0, 1.0,-1.0) | ||
32 | , ( 1.0, 1.0, 1.0) | ||
33 | , ( 1.0,-1.0, 1.0) | ||
34 | , ( 1.0, 1.0,-1.0) | ||
35 | , ( 1.0,-1.0, 1.0) | ||
36 | , ( 1.0,-1.0,-1.0) | ||
37 | , ( 1.0, 1.0, 1.0) | ||
38 | , (-1.0,-1.0, 1.0) | ||
39 | , ( 1.0,-1.0, 1.0) | ||
40 | , ( 1.0, 1.0, 1.0) | ||
41 | , (-1.0, 1.0, 1.0) | ||
42 | , (-1.0,-1.0, 1.0) | ||
43 | , (-1.0, 1.0, 1.0) | ||
44 | , (-1.0,-1.0,-1.0) | ||
45 | , (-1.0,-1.0, 1.0) | ||
46 | , (-1.0, 1.0, 1.0) | ||
47 | , (-1.0, 1.0,-1.0) | ||
48 | , (-1.0,-1.0,-1.0) | ||
49 | , ( 1.0, 1.0,-1.0) | ||
50 | , (-1.0, 1.0,-1.0) | ||
51 | , (-1.0, 1.0, 1.0) | ||
52 | , ( 1.0, 1.0,-1.0) | ||
53 | , (-1.0, 1.0, 1.0) | ||
54 | , ( 1.0, 1.0, 1.0) | ||
55 | , ( 1.0, 1.0,-1.0) | ||
56 | , ( 1.0, 1.0, 1.0) | ||
57 | , (-1.0, 1.0, 1.0) | ||
58 | , ( 1.0, 1.0,-1.0) | ||
59 | , (-1.0, 1.0, 1.0) | ||
60 | , (-1.0, 1.0,-1.0) | ||
61 | ] | ||
62 | |||
63 | -- Two UV coordinatesfor each vertex. They were created with Blender. | ||
64 | g_uv_buffer_data = | ||
65 | [ (0.0, 0.0) | ||
66 | , (0.0, 1.0) | ||
67 | , (1.0, 1.0) | ||
68 | , (0.0, 0.0) | ||
69 | , (1.0, 1.0) | ||
70 | , (1.0, 0.0) | ||
71 | , (0.0, 0.0) | ||
72 | , (1.0, 0.0) | ||
73 | , (1.0, 1.0) | ||
74 | , (0.0, 0.0) | ||
75 | , (1.0, 1.0) | ||
76 | , (0.0, 1.0) | ||
77 | , (1.0, 0.0) | ||
78 | , (0.0, 1.0) | ||
79 | , (1.0, 1.0) | ||
80 | , (1.0, 0.0) | ||
81 | , (0.0, 0.0) | ||
82 | , (0.0, 1.0) | ||
83 | , (0.0, 0.0) | ||
84 | , (1.0, 1.0) | ||
85 | , (0.0, 1.0) | ||
86 | , (0.0, 0.0) | ||
87 | , (1.0, 0.0) | ||
88 | , (1.0, 1.0) | ||
89 | , (0.0, 0.0) | ||
90 | , (1.0, 0.0) | ||
91 | , (1.0, 1.0) | ||
92 | , (0.0, 0.0) | ||
93 | , (1.0, 1.0) | ||
94 | , (0.0, 1.0) | ||
95 | , (0.0, 0.0) | ||
96 | , (0.0, 1.0) | ||
97 | , (1.0, 1.0) | ||
98 | , (0.0, 0.0) | ||
99 | , (1.0, 1.0) | ||
100 | , (1.0, 0.0) | ||
101 | ] | ||
102 | |||
103 | myCube :: Mesh | ||
104 | myCube = Mesh | ||
105 | { mAttributes = T.fromList | ||
106 | [ ("position4", A_V4F $ SV.fromList [V4 x y z 1 | (x,y,z) <- g_vertex_buffer_data]) | ||
107 | , ("vertexUV", A_V2F $ SV.fromList [V2 u v | (u,v) <- g_uv_buffer_data]) | ||
108 | ] | ||
109 | , mPrimitive = P_Triangles | ||
110 | , mGPUData = Nothing | ||
111 | } | ||
112 | |||
113 | main :: IO () | ||
114 | main = do | ||
115 | win <- initWindow "LambdaCube 3D DSL Sample" 1024 768 | ||
116 | let keyIsPressed k = fmap (==KeyState'Pressed) $ getKey win k | ||
117 | |||
118 | n <- getArgs | ||
119 | let srcName = case n of | ||
120 | [fn] -> fn | ||
121 | _ -> "gfx03" | ||
122 | |||
123 | let inputSchema = | ||
124 | PipelineSchema | ||
125 | { GL.slots = T.fromList [("stream",SlotSchema Triangles $ T.fromList [("position",TV3F),("normal",TV3F),("UVTex",TV2F)]) | ||
126 | ,("stream4",SlotSchema Triangles $ T.fromList [("position4",TV4F),("vertexUV",TV2F)]) | ||
127 | ] | ||
128 | , uniforms = T.fromList [("MVP",M44F),("MVP2",M44F)] | ||
129 | } | ||
130 | pplInput <- mkGLPipelineInput inputSchema | ||
131 | |||
132 | gpuCube <- compileMesh myCube | ||
133 | gpuMonkey <- loadMesh "Monkey.lcmesh" | ||
134 | |||
135 | addMesh pplInput "stream4" gpuCube [] | ||
136 | addMesh pplInput "stream" gpuMonkey [] | ||
137 | |||
138 | let setup = do | ||
139 | pplRes <- compileMain "../lambdacube-dsl/tests/accept" srcName | ||
140 | case pplRes of | ||
141 | Left err -> putStrLn ("error: " ++ err) >> return Nothing | ||
142 | Right ppl -> do | ||
143 | putStrLn $ ppShow ppl | ||
144 | renderer <- allocPipeline ppl | ||
145 | setPipelineInput renderer (Just pplInput) | ||
146 | sortSlotObjects pplInput | ||
147 | putStrLn "reloaded" | ||
148 | return $ Just renderer | ||
149 | |||
150 | let cm' = fromProjective (lookat (Vec3 4 0.5 (-0.6)) (Vec3 0 0 0) (Vec3 0 1 0)) | ||
151 | cm = fromProjective (lookat (Vec3 3 1.3 0.3) (Vec3 0 0 0) (Vec3 0 1 0)) | ||
152 | loop renderer = do | ||
153 | (w,h) <- getWindowSize win | ||
154 | let uniformMap = uniformSetter pplInput | ||
155 | texture = uniformFTexture2D "myTextureSampler" uniformMap | ||
156 | mvp = uniformM44F "MVP" uniformMap | ||
157 | mvp' = uniformM44F "MVP2" uniformMap | ||
158 | pm = perspective 0.1 100 (pi/4) (fromIntegral w / fromIntegral h) | ||
159 | |||
160 | setScreenSize pplInput (fromIntegral w) (fromIntegral h) | ||
161 | Just t <- getTime | ||
162 | let angle = pi / 24 * realToFrac t | ||
163 | mm = fromProjective $ rotationEuler $ Vec3 angle 0 0 | ||
164 | mvp $! mat4ToM44F $! mm .*. cm .*. pm | ||
165 | mvp' $! mat4ToM44F $! mm .*. cm' .*. pm | ||
166 | renderPipeline renderer | ||
167 | swapBuffers win >> pollEvents | ||
168 | |||
169 | k <- keyIsPressed Key'Escape | ||
170 | reload <- keyIsPressed Key'R | ||
171 | rend' <- if not reload then return renderer else do | ||
172 | r <- setup | ||
173 | case r of | ||
174 | Nothing -> return renderer | ||
175 | Just a -> do | ||
176 | disposePipeline renderer | ||
177 | return a | ||
178 | when k $ disposePipeline rend' | ||
179 | unless k $ loop rend' | ||
180 | |||
181 | r <- setup | ||
182 | case r of | ||
183 | Just a -> loop a | ||
184 | Nothing -> return () | ||
185 | |||
186 | destroyWindow win | ||
187 | terminate | ||
188 | |||
189 | vec4ToV4F :: Vec4 -> V4F | ||
190 | vec4ToV4F (Vec4 x y z w) = V4 x y z w | ||
191 | |||
192 | mat4ToM44F :: Mat4 -> M44F | ||
193 | mat4ToM44F (Mat4 a b c d) = V4 (vec4ToV4F a) (vec4ToV4F b) (vec4ToV4F c) (vec4ToV4F d) | ||
194 | |||
195 | initWindow :: String -> Int -> Int -> IO Window | ||
196 | initWindow title width height = do | ||
197 | GLFW.init | ||
198 | defaultWindowHints | ||
199 | mapM_ windowHint | ||
200 | [ WindowHint'ContextVersionMajor 3 | ||
201 | , WindowHint'ContextVersionMinor 3 | ||
202 | , WindowHint'OpenGLProfile OpenGLProfile'Core | ||
203 | , WindowHint'OpenGLForwardCompat True | ||
204 | ] | ||
205 | Just win <- createWindow width height title Nothing Nothing | ||
206 | makeContextCurrent $ Just win | ||
207 | |||
208 | return win | ||
209 | |||
210 | -- | Perspective transformation matrix in row major order. | ||
211 | perspective :: Float -- ^ Near plane clipping distance (always positive). | ||
212 | -> Float -- ^ Far plane clipping distance (always positive). | ||
213 | -> Float -- ^ Field of view of the y axis, in radians. | ||
214 | -> Float -- ^ Aspect ratio, i.e. screen's width\/height. | ||
215 | -> Mat4 | ||
216 | perspective n f fovy aspect = transpose $ | ||
217 | Mat4 (Vec4 (2*n/(r-l)) 0 (-(r+l)/(r-l)) 0) | ||
218 | (Vec4 0 (2*n/(t-b)) ((t+b)/(t-b)) 0) | ||
219 | (Vec4 0 0 (-(f+n)/(f-n)) (-2*f*n/(f-n))) | ||
220 | (Vec4 0 0 (-1) 0) | ||
221 | where | ||
222 | t = n*tan(fovy/2) | ||
223 | b = -t | ||
224 | r = aspect*t | ||
225 | l = -r | ||
226 | |||
227 | -- | Pure orientation matrix defined by Euler angles. | ||
228 | rotationEuler :: Vec3 -> Proj4 | ||
229 | rotationEuler (Vec3 a b c) = orthogonal $ toOrthoUnsafe $ rotMatrixY a .*. rotMatrixX b .*. rotMatrixZ c | ||
230 | |||
231 | -- | Camera transformation matrix. | ||
232 | lookat :: Vec3 -- ^ Camera position. | ||
233 | -> Vec3 -- ^ Target position. | ||
234 | -> Vec3 -- ^ Upward direction. | ||
235 | -> Proj4 | ||
236 | lookat pos target up = translateBefore4 (neg pos) (orthogonal $ toOrthoUnsafe r) | ||
237 | where | ||
238 | w = normalize $ pos &- target | ||
239 | u = normalize $ up &^ w | ||
240 | v = w &^ u | ||
241 | r = transpose $ Mat3 u v w | ||
diff --git a/lambdacube-gl-ir.cabal b/lambdacube-gl-ir.cabal new file mode 100644 index 0000000..515c433 --- /dev/null +++ b/lambdacube-gl-ir.cabal | |||
@@ -0,0 +1,86 @@ | |||
1 | -- Initial lambdacube-dsl.cabal generated by cabal init. For further | ||
2 | -- documentation, see http://haskell.org/cabal/users-guide/ | ||
3 | |||
4 | name: lambdacube-gl-ir | ||
5 | version: 0.1.0.0 | ||
6 | -- synopsis: | ||
7 | -- description: | ||
8 | homepage: lambdacube3d.com | ||
9 | license: BSD3 | ||
10 | license-file: LICENSE | ||
11 | author: Csaba Hruska, Peter Divianszky | ||
12 | maintainer: csaba.hruska@gmail.com | ||
13 | -- copyright: | ||
14 | category: Graphics | ||
15 | build-type: Simple | ||
16 | -- extra-source-files: | ||
17 | cabal-version: >=1.10 | ||
18 | |||
19 | library | ||
20 | exposed-modules: | ||
21 | SampleIR | ||
22 | -- Backend | ||
23 | Backend.GL | ||
24 | Backend.GL.Backend | ||
25 | Backend.GL.Data | ||
26 | Backend.GL.Input | ||
27 | Backend.GL.Mesh | ||
28 | Backend.GL.Type | ||
29 | Backend.GL.Util | ||
30 | -- other-modules: | ||
31 | other-extensions: | ||
32 | LambdaCase | ||
33 | PatternSynonyms | ||
34 | ViewPatterns | ||
35 | TypeSynonymInstances | ||
36 | FlexibleInstances | ||
37 | NoMonomorphismRestriction | ||
38 | TypeFamilies | ||
39 | RecordWildCards | ||
40 | DeriveFunctor | ||
41 | DeriveFoldable | ||
42 | DeriveTraversable | ||
43 | GeneralizedNewtypeDeriving | ||
44 | OverloadedStrings | ||
45 | TupleSections | ||
46 | MonadComprehensions | ||
47 | ExistentialQuantification | ||
48 | ScopedTypeVariables | ||
49 | ParallelListComp | ||
50 | build-depends: | ||
51 | base >=4.7 && <4.9, | ||
52 | containers >=0.5 && <0.6, | ||
53 | mtl >=2.2 && <2.3, | ||
54 | bytestring >=0.10 && <0.11, | ||
55 | vector >=0.10 && <0.11, | ||
56 | bytestring-trie >=0.2 && <0.3, | ||
57 | OpenGLRaw >=2.4 && <2.5, | ||
58 | JuicyPixels >=3.2 && <3.3, | ||
59 | JuicyPixels-util, | ||
60 | vector-algorithms >=0.6 && <0.7, | ||
61 | binary >=0.7 && <0.8, | ||
62 | GLFW-b >= 1.4.6, | ||
63 | vect >= 0.4.7, | ||
64 | pretty-show >=1.6 && <1.7, | ||
65 | lambdacube-dsl | ||
66 | hs-source-dirs: . | ||
67 | default-language: Haskell2010 | ||
68 | |||
69 | --executable sampleIR | ||
70 | -- hs-source-dirs: tests | ||
71 | -- main-is: sampleIR.hs | ||
72 | -- build-depends: | ||
73 | -- lambdacube-dsl, | ||
74 | -- base >=4.7 && <4.9 | ||
75 | -- default-language: Haskell2010 | ||
76 | |||
77 | --test-suite runtests | ||
78 | -- type: exitcode-stdio-1.0 | ||
79 | -- hs-source-dirs: tests | ||
80 | -- main-is: runTests.hs | ||
81 | -- | ||
82 | -- build-depends: base < 4.9 | ||
83 | -- , filepath | ||
84 | -- , directory | ||
85 | -- , lambdacube-dsl | ||
86 | -- default-language: Haskell2010 | ||
diff --git a/tests/sampleIR.hs b/tests/sampleIR.hs new file mode 100644 index 0000000..a68d28a --- /dev/null +++ b/tests/sampleIR.hs | |||
@@ -0,0 +1,3 @@ | |||
1 | import qualified SampleIR as S | ||
2 | |||
3 | main = S.main | ||