GPipe 2.1.5 → 2.1.6
raw patch · 9 files changed
+324/−223 lines, 9 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Graphics.GPipe.PrimitiveStream: class BufferFormat a => VertexInput a where type family VertexFormat a
+ Graphics.GPipe.PrimitiveStream: class VertexInput a where type family VertexFormat a
Files
- CHANGELOG.md +5/−0
- GPipe.cabal +1/−1
- LICENSE +20/−20
- src/Data/SNMap.hs +6/−6
- src/Graphics/GPipe/Internal/Buffer.hs +38/−31
- src/Graphics/GPipe/Internal/Compiler.hs +79/−60
- src/Graphics/GPipe/Internal/FrameBuffer.hs +2/−2
- src/Graphics/GPipe/Internal/PrimitiveStream.hs +150/−80
- src/Graphics/GPipe/Internal/Shader.hs +23/−23
CHANGELOG.md view
@@ -1,3 +1,8 @@+### 2.1.6 + +- Adding support for normal Floats, Int32s and Word32s in PrimitiveStreams +- Runtime optimizations + ### 2.1.5 - Fixed bug in clear where masks weren't set
GPipe.cabal view
@@ -1,5 +1,5 @@ name: GPipe -version: 2.1.5 +version: 2.1.6 cabal-version: >= 1.8 build-type: Simple author: Tobias Bexelius
LICENSE view
@@ -1,21 +1,21 @@-The MIT License (MIT)--Copyright (c) 2015 Tobias Bexelius--Permission is hereby granted, free of charge, to any person obtaining a copy-of this software and associated documentation files (the "Software"), to deal-in the Software without restriction, including without limitation the rights-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell-copies of the Software, and to permit persons to whom the Software is-furnished to do so, subject to the following conditions:--The above copyright notice and this permission notice shall be included in-all copies or substantial portions of the Software.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+The MIT License (MIT) + +Copyright (c) 2015 Tobias Bexelius + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
src/Data/SNMap.hs view
@@ -5,13 +5,13 @@ SNMapReaderT, runSNMapReaderT, newSNMap, - memoize, + memoize, memoizeM, scopedM )where -import System.Mem.StableName -import qualified Data.HashTable.IO as HT +import System.Mem.StableName +import qualified Data.HashTable.IO as HT import Data.Functor import Control.Monad.IO.Class (liftIO, MonadIO) import Control.Monad.Trans.Class @@ -27,12 +27,12 @@ memoize :: MonadIO m => m (SNMap m a) -> m a -> m a memoize getter m = do s <- liftIO $ makeStableName $! m - (SNMap h) <- getter + (SNMap h) <- getter x <- liftIO $ HT.lookup h s case x of Just a -> return a Nothing -> do a <- m - (SNMap h') <- getter --Need to redo because of scope + (SNMap h') <- getter --Need to redo because of scope liftIO $ HT.insert h' s a return a @@ -40,7 +40,7 @@ runSNMapReaderT :: MonadIO m => SNMapReaderT a m b -> m b runSNMapReaderT (SNMapReaderT m) = do h <- liftIO newSNMap - evalStateT m h + evalStateT m h instance MonadTrans (SNMapReaderT a) where lift = SNMapReaderT . lift
src/Graphics/GPipe/Internal/Buffer.hs view
@@ -7,7 +7,7 @@ BufferFormat(..), BufferColor, Buffer(), - ToBuffer(), + ToBuffer(..), B(..), B2(..), B3(..), B4(..), toB22, toB3, toB21, toB12, toB11, Uniform(..), Normalized(..), BPacked(), @@ -27,7 +27,6 @@ import Foreign.Marshal.Alloc import Prelude hiding ((.), id) -import Control.Monad.Trans.State import Control.Category import Control.Arrow import Control.Monad (void) @@ -36,11 +35,12 @@ import Control.Monad.IO.Class import Data.Word import Data.Int +import Control.Monad.Trans.State.Strict +import Control.Monad.Trans.Writer.Strict import Control.Monad.Trans.Reader import Control.Monad.Trans.Class (lift) import Data.IORef import Control.Applicative ((<$>)) -import Control.Monad.Trans.Writer.Lazy import Linear.V4 import Linear.V3 import Linear.V2 @@ -90,21 +90,22 @@ data BInput = BInput {bInSkipElems :: Int, bInInstanceDiv :: Int} -type ToBufferInput = (BufferName, Stride, BInput) - type UniformAlignment = Int data AlignmentMode = Align4 | AlignUniform | AlignPackedIndices | AlignUnknown deriving (Eq) -- | The arrow type for 'toBuffer'. data ToBuffer a b = ToBuffer - (Kleisli (StateT Offset (WriterT [Int] (Reader (ToBufferInput, UniformAlignment, AlignmentMode)))) a b) -- Normal = aligned to 4 bytes - (Kleisli (StateT (Ptr (), [Int]) IO) a b) -- Normal = aligned to 4 bytes - AlignmentMode + !(Kleisli (StateT Offset (WriterT [Int] (Reader (UniformAlignment, AlignmentMode)))) a b) -- Normal = aligned to 4 bytes + !(Kleisli (StateT Offset (Reader (BufferName, Stride, BInput))) a b) + !(Kleisli (StateT (Ptr (), [Int]) IO) a b) -- Normal = aligned to 4 bytes + !AlignmentMode instance Category ToBuffer where - id = ToBuffer id id AlignUnknown - ToBuffer a b m1 . ToBuffer x y m2 = ToBuffer (a.x) (b.y) (comb m1 m2) + {-# INLINE id #-} + id = ToBuffer id id id AlignUnknown + {-# INLINE (.) #-} + ToBuffer a b c m1 . ToBuffer x y z m2 = ToBuffer (a.x) (b.y) (c.z) (comb m1 m2) where -- If only one uniform or one PackedIndices, use that, otherwise use Align4 comb AlignUniform AlignUnknown = AlignUniform @@ -115,8 +116,10 @@ comb _ _ = Align4 instance Arrow ToBuffer where - arr f = ToBuffer (arr f) (arr f) AlignUnknown - first (ToBuffer a b m) = ToBuffer (first a) (first b) m + {-# INLINE arr #-} + arr f = ToBuffer (arr f) (arr f) (arr f) AlignUnknown + {-# INLINE first #-} + first (ToBuffer a b c m) = ToBuffer (first a) (first b) (first c) m -- | The atomic buffer value that represents a host value of type 'a'. data B a = B { bName :: IORef GLuint, bOffset :: Int, bStride :: Int, bSkipElems :: Int, bInstanceDiv :: Int} @@ -162,14 +165,18 @@ toBufferBUnaligned :: forall a. Storable a => ToBuffer a (B a) toBufferBUnaligned = ToBuffer (Kleisli $ const static) + (Kleisli $ const valueProd) (Kleisli writer) Align4 where size = sizeOf (undefined :: a) - static = do ((name, stride, bIn),_,_) <- lift $ lift ask - offset <- get + static = do offset <- get put $ offset + size - return $ B name offset stride (bInSkipElems bIn) (bInInstanceDiv bIn) + return undefined + valueProd = do (name, stride, bIn) <- lift ask + offset <- get + put $ offset + size + return $ B name offset stride (bInSkipElems bIn) (bInInstanceDiv bIn) writer a = do (ptr,pads) <- get put (ptr `plusPtr` size, pads) liftIO $ poke (castPtr ptr) a @@ -204,15 +211,16 @@ instance BufferFormat a => BufferFormat (Uniform a) where type HostFormat (Uniform a) = HostFormat a toBuffer = arr Uniform . ToBuffer + (Kleisli preStep) (Kleisli elementBuilderA) (Kleisli writerA) AlignUniform where - ToBuffer (Kleisli elementBuilderA') (Kleisli writerA') _ = toBuffer :: ToBuffer (HostFormat a) a - elementBuilderA a = do (_,x,_) <- lift $ lift ask - a' <- elementBuilderA' a - setElemAlignM [(AlignUniform, x)] () - return a' + ToBuffer (Kleisli preStep') (Kleisli elementBuilderA) (Kleisli writerA') _ = toBuffer :: ToBuffer (HostFormat a) a + preStep a = do (x,_) <- lift $ lift ask + a' <- preStep' a + setElemAlignM [(AlignUniform, x)] () + return a' writerA a = do a' <- writerA' a setWriterAlignM () return a' @@ -368,11 +376,11 @@ ---------------------------------------------- alignWhen :: [(AlignmentMode, Int)] -> ToBuffer a a -alignWhen x = ToBuffer (Kleisli $ setElemAlignM x) (Kleisli setWriterAlignM) AlignUniform where +alignWhen x = ToBuffer (Kleisli $ setElemAlignM x) (Kleisli $ return) (Kleisli setWriterAlignM) AlignUniform where -setElemAlignM :: [(AlignmentMode, Int)] -> b -> StateT Offset (WriterT [Int] (Reader (ToBufferInput, UniformAlignment, AlignmentMode))) b +setElemAlignM :: [(AlignmentMode, Int)] -> b -> StateT Offset (WriterT [Int] (Reader (UniformAlignment, AlignmentMode))) b setElemAlignM x a = do - (_,_,m) <- lift $ lift ask + (_,m) <- lift $ lift ask pad <- case lookup m x of Nothing -> return 0 Just al -> do @@ -394,12 +402,11 @@ makeBuffer :: forall os b. BufferFormat b => BufferName -> Int -> UniformAlignment -> Buffer os b makeBuffer name elementCount uniformAlignment = do - let ToBuffer a b m = toBuffer :: ToBuffer (HostFormat b) b - err = error "toBuffer, toVertex or toUniform are creating values that are dependant on the actual HostFormat values, this is not allowed since it doesn't allow static creation of shaders" :: HostFormat b - elementM = runWriterT (runStateT (runKleisli a err) 0) - ((_,elementSize),pads) = runReader elementM ((name, undefined, undefined), uniformAlignment, m) - elementF bIn = (fst . fst) $ runReader elementM ((name, elementSize, bIn), uniformAlignment, m) - writer ptr x = void $ runStateT (runKleisli b x) (ptr,pads) + let ToBuffer a b c m = toBuffer :: ToBuffer (HostFormat b) b + err = error "toBuffer is creating values that are dependant on the actual HostFormat values, this is not allowed since it doesn't allow static creation of shaders" :: HostFormat b + ((_,elementSize),pads) = runReader (runWriterT (runStateT (runKleisli a err) 0)) (uniformAlignment, m) + elementF bIn = fst $ runReader (runStateT (runKleisli b err) 0) (name, elementSize, bIn) + writer ptr x = void $ runStateT (runKleisli c x) (ptr,pads) Buffer name elementSize elementCount elementF writer -- | This type family restricts what host and buffer types a texture format may be converted into. @@ -494,14 +501,14 @@ instance BufferFormat (BPacked Word16) where type HostFormat (BPacked Word16) = Word16 - toBuffer = let ToBuffer a b _ = toBufferB :: ToBuffer Word16 (B Word16) in arr BPacked . ToBuffer a b AlignPackedIndices + toBuffer = let ToBuffer a b c _ = toBufferB :: ToBuffer Word16 (B Word16) in arr BPacked . ToBuffer a b c AlignPackedIndices getGlType _ = GL_UNSIGNED_SHORT peekPixel = peekPixel1 getGlPaddedFormat _ = GL_RED_INTEGER instance BufferFormat (BPacked Word8) where type HostFormat (BPacked Word8) = Word8 - toBuffer = let ToBuffer a b _ = toBufferB :: ToBuffer Word8 (B Word8) in arr BPacked . ToBuffer a b AlignPackedIndices + toBuffer = let ToBuffer a b c _ = toBufferB :: ToBuffer Word8 (B Word8) in arr BPacked . ToBuffer a b c AlignPackedIndices getGlType _ = GL_UNSIGNED_BYTE peekPixel = peekPixel1 getGlPaddedFormat _ = GL_RED_INTEGER
src/Graphics/GPipe/Internal/Compiler.hs view
@@ -12,6 +12,7 @@ import Control.Monad.Trans.Class (lift) import Graphics.GL.Core33 +import Graphics.GL.Types import Foreign.Marshal.Utils import Foreign.Marshal.Alloc (alloca) import Foreign.Storable (peek) @@ -34,8 +35,9 @@ usedVUniforms :: [Int], usedVSamplers :: [Int], usedFUniforms :: [Int], - usedFSamplers :: [Int] - } + usedFSamplers :: [Int], + primStrUBufferSize :: Int -- The size of the ubuffer for uniforms in primitive stream + } -- index/binding refers to what is used in the final shader. Index space is limited, usually 16 -- attribname is what was declared, but all might not be used. Attribname share namespace with uniforms and textures and is unlimited(TM) @@ -44,49 +46,49 @@ -- TODO: Add usedBuffers to RenderIOState, ie Map.IntMap (s -> (Binding -> IO (), Int)) and the like -- then create a function that checks that none of the input buffers are used as output, and throws if it is -data RenderIOState s = RenderIOState +data RenderIOState s = RenderIOState { uniformNameToRenderIO :: Map.IntMap (s -> Binding -> IO ()), -- TODO: Return buffer name here when we start writing to buffers during rendering (transform feedback, buffer textures) samplerNameToRenderIO :: Map.IntMap (s -> Binding -> IO Int), -- IO returns texturename for validating that it isnt used as render target rasterizationNameToRenderIO :: Map.IntMap (s -> IO ()), - inputArrayToRenderIOs :: Map.IntMap (s -> [[Binding] -> ((IO [VAOKey], IO ()), IO ())]) - } - + inputArrayToRenderIOs :: Map.IntMap (s -> [([Binding], GLuint, Int) -> ((IO [VAOKey], IO ()), IO ())]) + } + newRenderIOState :: RenderIOState s newRenderIOState = RenderIOState Map.empty Map.empty Map.empty Map.empty mapRenderIOState :: (s -> s') -> RenderIOState s' -> RenderIOState s -> RenderIOState s mapRenderIOState f (RenderIOState a b c d) (RenderIOState i j k l) = let g x = x . f in RenderIOState (Map.union i $ Map.map g a) (Map.union j $ Map.map g b) (Map.union k $ Map.map g c) (Map.union l $ Map.map g d) -data BoundState = BoundState { - boundUniforms :: Map.IntMap Int, +data BoundState = BoundState { + boundUniforms :: Map.IntMap Int, boundSamplers :: Map.IntMap Int, boundRasterizerN :: Int - } + } --- | May throw a GPipeException +-- | May throw a GPipeException compile :: (Monad m, MonadIO m, MonadException m) => [IO (Drawcall s)] -> RenderIOState s -> ContextT w os f m (ContextData -> s -> Asserter Int -> IO (Maybe String)) compile dcs s = do drawcalls <- liftIO $ sequence dcs -- IO only for SNMap - (maxUnis, + (maxUnis, maxSamplers, maxVUnis, maxVSamplers, maxFUnis, - maxFSamplers) <- liftContextIO $ do + maxFSamplers) <- liftContextIO $ do maxUnis <- alloca (\ptr -> glGetIntegerv GL_MAX_COMBINED_UNIFORM_BLOCKS ptr >> peek ptr) maxSamplers <- alloca (\ptr -> glGetIntegerv GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS ptr >> peek ptr) maxVUnis <- alloca (\ptr -> glGetIntegerv GL_MAX_VERTEX_UNIFORM_BLOCKS ptr >> peek ptr) maxVSamplers <- alloca (\ptr -> glGetIntegerv GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS ptr >> peek ptr) maxFUnis <- alloca (\ptr -> glGetIntegerv GL_MAX_FRAGMENT_UNIFORM_BLOCKS ptr >> peek ptr) maxFSamplers <- alloca (\ptr -> glGetIntegerv GL_MAX_TEXTURE_IMAGE_UNITS ptr >> peek ptr) - return - (fromIntegral maxUnis, + return + (fromIntegral maxUnis, fromIntegral maxSamplers, - fromIntegral maxVUnis, + fromIntegral maxVUnis, fromIntegral maxVSamplers, - fromIntegral maxFUnis, + fromIntegral maxFUnis, fromIntegral maxFSamplers) let vUnisPerDc = map usedVUniforms drawcalls @@ -95,51 +97,56 @@ fSampsPerDc = map usedFSamplers drawcalls unisPerDc = zipWith orderedUnion vUnisPerDc fUnisPerDc sampsPerDc = zipWith orderedUnion vSampsPerDc fSampsPerDc - + limitErrors = concat [ - ["Too many uniform blocks used in a single shader program\n" | any (\ xs -> length xs >= maxUnis) unisPerDc], + ["Too many uniform blocks used in a single shader program\n" | any (\ xs -> length xs >= maxUnis) unisPerDc], ["Too many textures used in a single shader program\n" | any (\ xs -> length xs >= maxSamplers) sampsPerDc], ["Too many uniform blocks used in a single vertex shader\n" | any (\ xs -> length xs >= maxVUnis) vUnisPerDc], ["Too many textures used in a single vertex shader\n" | any (\ xs -> length xs >= maxVSamplers) vSampsPerDc], ["Too many uniform blocks used in a single fragment shader\n" | any (\ xs -> length xs >= maxFUnis) fUnisPerDc], ["Too many textures used in a single fragment shader\n" | any (\ xs -> length xs >= maxFSamplers) fSampsPerDc] ] - - allocatedUniforms = allocate maxUnis unisPerDc + + allocatedUniforms = allocate maxUnis unisPerDc allocatedSamplers = allocate maxSamplers sampsPerDc compRet <- evalStateT (mapM comp (zip5 drawcalls unisPerDc sampsPerDc allocatedUniforms allocatedSamplers)) (BoundState Map.empty Map.empty (-1)) fAdd <- getContextFinalizerAdder let (errs, ret) = partitionEithers compRet (pnames, fs) = unzip ret - fr cd x asserter = foldl (\ io f -> do + fr cd x asserter = foldl (\ io f -> do mErr <- io mErr2 <- f x asserter cd fAdd return $ mErr <> mErr2) - (return Nothing) + (return Nothing) fs allErrs = limitErrors ++ errs - if null allErrs - then do - forM_ pnames (\pNameRef -> do pName <- liftIO $ readIORef pNameRef - addContextFinalizer pNameRef (glDeleteProgram pName)) + if null allErrs + then do + forM_ pnames (\(pNameRef,pStrUDeleter) -> do pName <- liftIO $ readIORef pNameRef + addContextFinalizer pNameRef (glDeleteProgram pName >> pStrUDeleter)) return fr else do - liftContextIOAsync $ mapM_ (readIORef >=> glDeleteProgram) pnames - liftIO $ throwIO $ GPipeException $ concat allErrs - where - comp (Drawcall fboSetup primN rastN vsource fsource inps _ _ _ _, unis, samps, ubinds, sbinds) = do + liftContextIOAsync $ mapM_ (\(pNameRef, pStrUDeleter) -> readIORef pNameRef >>= glDeleteProgram >> pStrUDeleter) pnames + liftIO $ throwIO $ GPipeException $ concat allErrs + where + comp (Drawcall fboSetup primN rastN vsource fsource inps _ _ _ _ pstrUSize', unis, samps, ubinds, sbinds) = do + let pstrUSize = if 0 `elem` unis then pstrUSize' else 0 + let uNameToRenderIOmap = uniformNameToRenderIO s + pstrUBuf <- createUBuffer pstrUSize -- Create uniform buffer for primiveStream uniforms + let pStrUDeleter = if pstrUSize > 0 then with pstrUBuf (glDeleteBuffers 1) else return () + let uNameToRenderIOmap' = addPstrUniform pstrUBuf pstrUSize uNameToRenderIOmap BoundState uniState sampState boundRastN <- get - let (bindUni, uniState') = makeBind uniState (uniformNameToRenderIO s) (zip unis ubinds) + let (bindUni, uniState') = makeBind uniState uNameToRenderIOmap' (zip unis ubinds) let (bindSamp, sampState') = makeBind sampState (samplerNameToRenderIO s) $ zip samps sbinds - let bindRast = if rastN == boundRastN then const $ return () else rasterizationNameToRenderIO s ! rastN + let bindRast = if rastN == boundRastN then const $ return () else rasterizationNameToRenderIO s ! rastN put $ BoundState uniState' sampState' rastN - + lift $ do ePname <- liftContextIO $ do vShader <- glCreateShader GL_VERTEX_SHADER mErrV <- compileShader vShader vsource - fShader <- glCreateShader GL_FRAGMENT_SHADER + fShader <- glCreateShader GL_FRAGMENT_SHADER mErrF <- compileShader fShader fsource - if isNothing mErrV && isNothing mErrV + if isNothing mErrV && isNothing mErrV then do pName <- glCreateProgram glAttachShader pName vShader glAttachShader pName fShader @@ -150,28 +157,30 @@ glDeleteShader vShader glDeleteShader fShader case mPErr of - Just errP -> do glDeleteProgram pName + Just errP -> do glDeleteProgram pName + pStrUDeleter return $ Left $ "Linking a GPU progam failed:\n" ++ errP ++ "\nVertex source:\n" ++ vsource ++ "\nFragment source:\n" ++ fsource Nothing -> return $ Right pName else do glDeleteShader vShader glDeleteShader fShader + pStrUDeleter let err = maybe "" (\e -> "A vertex shader compilation failed:\n" ++ e ++ "\nSource:\n" ++ vsource) mErrV ++ maybe "" (\e -> "A fragment shader compilation failed:\n" ++ e ++ "\nSource:\n" ++ fsource) mErrF return $ Left err - case ePname of + case ePname of Left err -> return $ Left err - Right pName -> liftContextIO $ do + Right pName -> liftContextIO $ do forM_ (zip unis ubinds) $ \(name, bind) -> do uix <- withCString ("uBlock" ++ show name) $ glGetUniformBlockIndex pName glUniformBlockBinding pName uix (fromIntegral bind) - + glUseProgram pName -- For setting texture uniforms forM_ (zip samps sbinds) $ \(name, bind) -> do six <- withCString ("s" ++ show name) $ glGetUniformLocation pName glUniform1i six (fromIntegral bind) pNameRef <- newIORef pName - - return $ Right (pNameRef, \x asserter cd fAdd -> do + + return $ Right ((pNameRef, pStrUDeleter), \x asserter cd fAdd -> do -- Drawing with program -- pName' <- readIORef pNameRef -- Cant use pName, need to touch pNameRef glUseProgram pName' @@ -196,13 +205,13 @@ setFBO cd fbokey fbo glBindFramebuffer GL_DRAW_FRAMEBUFFER fbo' glEnable GL_FRAMEBUFFER_SRGB - fboio + fboio let numColors = length $ fboColors fbokey withArray [GL_COLOR_ATTACHMENT0 .. (GL_COLOR_ATTACHMENT0 + fromIntegral numColors - 1)] $ glDrawBuffers (fromIntegral numColors) - getFBOerror - when (isNothing mError) $ + getFBOerror + when (isNothing mError) $ -- Draw each Vertex Array -- - forM_ (map ($ inps) ((inputArrayToRenderIOs s ! primN) x)) $ \ ((keyio, vaoio), drawio) -> do + forM_ (map ($ (inps, pstrUBuf, pstrUSize)) ((inputArrayToRenderIOs s ! primN) x)) $ \ ((keyio, vaoio), drawio) -> do key <- keyio mvao <- getVAO cd key case mvao of @@ -217,7 +226,7 @@ drawio return mError) - compileShader name source = do + compileShader name source = do withCStringLen source $ \ (ptr, len) -> with ptr $ \ pptr -> with (fromIntegral len) $ \ plen -> @@ -230,7 +239,7 @@ let logLen' = fromIntegral logLen liftM Just $ allocaArray logLen' $ \ ptr -> do glGetShaderInfoLog name logLen nullPtr ptr - peekCString ptr + peekCString ptr linkProgram name = do glLinkProgram name linkStatus <- alloca $ \ ptr -> glGetProgramiv name GL_LINK_STATUS ptr >> peek ptr if linkStatus /= GL_FALSE @@ -239,8 +248,18 @@ let logLen' = fromIntegral logLen liftM Just $ allocaArray logLen' $ \ ptr -> do glGetProgramInfoLog name logLen nullPtr ptr - peekCString ptr + peekCString ptr + createUBuffer 0 = return undefined + createUBuffer uSize = lift $ liftContextIO $ do bname <- alloca (\ptr -> glGenBuffers 1 ptr >> peek ptr) + glBindBuffer GL_COPY_WRITE_BUFFER bname + glBufferData GL_COPY_WRITE_BUFFER (fromIntegral uSize) nullPtr GL_STREAM_DRAW + return bname + addPstrUniform _ 0 = id + addPstrUniform bname uSize = Map.insert 0 $ \_ bind -> glBindBufferRange GL_UNIFORM_BUFFER (fromIntegral bind) bname 0 (fromIntegral uSize) + + + orderedUnion :: Ord a => [a] -> [a] -> [a] orderedUnion xxs@(x:xs) yys@(y:ys) | x == y = x : orderedUnion xs ys | x < y = x : orderedUnion xs yys @@ -253,30 +272,30 @@ -- Optimization, save gl calls to already bound buffers/samplers makeBind :: Map.IntMap Int -> Map.IntMap (s -> Binding -> IO x) -> [(Int, Int)] -> (s -> Asserter x -> IO (), Map.IntMap Int) makeBind m iom ((n,b):xs) = (g, m'') - where + where (f, m') = makeBind m iom xs (io, m'') = case Map.lookup b m' of Just x | x == n -> (\_ _ -> return (), m') - _ -> (\s asserter -> (iom ! n) s b >>= asserter, Map.insert b n m') - g s a = f s a >> io s a -makeBind m _ [] = (\_ _ -> return (), m) + _ -> (\s asserter -> (iom ! n) s b >>= asserter, Map.insert b n m') + g s a = f s a >> io s a +makeBind m _ [] = (\_ _ -> return (), m) allocate :: Int -> [[Int]] -> [[Int]] allocate mx = allocate' Map.empty [] - where allocate' m ys ((x:xs):xss) | Just a <- Map.lookup x m = allocate' m (a:ys) (xs:xss) + where allocate' m ys ((x:xs):xss) | Just a <- Map.lookup x m = allocate' m (a:ys) (xs:xss) | ms <- Map.size m, ms < mx = allocate' (Map.insert x ms m) (ms:ys) (xs:xss) | otherwise = let (ek,ev) = findLastUsed m mx (ys ++ xs ++ concat xss) in allocate' (Map.insert x ev (Map.delete ek m)) (ev:ys) (xs:xss) - allocate' m ys (_:xss) = reverse ys : allocate' m [] xss + allocate' m ys (_:xss) = reverse ys : allocate' m [] xss allocate' _ _ [] = [] - - findLastUsed m n (x:xs) | n > 1 = let (a, m') = Map.updateLookupWithKey (const $ const Nothing) x m + + findLastUsed m n (x:xs) | n > 1 = let (a, m') = Map.updateLookupWithKey (const $ const Nothing) x m n' = if isJust a then n-1 else n in findLastUsed m' n' xs - findLastUsed m _ _ = head $ Map.toList m - -getFBOerror :: MonadIO m => m (Maybe String) + findLastUsed m _ _ = head $ Map.toList m + +getFBOerror :: MonadIO m => m (Maybe String) getFBOerror = do status <- glCheckFramebufferStatus GL_DRAW_FRAMEBUFFER return $ case status of GL_FRAMEBUFFER_COMPLETE -> Nothing GL_FRAMEBUFFER_UNSUPPORTED -> Just "The combination of draw images (FBO) used in the render call is unsupported by this graphics driver\n" - _ -> error "GPipe internal FBO error" + _ -> error "GPipe internal FBO error"
src/Graphics/GPipe/Internal/FrameBuffer.hs view
@@ -149,10 +149,10 @@ mapM_ g xs makeDrawcall :: (ExprM (), GlobDeclM (), s -> (Maybe (IO FBOKeys, IO ()), IO ())) -> FragmentStreamData -> IO (Drawcall s) -makeDrawcall (sh, shd, io) (FragmentStreamData rastN shaderpos (PrimitiveStreamData primN) keep) = +makeDrawcall (sh, shd, io) (FragmentStreamData rastN shaderpos (PrimitiveStreamData primN ubuff) keep) = do (fsource, funis, fsamps, _, prevDecls, prevS) <- runExprM shd (discard keep >> sh) (vsource, vunis, vsamps, vinps, _, _) <- runExprM prevDecls (prevS >> shaderpos) - return $ Drawcall io primN rastN vsource fsource vinps vunis vsamps funis fsamps + return $ Drawcall io primN rastN vsource fsource vinps vunis vsamps funis fsamps ubuff setColor :: forall c. ColorSampleable c => c -> Int -> FragColor c -> (ExprM (), GlobDeclM ()) setColor ct n c = let name = "out" ++ show n
src/Graphics/GPipe/Internal/PrimitiveStream.hs view
@@ -3,8 +3,8 @@ module Graphics.GPipe.Internal.PrimitiveStream where import Control.Monad.Trans.Class -import Control.Monad.Trans.Writer.Lazy -import Control.Monad.Trans.State.Lazy +import Control.Monad.Trans.State.Strict +import Control.Monad.Trans.Reader import Prelude hiding (length, id, (.)) import Graphics.GPipe.Internal.Buffer import Graphics.GPipe.Internal.Expr @@ -12,16 +12,21 @@ import Graphics.GPipe.Internal.Compiler import Graphics.GPipe.Internal.PrimitiveArray import Graphics.GPipe.Internal.Context +import Graphics.GPipe.Internal.Uniform import Control.Category import Control.Arrow +import Control.Monad (void) import Data.Monoid (Monoid(..)) import Data.IntMap.Lazy (insert) import Data.Word import Data.Int +import Foreign.Storable +import Foreign.Ptr +import qualified Data.IntMap as Map import Graphics.GL.Core33 +import Graphics.GL.Types import Foreign.Marshal.Utils -import Foreign.Ptr (intPtrToPtr) import Data.IORef import Linear.V4 import Linear.V3 @@ -34,7 +39,8 @@ import Data.Maybe (fromMaybe) type DrawCallName = Int -data PrimitiveStreamData = PrimitiveStreamData DrawCallName +type USize = Int +data PrimitiveStreamData = PrimitiveStreamData DrawCallName USize -- | A @'PrimitiveStream' t a @ is a stream of primitives of type @t@ where the vertices are values of type @a@. You -- can operate a stream's vertex values using the 'Functor' instance (this will result in a shader running on the GPU). @@ -46,7 +52,7 @@ fmap f (PrimitiveStream xs) = PrimitiveStream $ map (first f) xs -- | This class constraints which buffer types can be turned into vertex values, and what type those values have. -class BufferFormat a => VertexInput a where +class VertexInput a where -- | The type the buffer value will be turned into once it becomes a vertex value. type VertexFormat a -- | An arrow action that turns a value from it's buffer representation to it's vertex representation. Use 'toVertex' from @@ -56,20 +62,38 @@ -- @proc ~pattern -> do ...@. toVertex :: ToVertex a (VertexFormat a) +type UniOffset = Int + -- | The arrow type for 'toVertex'. -newtype ToVertex a b = ToVertex (Kleisli (StateT Int (Writer [Binding -> (IO VAOKey, IO ())])) a b) deriving (Category, Arrow) +data ToVertex a b = ToVertex + !(Kleisli (StateT (Ptr ()) IO) a b) + !(Kleisli (StateT (Int, UniOffset, OffsetToSType) (Reader (Int -> ExprM String))) a b) + !(Kleisli (State [Binding -> (IO VAOKey, IO ())]) a b) +instance Category ToVertex where + {-# INLINE id #-} + id = ToVertex id id id + {-# INLINE (.) #-} + ToVertex a b c . ToVertex x y z = ToVertex (a.x) (b.y) (c.z) +instance Arrow ToVertex where + {-# INLINE arr #-} + arr f = ToVertex (arr f) (arr f) (arr f) + {-# INLINE first #-} + first (ToVertex a b c) = ToVertex (first a) (first b) (first c) + + -- | Create a primitive stream from a primitive array provided from the shader environment. toPrimitiveStream :: forall os f s a p. VertexInput a => (s -> PrimitiveArray p a) -> Shader os f s (PrimitiveStream p (VertexFormat a)) toPrimitiveStream sf = Shader $ do n <- getName uniAl <- askUniformAlignment - let sampleBuffer = makeBuffer undefined undefined uniAl :: Buffer os a - x = fst $ runWriter (evalStateT (mf $ bufBElement sampleBuffer $ BInput 0 0) 0) + let err = error "toPrimitiveStream is creating values that are dependant on the actual HostFormat values, this is not allowed since it doesn't allow static creation of shaders" + (x,(_,uSize, offToStype)) = runReader (runStateT (mf err) (0,0,mempty)) (useUniform (buildUDecl offToStype) 0) -- 0 is special blockname for the one used by primitive stream doForInputArray n (map drawcall . getPrimitiveArray . sf) - return $ PrimitiveStream [(x, (Nothing, PrimitiveStreamData n))] + + return $ PrimitiveStream [(x, (Nothing, PrimitiveStreamData n uSize))] where - ToVertex (Kleisli mf) = toVertex :: ToVertex a (VertexFormat a) + ToVertex (Kleisli uWriter) (Kleisli mf) (Kleisli bindingm) = toVertex :: ToVertex a (VertexFormat a) drawcall (PrimitiveArraySimple p l a) binds = (attribs a binds, glDrawArrays (toGLtopology p) 0 (fromIntegral l)) drawcall (PrimitiveArrayIndexed p i a) binds = (attribs a binds, do bindIndexBuffer i @@ -94,11 +118,21 @@ assignIxs _ _ [] _ = [] assignIxs _ _ _ _ = error "Too few attributes generated in toPrimitiveStream" - attribs a binds = first sequence $ second sequence_ $ unzip $ assignIxs 0 0 binds $ execWriter (runStateT (mf a) 0) + attribs a (binds, uBname, uSize) = let + (_,bindsAssoc) = runState (bindingm a) [] + (ioVaokeys, ios) = unzip $ assignIxs 0 0 binds $ reverse bindsAssoc + in (writeUBuffer uBname uSize a >> sequence ioVaokeys, sequence_ ios) - doForInputArray :: Int -> (s -> [[Binding] -> ((IO [VAOKey], IO ()), IO ())]) -> ShaderM s () + doForInputArray :: Int -> (s -> [([Binding], GLuint, Int) -> ((IO [VAOKey], IO ()), IO ())]) -> ShaderM s () doForInputArray n io = modifyRenderIO (\s -> s { inputArrayToRenderIOs = insert n io (inputArrayToRenderIOs s) } ) + writeUBuffer _ 0 _ = return () -- If the uniform buffer is size 0 there is no buffer + writeUBuffer bname size a = do + glBindBuffer GL_COPY_WRITE_BUFFER bname + ptr <- glMapBufferRange GL_COPY_WRITE_BUFFER 0 (fromIntegral size) (GL_MAP_WRITE_BIT + GL_MAP_INVALIDATE_BUFFER_BIT) + void $ runStateT (uWriter a) ptr + void $ glUnmapBuffer GL_COPY_WRITE_BUFFER + data InputIndices = InputIndices { inputVertexID :: VInt, inputInstanceID :: VInt @@ -115,37 +149,73 @@ withPointSize :: (a -> PointSize -> (b, PointSize)) -> PrimitiveStream Points a -> PrimitiveStream Points b withPointSize f (PrimitiveStream xs) = PrimitiveStream $ map (\(a, (ps, d)) -> let (b, ps') = f a (fromMaybe (scalarS' "1") ps) in (b, (Just ps', d))) xs -makeVertexFx norm x f styp typ b = do - n <- get - put $ n + 1 - let combOffset = bStride b * bSkipElems b + bOffset b - lift $ tell [\ix -> ( do bn <- readIORef $ bName b - return $ VAOKey bn combOffset x norm (bInstanceDiv b) - , do bn <- readIORef $ bName b - let ix' = fromIntegral ix - glEnableVertexAttribArray ix' - glBindBuffer GL_ARRAY_BUFFER bn - glVertexAttribDivisor ix' (fromIntegral $ bInstanceDiv b) - glVertexAttribPointer ix' x typ (fromBool norm) (fromIntegral $ bStride b) (intPtrToPtr $ fromIntegral combOffset))] - return (f styp $ useVInput styp n) +makeVertexF x f styp _ = do + (n,uoffset,m) <- get + put (n + 1, uoffset,m) + return (f styp $ useVInput styp n) -makeVertexFnorm = makeVertexFx True -makeVertexF = makeVertexFx False +append x = modify (x:) -makeVertexI x f styp typ b = do - n <- get - put $ n + 1 - let combOffset = bStride b * bSkipElems b + bOffset b - lift $ tell [\ix -> ( do bn <- readIORef $ bName b - return $ VAOKey bn combOffset x False (bInstanceDiv b) - , do bn <- readIORef $ bName b - let ix' = fromIntegral ix - glEnableVertexAttribArray ix' - glBindBuffer GL_ARRAY_BUFFER bn - glVertexAttribDivisor ix' (fromIntegral $ bInstanceDiv b) - glVertexAttribIPointer ix' x typ (fromIntegral $ bStride b) (intPtrToPtr $ fromIntegral combOffset))] - return (f styp $ useVInput styp n) +makeBindVertexFx norm x typ b = do + let combOffset = bStride b * bSkipElems b + bOffset b + append (\ix -> ( do bn <- readIORef $ bName b + return $ VAOKey bn combOffset x norm (bInstanceDiv b) + , do bn <- readIORef $ bName b + let ix' = fromIntegral ix + glEnableVertexAttribArray ix' + glBindBuffer GL_ARRAY_BUFFER bn + glVertexAttribDivisor ix' (fromIntegral $ bInstanceDiv b) + glVertexAttribPointer ix' x typ (fromBool norm) (fromIntegral $ bStride b) (intPtrToPtr $ fromIntegral combOffset))) + return undefined +makeBindVertexFnorm = makeBindVertexFx True +makeBindVertexF = makeBindVertexFx False + +makeVertexI x f styp _ = do + (n, uoffset,m) <- get + put (n + 1, uoffset,m) + return (f styp $ useVInput styp n) +makeBindVertexI x typ b = do + let combOffset = bStride b * bSkipElems b + bOffset b + append (\ix -> ( do bn <- readIORef $ bName b + return $ VAOKey bn combOffset x False (bInstanceDiv b) + , do bn <- readIORef $ bName b + let ix' = fromIntegral ix + glEnableVertexAttribArray ix' + glBindBuffer GL_ARRAY_BUFFER bn + glVertexAttribDivisor ix' (fromIntegral $ bInstanceDiv b) + glVertexAttribIPointer ix' x typ (fromIntegral $ bStride b) (intPtrToPtr $ fromIntegral combOffset))) + return undefined +noWriter = Kleisli (const $ return undefined) + +-- Uniform values + +toUniformVertex :: forall a b. Storable a => SType -> ToVertex a (S V b) +toUniformVertex styp = ToVertex (Kleisli uWriter) (Kleisli makeV) (Kleisli makeBind) + where + size = sizeOf (undefined :: a) + uWriter a = do ptr <- get + put (ptr `plusPtr` size) + lift $ poke (castPtr ptr) a + return undefined + makeV a = do (n, uoffset,m) <- get + put (n, uoffset + size, Map.insert uoffset styp m) + useF <- lift ask + return $ S $ useF uoffset + makeBind a = return undefined + +instance VertexInput Float where + type VertexFormat Float = VFloat + toVertex = toUniformVertex STypeFloat + +instance VertexInput Int32 where + type VertexFormat Int32 = VInt + toVertex = toUniformVertex STypeInt + +instance VertexInput Word32 where + type VertexFormat Word32 = VWord + toVertex = toUniformVertex STypeUInt + -- scalars unBnorm :: Normalized t -> t @@ -153,134 +223,134 @@ instance VertexInput (B Float) where type VertexFormat (B Float) = VFloat - toVertex = ToVertex $ Kleisli $ makeVertexF 1 (const S) STypeFloat GL_FLOAT + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 1 (const S) STypeFloat) (Kleisli $ makeBindVertexF 1 GL_FLOAT) instance VertexInput (Normalized (B Int32)) where type VertexFormat (Normalized (B Int32)) = VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 1 (const S) STypeFloat GL_INT . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 1 (const S) STypeFloat . unBnorm) (Kleisli $ makeBindVertexFnorm 1 GL_INT . unBnorm) instance VertexInput (Normalized (B Word32)) where type VertexFormat (Normalized (B Word32)) = VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 1 (const S) STypeFloat GL_UNSIGNED_INT . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 1 (const S) STypeFloat . unBnorm) (Kleisli $ makeBindVertexFnorm 1 GL_UNSIGNED_INT . unBnorm) instance VertexInput (B Int32) where type VertexFormat (B Int32) = VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 1 (const S) STypeInt GL_INT + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 1 (const S) STypeInt) (Kleisli $ makeBindVertexI 1 GL_INT) instance VertexInput (B Word32) where type VertexFormat (B Word32) = VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 1 (const S) STypeUInt GL_UNSIGNED_INT + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 1 (const S) STypeUInt) (Kleisli $ makeBindVertexI 1 GL_UNSIGNED_INT) -- B2 instance VertexInput (B2 Float) where type VertexFormat (B2 Float) = V2 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexF 2 vec2S (STypeVec 2) GL_FLOAT . unB2 + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 2 vec2S (STypeVec 2) . unB2) (Kleisli $ makeBindVertexF 2 GL_FLOAT . unB2) instance VertexInput (Normalized (B2 Int32)) where type VertexFormat (Normalized (B2 Int32)) = V2 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 2 vec2S (STypeVec 2) GL_INT . unB2 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 2 vec2S (STypeVec 2) . unB2 . unBnorm) (Kleisli $ makeBindVertexFnorm 2 GL_INT . unB2 . unBnorm) instance VertexInput (Normalized (B2 Int16)) where type VertexFormat (Normalized (B2 Int16)) = V2 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 2 vec2S (STypeVec 2) GL_SHORT . unB2 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 2 vec2S (STypeVec 2) . unB2 . unBnorm) (Kleisli $ makeBindVertexFnorm 2 GL_SHORT . unB2 . unBnorm) instance VertexInput (Normalized (B2 Word32)) where type VertexFormat (Normalized (B2 Word32)) = V2 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 2 vec2S (STypeVec 2) GL_UNSIGNED_INT . unB2 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 2 vec2S (STypeVec 2) . unB2 . unBnorm) (Kleisli $ makeBindVertexFnorm 2 GL_UNSIGNED_INT . unB2 . unBnorm) instance VertexInput (Normalized (B2 Word16)) where type VertexFormat (Normalized (B2 Word16)) = V2 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 2 vec2S (STypeVec 2) GL_UNSIGNED_SHORT . unB2 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 2 vec2S (STypeVec 2) . unB2 . unBnorm) (Kleisli $ makeBindVertexFnorm 2 GL_UNSIGNED_SHORT . unB2 . unBnorm) instance VertexInput (B2 Int32) where type VertexFormat (B2 Int32) = V2 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 2 vec2S (STypeIVec 2) GL_INT . unB2 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 2 vec2S (STypeIVec 2) . unB2) (Kleisli $ makeBindVertexI 2 GL_INT . unB2) instance VertexInput (B2 Int16) where type VertexFormat (B2 Int16) = V2 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 2 vec2S (STypeIVec 2) GL_SHORT . unB2 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 2 vec2S (STypeIVec 2) . unB2) (Kleisli $ makeBindVertexI 2 GL_SHORT . unB2) instance VertexInput (B2 Word32) where type VertexFormat (B2 Word32) = V2 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 2 vec2S (STypeUVec 2) GL_UNSIGNED_INT . unB2 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 2 vec2S (STypeUVec 2) . unB2) (Kleisli $ makeBindVertexI 2 GL_UNSIGNED_INT . unB2) instance VertexInput (B2 Word16) where type VertexFormat (B2 Word16) = V2 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 2 vec2S (STypeUVec 2) GL_UNSIGNED_SHORT . unB2 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 2 vec2S (STypeUVec 2) . unB2) (Kleisli $ makeBindVertexI 2 GL_UNSIGNED_SHORT . unB2) -- B3 instance VertexInput (B3 Float) where type VertexFormat (B3 Float) = V3 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexF 3 vec3S (STypeVec 3) GL_FLOAT . unB3 + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 3 vec3S (STypeVec 3) . unB3) (Kleisli $ makeBindVertexF 3 GL_FLOAT . unB3) instance VertexInput (Normalized (B3 Int32)) where type VertexFormat (Normalized (B3 Int32)) = V3 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 3 vec3S (STypeVec 3) GL_INT . unB3 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 3 vec3S (STypeVec 3) . unB3 . unBnorm) (Kleisli $ makeBindVertexFnorm 3 GL_INT . unB3 . unBnorm) instance VertexInput (Normalized (B3 Int16)) where type VertexFormat (Normalized (B3 Int16)) = V3 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 3 vec3S (STypeVec 3) GL_SHORT . unB3 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 3 vec3S (STypeVec 3) . unB3 . unBnorm) (Kleisli $ makeBindVertexFnorm 3 GL_SHORT . unB3 . unBnorm) instance VertexInput (Normalized (B3 Int8)) where type VertexFormat (Normalized (B3 Int8)) = V3 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 3 vec3S (STypeVec 3) GL_BYTE . unB3 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 3 vec3S (STypeVec 3) . unB3 . unBnorm) (Kleisli $ makeBindVertexFnorm 3 GL_BYTE . unB3 . unBnorm) instance VertexInput (Normalized (B3 Word32)) where type VertexFormat (Normalized (B3 Word32)) = V3 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 3 vec3S (STypeVec 3) GL_UNSIGNED_INT . unB3 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 3 vec3S (STypeVec 3) . unB3 . unBnorm) (Kleisli $ makeBindVertexFnorm 3 GL_UNSIGNED_INT . unB3 . unBnorm) instance VertexInput (Normalized (B3 Word16)) where type VertexFormat (Normalized (B3 Word16)) = V3 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 3 vec3S (STypeVec 3) GL_UNSIGNED_SHORT . unB3 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 3 vec3S (STypeVec 3) . unB3 . unBnorm) (Kleisli $ makeBindVertexFnorm 3 GL_UNSIGNED_SHORT . unB3 . unBnorm) instance VertexInput (Normalized (B3 Word8)) where type VertexFormat (Normalized (B3 Word8)) = V3 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 3 vec3S (STypeVec 3) GL_UNSIGNED_BYTE . unB3 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 3 vec3S (STypeVec 3) . unB3 . unBnorm) (Kleisli $ makeBindVertexFnorm 3 GL_UNSIGNED_BYTE . unB3 . unBnorm) instance VertexInput (B3 Int32) where type VertexFormat (B3 Int32) = V3 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 3 vec3S (STypeIVec 3) GL_INT . unB3 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 3 vec3S (STypeIVec 3) . unB3) (Kleisli $ makeBindVertexI 3 GL_INT . unB3) instance VertexInput (B3 Int16) where type VertexFormat (B3 Int16) = V3 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 3 vec3S (STypeIVec 3) GL_SHORT . unB3 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 3 vec3S (STypeIVec 3) . unB3) (Kleisli $ makeBindVertexI 3 GL_SHORT . unB3) instance VertexInput (B3 Int8) where type VertexFormat (B3 Int8) = V3 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 3 vec3S (STypeIVec 3) GL_BYTE . unB3 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 3 vec3S (STypeIVec 3) . unB3) (Kleisli $ makeBindVertexI 3 GL_BYTE . unB3) instance VertexInput (B3 Word32) where type VertexFormat (B3 Word32) = V3 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 3 vec3S (STypeUVec 3) GL_UNSIGNED_INT . unB3 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 3 vec3S (STypeUVec 3) . unB3) (Kleisli $ makeBindVertexI 3 GL_UNSIGNED_INT . unB3) instance VertexInput (B3 Word16) where type VertexFormat (B3 Word16) = V3 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 3 vec3S (STypeUVec 3) GL_UNSIGNED_SHORT . unB3 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 3 vec3S (STypeUVec 3) . unB3) (Kleisli $ makeBindVertexI 3 GL_UNSIGNED_SHORT . unB3) instance VertexInput (B3 Word8) where type VertexFormat (B3 Word8) = V3 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 3 vec3S (STypeUVec 3) GL_UNSIGNED_BYTE . unB3 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 3 vec3S (STypeUVec 3) . unB3) (Kleisli $ makeBindVertexI 3 GL_UNSIGNED_BYTE . unB3) -- B4 instance VertexInput (B4 Float) where type VertexFormat (B4 Float) = V4 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexF 4 vec4S (STypeVec 4) GL_FLOAT . unB4 + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 4 vec4S (STypeVec 4) . unB4) (Kleisli $ makeBindVertexF 4 GL_FLOAT . unB4) instance VertexInput (Normalized (B4 Int32)) where type VertexFormat (Normalized (B4 Int32)) = V4 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 4 vec4S (STypeVec 4) GL_INT . unB4 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 4 vec4S (STypeVec 4) . unB4 . unBnorm) (Kleisli $ makeBindVertexFnorm 4 GL_INT . unB4 . unBnorm) instance VertexInput (Normalized (B4 Int16)) where type VertexFormat (Normalized (B4 Int16)) = V4 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 4 vec4S (STypeVec 4) GL_SHORT . unB4 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 4 vec4S (STypeVec 4) . unB4 . unBnorm) (Kleisli $ makeBindVertexFnorm 4 GL_SHORT . unB4 . unBnorm) instance VertexInput (Normalized (B4 Int8)) where type VertexFormat (Normalized (B4 Int8)) = V4 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 4 vec4S (STypeVec 4) GL_BYTE . unB4 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 4 vec4S (STypeVec 4) . unB4 . unBnorm) (Kleisli $ makeBindVertexFnorm 4 GL_BYTE . unB4 . unBnorm) instance VertexInput (Normalized (B4 Word32)) where type VertexFormat (Normalized (B4 Word32)) = V4 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 4 vec4S (STypeVec 4) GL_UNSIGNED_INT . unB4 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 4 vec4S (STypeVec 4) . unB4 . unBnorm) (Kleisli $ makeBindVertexFnorm 4 GL_UNSIGNED_INT . unB4 . unBnorm) instance VertexInput (Normalized (B4 Word16)) where type VertexFormat (Normalized (B4 Word16)) = V4 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 4 vec4S (STypeVec 4) GL_UNSIGNED_SHORT . unB4 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 4 vec4S (STypeVec 4) . unB4 . unBnorm) (Kleisli $ makeBindVertexFnorm 4 GL_UNSIGNED_SHORT . unB4 . unBnorm) instance VertexInput (Normalized (B4 Word8)) where type VertexFormat (Normalized (B4 Word8)) = V4 VFloat - toVertex = ToVertex $ Kleisli $ makeVertexFnorm 4 vec4S (STypeVec 4) GL_UNSIGNED_BYTE . unB4 . unBnorm + toVertex = ToVertex noWriter (Kleisli $ makeVertexF 4 vec4S (STypeVec 4) . unB4 . unBnorm) (Kleisli $ makeBindVertexFnorm 4 GL_UNSIGNED_BYTE . unB4 . unBnorm) instance VertexInput (B4 Int32) where type VertexFormat (B4 Int32) = V4 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 4 vec4S (STypeIVec 4) GL_INT . unB4 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 4 vec4S (STypeIVec 4) . unB4) (Kleisli $ makeBindVertexI 4 GL_INT . unB4) instance VertexInput (B4 Int16) where type VertexFormat (B4 Int16) = V4 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 4 vec4S (STypeIVec 4) GL_SHORT . unB4 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 4 vec4S (STypeIVec 4) . unB4) (Kleisli $ makeBindVertexI 4 GL_SHORT . unB4) instance VertexInput (B4 Int8) where type VertexFormat (B4 Int8) = V4 VInt - toVertex = ToVertex $ Kleisli $ makeVertexI 4 vec4S (STypeIVec 4) GL_BYTE . unB4 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 4 vec4S (STypeIVec 4) . unB4) (Kleisli $ makeBindVertexI 4 GL_BYTE . unB4) instance VertexInput (B4 Word32) where type VertexFormat (B4 Word32) = V4 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 4 vec4S (STypeUVec 4) GL_UNSIGNED_INT . unB4 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 4 vec4S (STypeUVec 4) . unB4) (Kleisli $ makeBindVertexI 4 GL_UNSIGNED_INT . unB4) instance VertexInput (B4 Word16) where type VertexFormat (B4 Word16) = V4 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 4 vec4S (STypeUVec 4) GL_UNSIGNED_SHORT . unB4 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 4 vec4S (STypeUVec 4) . unB4) (Kleisli $ makeBindVertexI 4 GL_UNSIGNED_SHORT . unB4) instance VertexInput (B4 Word8) where type VertexFormat (B4 Word8) = V4 VWord - toVertex = ToVertex $ Kleisli $ makeVertexI 4 vec4S (STypeUVec 4) GL_UNSIGNED_BYTE . unB4 + toVertex = ToVertex noWriter (Kleisli $ makeVertexI 4 vec4S (STypeUVec 4) . unB4) (Kleisli $ makeBindVertexI 4 GL_UNSIGNED_BYTE . unB4) instance VertexInput () where type VertexFormat () = ()
src/Graphics/GPipe/Internal/Shader.hs view
@@ -6,7 +6,7 @@ Shader(..), ShaderM(..), ShaderState(..), - CompiledShader, + CompiledShader, Render(..), getName, tellDrawcall, @@ -26,10 +26,10 @@ import Graphics.GPipe.Internal.Compiler import Graphics.GPipe.Internal.Context import Graphics.GPipe.Internal.Buffer -import Control.Monad.Trans.State -import qualified Control.Monad.Trans.State.Strict as StrictState +import Control.Monad.Trans.State +import qualified Control.Monad.Trans.State.Strict as StrictState import Control.Monad.IO.Class -import qualified Data.IntSet as Set +import qualified Data.IntSet as Set import Control.Monad.Trans.Writer.Lazy (tell, WriterT(..), runWriterT) import Control.Monad.Exception (MonadException) import Control.Applicative (Applicative, Alternative, (<|>)) @@ -45,55 +45,55 @@ data ShaderState s = ShaderState Int (RenderIOState s) newShaderState :: ShaderState s -newShaderState = ShaderState 0 newRenderIOState +newShaderState = ShaderState 1 newRenderIOState getName :: ShaderM s Int getName = do ShaderState n r <- ShaderM $ lift $ lift $ lift get ShaderM $ lift $ lift $ lift $ put $ ShaderState (n+1) r return n -askUniformAlignment = ShaderM ask +askUniformAlignment = ShaderM ask modifyRenderIO :: (RenderIOState s -> RenderIOState s) -> ShaderM s () modifyRenderIO f = ShaderM $ lift $ lift $ lift $ modify (\(ShaderState a s) -> ShaderState a (f s)) tellDrawcall :: IO (Drawcall s) -> ShaderM s () -tellDrawcall dc = ShaderM $ lift $ tell ([dc], mempty) +tellDrawcall dc = ShaderM $ lift $ tell ([dc], mempty) mapDrawcall :: (s -> s') -> Drawcall s' -> Drawcall s -mapDrawcall f (Drawcall a b c d e g h i j k) = Drawcall (a . f) b c d e g h i j k +mapDrawcall f (Drawcall a b c d e g h i j k m) = Drawcall (a . f) b c d e g h i j k m newtype ShaderM s a = ShaderM (ReaderT UniformAlignment (WriterT ([IO (Drawcall s)], s -> All) (ListT (State (ShaderState s)))) a) deriving (MonadPlus, Monad, Alternative, Applicative, Functor) --- | The monad in which all GPU computations are done. 'Shader os f s a' lives in an object space 'os' and a context with format 'f', closing over an environent of type 's'. +-- | The monad in which all GPU computations are done. 'Shader os f s a' lives in an object space 'os' and a context with format 'f', closing over an environent of type 's'. newtype Shader os f s a = Shader (ShaderM s a) deriving (MonadPlus, Monad, Alternative, Applicative, Functor) --- | Map the environment to a different environment and run a Shader in that sub environment, returning it's result. +-- | Map the environment to a different environment and run a Shader in that sub environment, returning it's result. mapShader :: (s -> s') -> Shader os f s' a -> Shader os f s a mapShader f (Shader (ShaderM m)) = Shader $ ShaderM $ do uniAl <- ask lift $ WriterT $ ListT $ do ShaderState x s <- get let (adcs, ShaderState x' s') = runState (runListT (runWriterT (runReaderT m uniAl))) (ShaderState x newRenderIOState) - put $ ShaderState x' (mapRenderIOState f s' s) + put $ ShaderState x' (mapRenderIOState f s' s) return $ map (\(a,(dcs, disc)) -> (a, (map (>>= (return . mapDrawcall f)) dcs, disc . f))) adcs --- | Conditionally run the effects of a shader when a 'Maybe' value is 'Just' something. +-- | Conditionally run the effects of a shader when a 'Maybe' value is 'Just' something. maybeShader :: (s -> Maybe s') -> Shader os f s' () -> Shader os f s () -maybeShader f m = (guard' (isJust . f) >> mapShader (fromJust . f) m) <|> guard' (isNothing . f) +maybeShader f m = (guard' (isJust . f) >> mapShader (fromJust . f) m) <|> guard' (isNothing . f) -- | Like 'guard', but dependent on the 'Shaders' environment value. Since this will be evaluated at shader run time, as opposed to shader compile time for 'guard', -- using this to do recursion will make 'compileShader' diverge. You can break that divergence by combining it with a normal 'guard' and a maximum loop count. guard' :: (s -> Bool) -> Shader os f s () -guard' f = Shader $ ShaderM $ lift $ tell (mempty, All . f) +guard' f = Shader $ ShaderM $ lift $ tell (mempty, All . f) -- | Select one of two 'Shader' actions based on whether an 'Either' value is 'Left' or 'Right'. chooseShader :: (s -> Either s' s'') -> Shader os f s' a -> Shader os f s'' a -> Shader os f s a -chooseShader f a b = (guard' (isLeft . f) >> mapShader (fromLeft . f) a) <|> (guard' (isRight . f) >> mapShader (fromRight . f) b) +chooseShader f a b = (guard' (isLeft . f) >> mapShader (fromLeft . f) a) <|> (guard' (isRight . f) >> mapShader (fromRight . f) b) where fromLeft (Left x) = x - fromRight (Right x) = x + fromRight (Right x) = x --- | Discard all effects of a 'Shader' action (i.e., dont draw anything) and just return the resulting value. This makes it possible to use a 'Shader' written for a different context format. +-- | Discard all effects of a 'Shader' action (i.e., dont draw anything) and just return the resulting value. This makes it possible to use a 'Shader' written for a different context format. silenceShader :: Shader os f' s a -> Shader os f s a silenceShader (Shader (ShaderM m)) = Shader $ ShaderM $ do uniAl <- ask @@ -103,10 +103,10 @@ put s' return $ map (\ (a, (_, disc)) -> (a, ([], disc))) adcs --- | A compiled shader is just a function that takes an environment and returns a 'Render' action +-- | A compiled shader is just a function that takes an environment and returns a 'Render' action type CompiledShader os f s = s -> Render os f () --- | Compiles a shader into a 'CompiledShader'. This action will usually take a second or more, so put it during a loading sequence or something. +-- | Compiles a shader into a 'CompiledShader'. This action will usually take a second or more, so put it during a loading sequence or something. -- -- May throw a 'GPipeException' if the graphics driver doesn't support something in this shader (e.g. too many interpolated floats sent between a vertex and a fragment shader), -- or if shader evaluates to 'mzero'. @@ -116,20 +116,20 @@ let (adcs, ShaderState _ s) = runState (runListT (runWriterT (runReaderT m uniAl))) newShaderState f ((disc, runF):ys) e@(cd, env, asserter) = if getAll (disc env) then runF cd env asserter else f ys e f [] _ = error "render: Shader evaluated to mzero\n" - xs <- mapM (\(_,(dcs, disc)) -> do + xs <- mapM (\(_,(dcs, disc)) -> do runF <- compile dcs s return (disc, runF)) adcs return $ \ s -> Render $ do cd <- lift $ lift $ asks $ fst . snd texmap <- lift StrictState.get - let asserter x = when (Set.member x texmap) $ error "render: Running shader that samples from texture that currently has an image borrowed from it. Try run this shader from a separate render call where no images from the same texture are drawn to or cleared." + let asserter x = when (Set.member x texmap) $ error "render: Running shader that samples from texture that currently has an image borrowed from it. Try run this shader from a separate render call where no images from the same texture are drawn to or cleared." throwFromMaybe $ lift $ lift $ lift $ f xs (cd, s, asserter) throwFromMaybe m = do mErr <- m case mErr of Just err -> throwError err Nothing -> return () - + -- | Use this to run a shader that doesn't reference the context frame buffer, allowing the same shader to be run in another context with a different context format (but still with same object space). withoutContext :: Render os () () -> Render os f () -withoutContext (Render m) = Render m+withoutContext (Render m) = Render m