{-# LANGUAGE Arrows #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeSynonymInstances #-} module Graphics.GPipe.Internal.Buffer ( BufferFormat(..), BufferColor, Buffer(), ToBuffer(..), B(..), B2(..), B3(..), B4(..), toB22, toB3, toB21, toB12, toB11, Uniform(..), Normalized(..), BPacked(), BInput(..), newBuffer, writeBuffer, copyBuffer, BufferStartPos, bufSize, bufName, bufElementSize, bufferLength, bufBElement, bufTransformFeedback, bufferWriteInternal, makeBuffer, getUniformAlignment, UniformAlignment ) where import Control.Arrow (Arrow (arr, first), Kleisli (..), returnA) import Control.Category (Category (..)) import Control.Monad (void) import Control.Monad.IO.Class (MonadIO (..)) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Reader (Reader, ask, runReader) import Control.Monad.Trans.State.Strict (StateT (runStateT), get, put) import Control.Monad.Trans.Writer.Strict (WriterT (runWriterT), tell) import Data.IORef (IORef, newIORef, readIORef) import Data.Int (Int16, Int32, Int8) import Data.Word (Word16, Word32, Word8) import Foreign.Marshal.Alloc (alloca) import Foreign.Marshal.Utils (with) import Foreign.Ptr (Ptr, castPtr, minusPtr, nullPtr, plusPtr) import Foreign.Storable (Storable (peek, peekElemOff, poke, sizeOf)) import Graphics.GL.Core45 import Graphics.GL.Types (GLenum, GLuint) import Graphics.GPipe.Internal.Context (ContextHandler, ContextT, addContextFinalizer, addVAOBufferFinalizer, liftNonWinContextAsyncIO, liftNonWinContextIO) import Linear (Quaternion (..), V0 (..), V1 (..), V2 (..), V3 (..), V4 (..)) import Linear.Affine (Point (..)) import Linear.Plucker (Plucker (..)) import Prelude hiding (id, (.)) -- | The class that constraints which types can live in a buffer. class BufferFormat f where -- | The type a value of this format has when it lives on the host (i.e. normal Haskell world) type HostFormat f -- | An arrow action that turns a value from it's host representation to it's buffer representation. Use 'toBuffer' from -- the GPipe provided instances to operate in this arrow. Also note that this arrow needs to be able to return a value -- lazily, so ensure you use -- -- @proc ~pattern -> do ...@ toBuffer :: ToBuffer (HostFormat f) f getGlType :: f -> GLenum peekPixel :: f -> Ptr () -> IO (HostFormat f) getGlPaddedFormat :: f -> GLenum getGlType = error "This is only defined for BufferColor types" peekPixel = error "This is only defined for BufferColor types" getGlPaddedFormat = error "This is only defined for BufferColor types" -- | A @Buffer os b@ lives in the object space @os@ and contains elements of type @b@. data Buffer os b = Buffer { bufName :: BufferName , bufElementSize :: Int -- | Retrieve the number of elements in a buffer. , bufferLength :: Int , bufBElement :: BInput -> b , bufWriter :: Ptr () -> HostFormat b -> IO () , bufTransformFeedback :: IORef (Maybe (GLuint, GLuint)) } instance Eq (Buffer os b) where a == b = bufName a == bufName b bufSize :: forall os b. Buffer os b -> Int bufSize b = bufElementSize b * bufferLength b type BufferName = IORef GLuint type Offset = Int type Stride = Int type BufferStartPos = Int data BInput = BInput { bInSkipElems :: Int , bInInstanceDiv :: Int } 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 (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 {-# 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 comb AlignUnknown AlignUniform = AlignUniform comb AlignUnknown AlignPackedIndices = AlignPackedIndices comb AlignPackedIndices AlignUnknown = AlignPackedIndices comb AlignUnknown AlignUnknown = AlignUnknown comb _ _ = Align4 instance Arrow ToBuffer where {-# 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 } -- | An atomic buffer value that represents a vector of 2 'a's on the host. newtype B2 a = B2 { unB2 :: B a } -- Internal -- | An atomic buffer value that represents a vector of 3 'a's on the host. newtype B3 a = B3 { unB3 :: B a } -- Internal -- | An atomic buffer value that represents a vector of 4 'a's on the host. This works similar to '(B a, B a, B a, B a)' but has some performance advantage, especially when used -- in 'VertexArray's. newtype B4 a = B4 { unB4 :: B a } -- Internal -- | Split up a @'B4' a@ into two @'B2' a@s. toB22 :: forall a. (Storable a, BufferFormat (B2 a)) => B4 a -> (B2 a, B2 a) -- | Discard the last component of a @'B4' a@ to get a @'B3' a@. toB3 :: forall a. (Storable a, BufferFormat (B3 a)) => B4 a -> B3 a -- | Split up a @'B3' a@ into a @'B2' a@ and a @'B1' a@. toB21 :: forall a. (Storable a, BufferFormat (B a)) => B3 a -> (B2 a, B a) -- | Split up a @'B3' a@ into a @'B1' a@ and a @'B2' a@. toB12 :: forall a. (Storable a, BufferFormat (B a)) => B3 a -> (B a, B2 a) -- | Split up a @'B2' a@ into two @'B1' a@s. toB11 :: forall a. (Storable a, BufferFormat (B a)) => B2 a -> (B a, B a) toB22 (B4 b) = (B2 b, B2 $ b { bOffset = bOffset b + 2 * sizeOf (undefined :: a) }) toB3 (B4 b) = B3 b toB21 (B3 b) = (B2 b, b { bOffset = bOffset b + 2*sizeOf (undefined :: a) }) toB12 (B3 b) = (b, B2 $ b { bOffset = bOffset b + sizeOf (undefined :: a) }) toB11 (B2 b) = (b, b { bOffset = bOffset b + sizeOf (undefined :: a) }) -- | Any buffer value that is going to be used as a uniform needs to be wrapped in this newtype. This will cause is to be aligned -- properly for uniform usage. It can still be used as input for vertex arrays, but due to the uniform alignment it will probably be -- padded quite heavily and thus wasteful. newtype Uniform a = Uniform a -- | This wrapper is used for integer values to indicate that it should be interpreted as a floating point value, in the range [-1,1] or [0,1] depending on wether it is a -- signed or unsigned integer (i.e. 'Int' or 'Word'). newtype Normalized a = Normalized a -- | This works like a 'B a', but has an alignment smaller than 4 bytes that is the limit for vertex buffers, and thus cannot be used for those. -- Index buffers on the other hand need to be tightly packed, so you need to use this type for index buffers of 'Word8' or 'Word16'. newtype BPacked a = BPacked (B a) 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 offset <- get put $ offset + size 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 return undefined toBufferB :: forall a. Storable a => ToBuffer a (B a) toBufferB = toBufferBUnaligned -- Will always be 4 aligned, only 4 size types defined for B1 toBufferB2 :: forall a. Storable a => ToBuffer (V2 a) (B2 a) toBufferB2 = proc ~(V2 a b) -> do (if sizeOf (undefined :: a) >= 4 then alignWhen [(AlignUniform, 2 * sizeOf (undefined :: a))] else id) -< () -- Small optimization if someone puts non-usable types in a uniform a' <- toBufferBUnaligned -< a toBufferBUnaligned -< b returnA -< B2 a' -- Will always be 4 aligned, only 4 size types defined for B2 toBufferB3 :: forall a. Storable a => ToBuffer (V3 a) (B3 a) toBufferB3 = proc ~(V3 a b c) -> do (if sizeOf (undefined :: a) >= 4 then alignWhen [(AlignUniform, 4 * sizeOf (undefined :: a))] else id) -< () -- Small optimization if someone puts non-usable types in a uniform a' <- toBufferBUnaligned -< a toBufferBUnaligned -< b toBufferBUnaligned -< c (if sizeOf (undefined :: a) < 4 then alignWhen [(Align4, 4), (AlignUniform, 4)] else id) -< () -- For types smaller than 4 we need to pad returnA -< B3 a' toBufferB4 :: forall a. Storable a => ToBuffer (V4 a) (B4 a) toBufferB4 = proc ~(V4 a b c d) -> do (if sizeOf (undefined :: a) >= 4 then alignWhen [(AlignUniform, 4 * sizeOf (undefined :: a))] else id) -< () -- Small optimization if someone puts non-usable types in a uniform a' <- toBufferBUnaligned -< a toBufferBUnaligned -< b toBufferBUnaligned -< c toBufferBUnaligned -< d returnA -< B4 a' -- Will always be 4 aligned 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 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' instance BufferFormat a => BufferFormat (Normalized a) where type HostFormat (Normalized a) = HostFormat a toBuffer = arr Normalized . toBuffer getGlType (Normalized a) = getGlType a getGlPaddedFormat (Normalized a) = case getGlPaddedFormat a of GL_RGBA_INTEGER -> GL_RGBA GL_RGB_INTEGER -> GL_RGB GL_RG_INTEGER -> GL_RG GL_RED_INTEGER -> GL_RED x -> x instance BufferFormat a => BufferFormat (V0 a) where type HostFormat (V0 a) = V0 (HostFormat a) toBuffer = arr (const V0) instance BufferFormat a => BufferFormat (V1 a) where type HostFormat (V1 a) = V1 (HostFormat a) toBuffer = proc ~(V1 a) -> do a' <- toBuffer -< a returnA -< V1 a' instance BufferFormat a => BufferFormat (V2 a) where type HostFormat (V2 a) = V2 (HostFormat a) toBuffer = proc ~(V2 a b) -> do (a', b') <- toBuffer -< (a,b) returnA -< V2 a' b' instance BufferFormat a => BufferFormat (V3 a) where type HostFormat (V3 a) = V3 (HostFormat a) toBuffer = proc ~(V3 a b c) -> do (a', b', c') <- toBuffer -< (a, b, c) returnA -< V3 a' b' c' instance BufferFormat a => BufferFormat (V4 a) where type HostFormat (V4 a) = V4 (HostFormat a) toBuffer = proc ~(V4 a b c d) -> do (a', b', c', d') <- toBuffer -< (a, b, c, d) returnA -< V4 a' b' c' d' instance BufferFormat () where type HostFormat () = () toBuffer = arr (const ()) instance (BufferFormat a, BufferFormat b) => BufferFormat (a, b) where type HostFormat (a,b) = (HostFormat a, HostFormat b) toBuffer = proc ~(a, b) -> do a' <- toBuffer -< a b' <- toBuffer -< b returnA -< (a', b') instance (BufferFormat a, BufferFormat b, BufferFormat c) => BufferFormat (a, b, c) where type HostFormat (a,b,c) = (HostFormat a, HostFormat b, HostFormat c) toBuffer = proc ~(a, b, c) -> do ((a', b'), c') <- toBuffer -< ((a, b), c) returnA -< (a', b', c') instance (BufferFormat a, BufferFormat b, BufferFormat c, BufferFormat d) => BufferFormat (a, b, c, d) where type HostFormat (a,b,c,d) = (HostFormat a, HostFormat b, HostFormat c, HostFormat d) toBuffer = proc ~(a, b, c, d) -> do ((a', b', c'), d') <- toBuffer -< ((a, b, c), d) returnA -< (a', b', c', d') instance (BufferFormat a, BufferFormat b, BufferFormat c, BufferFormat d, BufferFormat e) => BufferFormat (a, b, c, d, e) where type HostFormat (a,b,c,d,e) = (HostFormat a, HostFormat b, HostFormat c, HostFormat d, HostFormat e) toBuffer = proc ~(a, b, c, d, e) -> do ((a', b', c', d'), e') <- toBuffer -< ((a, b, c, d), e) returnA -< (a', b', c', d', e') instance (BufferFormat a, BufferFormat b, BufferFormat c, BufferFormat d, BufferFormat e, BufferFormat f) => BufferFormat (a, b, c, d, e, f) where type HostFormat (a,b,c,d,e,f) = (HostFormat a, HostFormat b, HostFormat c, HostFormat d, HostFormat e, HostFormat f) toBuffer = proc ~(a, b, c, d, e, f) -> do ((a', b', c', d', e'), f') <- toBuffer -< ((a, b, c, d, e), f) returnA -< (a', b', c', d', e', f') instance (BufferFormat a, BufferFormat b, BufferFormat c, BufferFormat d, BufferFormat e, BufferFormat f, BufferFormat g) => BufferFormat (a, b, c, d, e, f, g) where type HostFormat (a,b,c,d,e,f,g) = (HostFormat a, HostFormat b, HostFormat c, HostFormat d, HostFormat e, HostFormat f, HostFormat g) toBuffer = proc ~(a, b, c, d, e, f, g) -> do ((a', b', c', d', e', f'), g') <- toBuffer -< ((a, b, c, d, e, f), g) returnA -< (a', b', c', d', e', f', g') instance BufferFormat a => BufferFormat (Quaternion a) where type HostFormat (Quaternion a) = Quaternion (HostFormat a) toBuffer = proc ~(Quaternion a v) -> do a' <- toBuffer -< a v' <- toBuffer -< v returnA -< Quaternion a' v' instance (BufferFormat (f a), BufferFormat a, HostFormat (f a) ~ f (HostFormat a)) => BufferFormat (Point f a) where type HostFormat (Point f a) = Point f (HostFormat a) toBuffer = proc ~(P a) -> do a' <- toBuffer -< a returnA -< P a' instance BufferFormat a => BufferFormat (Plucker a) where type HostFormat (Plucker a) = Plucker (HostFormat a) toBuffer = proc ~(Plucker a b c d e f) -> do a' <- toBuffer -< a b' <- toBuffer -< b c' <- toBuffer -< c d' <- toBuffer -< d e' <- toBuffer -< e f' <- toBuffer -< f returnA -< Plucker a' b' c' d' e' f' -- | Create a buffer with a specified number of elements. newBuffer :: (MonadIO m, BufferFormat b, ContextHandler ctx) => Int -> ContextT ctx os m (Buffer os b) newBuffer elementCount | elementCount < 0 = error "newBuffer, length negative" | otherwise = do (buffer, nameRef, name) <- liftNonWinContextIO $ do name <- alloca $ \ptr -> do glGenBuffers 1 ptr peek ptr nameRef <- newIORef name tfRef <- newIORef Nothing uniAl <- getUniformAlignment let buffer = makeBuffer' nameRef elementCount uniAl tfRef bname <- readIORef $ bufName buffer glBindBuffer GL_COPY_WRITE_BUFFER bname glBufferData GL_COPY_WRITE_BUFFER (fromIntegral $ bufSize buffer) nullPtr GL_STREAM_DRAW return (buffer, nameRef, name) addContextFinalizer nameRef $ with name (glDeleteBuffers 1) addVAOBufferFinalizer nameRef return buffer bufferWriteInternal :: Buffer os f -> Ptr () -> [HostFormat f] -> IO (Ptr ()) bufferWriteInternal b ptr (x:xs) = do bufWriter b ptr x bufferWriteInternal b (ptr `plusPtr` bufElementSize b) xs bufferWriteInternal _ ptr [] = return ptr -- | Write a buffer from the host (i.e. the normal Haskell world). writeBuffer :: (ContextHandler ctx, MonadIO m) => Buffer os b -> BufferStartPos -> [HostFormat b] -> ContextT ctx os m () writeBuffer buffer offset elems | offset < 0 || offset >= bufferLength buffer = error "writeBuffer, offset out of bounds" | otherwise = let maxElems = max 0 $ bufferLength buffer - offset elemSize = bufElementSize buffer off = fromIntegral $ offset * elemSize in liftNonWinContextAsyncIO $ do bname <- readIORef $ bufName buffer glBindBuffer GL_COPY_WRITE_BUFFER bname ptr <- glMapBufferRange GL_COPY_WRITE_BUFFER off (fromIntegral $maxElems * elemSize) (GL_MAP_WRITE_BIT + GL_MAP_FLUSH_EXPLICIT_BIT) end <- bufferWriteInternal buffer ptr (take maxElems elems) glFlushMappedBufferRange GL_COPY_WRITE_BUFFER off (fromIntegral $ end `minusPtr` ptr) void $ glUnmapBuffer GL_COPY_WRITE_BUFFER -- | Copies values from one buffer to another (of the same type). -- -- @copyBuffer fromBuffer fromStart toBuffer toStart length@ will copy @length@ elements from position @fromStart@ in @fromBuffer@ to position @toStart@ in @toBuffer@. copyBuffer :: (ContextHandler ctx, MonadIO m) => Buffer os b -> BufferStartPos -> Buffer os b -> BufferStartPos -> Int -> ContextT ctx os m () copyBuffer bFrom from bTo to len | from < 0 || from >= bufferLength bFrom = error "writeBuffer, source offset out of bounds" | to < 0 || to >= bufferLength bTo = error "writeBuffer, destination offset out of bounds" | len < 0 = error "writeBuffer, length negative" | len + from > bufferLength bFrom = error "writeBuffer, source buffer too small" | len + to > bufferLength bTo = error "writeBuffer, destination buffer too small" | otherwise = liftNonWinContextAsyncIO $ do bnamef <- readIORef $ bufName bFrom bnamet <- readIORef $ bufName bTo glBindBuffer GL_COPY_READ_BUFFER bnamef glBindBuffer GL_COPY_WRITE_BUFFER bnamet let elemSize = bufElementSize bFrom -- same as for bTo glCopyBufferSubData GL_COPY_READ_BUFFER GL_COPY_WRITE_BUFFER (fromIntegral $ from * elemSize) (fromIntegral $ to * elemSize) (fromIntegral $ len * elemSize) ---------------------------------------------- alignWhen :: [(AlignmentMode, Int)] -> ToBuffer a a alignWhen x = ToBuffer (Kleisli $ setElemAlignM x) (Kleisli return) (Kleisli setWriterAlignM) AlignUniform setElemAlignM :: [(AlignmentMode, Int)] -> b -> StateT Offset (WriterT [Int] (Reader (UniformAlignment, AlignmentMode))) b setElemAlignM x a = do (_,m) <- lift $ lift ask pad <- case lookup m x of Nothing -> return 0 Just al -> do offset <- get let pad = al - 1 - ((offset - 1) `mod` al) put $ offset + pad return pad lift $ tell [pad] return a setWriterAlignM :: b -> StateT (Ptr a, [Int]) IO b setWriterAlignM a = do (ptr, pad:pads) <- get put (ptr `plusPtr` pad, pads) return a getUniformAlignment :: IO Int getUniformAlignment = fromIntegral <$> alloca (\ ptr -> glGetIntegerv GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT ptr >> peek ptr) makeBuffer :: forall os b. BufferFormat b => BufferName -> Int -> UniformAlignment -> Buffer os b makeBuffer name elementCount uniformAlignment = makeBuffer' name elementCount uniformAlignment (error "Not meant to be used for transform feedback") makeBuffer' :: forall os b. BufferFormat b => BufferName -> Int -> UniformAlignment -> IORef (Maybe (GLuint, GLuint)) -> Buffer os b makeBuffer' name elementCount uniformAlignment tfRef = do let ToBuffer skipIt readIt writeIt alignMode = 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 skipIt err) 0)) (uniformAlignment, alignMode) elementF bIn = fst $ runReader (runStateT (runKleisli readIt err) 0) (name, elementSize, bIn) writer ptr x = void $ runStateT (runKleisli writeIt x) (ptr,pads) Buffer name elementSize elementCount elementF writer tfRef -- | This type family restricts what host and buffer types a texture format may be converted into. -- 'BufferColor t h' for a texture representation 't' and a host representation 'h' will evaluate to a buffer type used in the transfer. -- This family is closed, i.e. you cannot create additional instances to it. type family BufferColor c h where BufferColor Float Int32 = Normalized (B Int32) BufferColor Float Word32 = Normalized (B Word32) BufferColor Float Float = B Float BufferColor Int Int32 = B Int32 BufferColor Word Word32 = B Word32 BufferColor Word Word16 = BPacked Word16 BufferColor Word Word8 = BPacked Word8 BufferColor (V2 Float) (V2 Int32) = Normalized (B2 Int32) BufferColor (V2 Float) (V2 Int16) = Normalized (B2 Int16) BufferColor (V2 Float) (V2 Word32) = Normalized (B2 Word32) BufferColor (V2 Float) (V2 Word16) = Normalized (B2 Word16) BufferColor (V2 Float) (V2 Float) = B2 Float BufferColor (V2 Int) (V2 Int32) = B2 Int32 BufferColor (V2 Int) (V2 Int16) = B2 Int16 BufferColor (V2 Word) (V2 Word32) = B2 Word32 BufferColor (V2 Word) (V2 Word16) = B2 Word16 BufferColor (V3 Float) (V3 Int32) = Normalized (B3 Int32) BufferColor (V3 Float) (V3 Int16) = Normalized (B3 Int16) BufferColor (V3 Float) (V3 Int8) = Normalized (B3 Int8) BufferColor (V3 Float) (V3 Word32) = Normalized (B3 Word32) BufferColor (V3 Float) (V3 Word16) = Normalized (B3 Word16) BufferColor (V3 Float) (V3 Word8) = Normalized (B3 Word8) BufferColor (V3 Float) (V3 Float) = B3 Float BufferColor (V3 Int) (V3 Int32) = B3 Int32 BufferColor (V3 Int) (V3 Int16) = B3 Int16 BufferColor (V3 Int) (V3 Int8) = B3 Int8 BufferColor (V3 Word) (V3 Word32) = B3 Word32 BufferColor (V3 Word) (V3 Word16) = B3 Word16 BufferColor (V3 Word) (V3 Word8) = B3 Word8 BufferColor (V4 Float) (V4 Int32) = Normalized (B4 Int32) BufferColor (V4 Float) (V4 Int16) = Normalized (B4 Int16) BufferColor (V4 Float) (V4 Int8) = Normalized (B4 Int8) BufferColor (V4 Float) (V4 Word32) = Normalized (B4 Word32) BufferColor (V4 Float) (V4 Word16) = Normalized (B4 Word16) BufferColor (V4 Float) (V4 Word8) = Normalized (B4 Word8) BufferColor (V4 Float) (V4 Float) = B4 Float BufferColor (V4 Int) (V4 Int32) = B4 Int32 BufferColor (V4 Int) (V4 Int16) = B4 Int16 BufferColor (V4 Int) (V4 Int8) = B4 Int8 BufferColor (V4 Word) (V4 Word32) = B4 Word32 BufferColor (V4 Word) (V4 Word16) = B4 Word16 BufferColor (V4 Word) (V4 Word8) = B4 Word8 peekPixel1 :: Storable a => Ptr x -> IO a peekPixel1 = peek . castPtr peekPixel2 :: (Storable a) => Ptr x -> IO (V2 a) peekPixel2 ptr = do x <- peek (castPtr ptr) y <- peekElemOff (castPtr ptr ) 1 return (V2 x y) peekPixel3 :: (Storable a) => Ptr x -> IO (V3 a) peekPixel3 ptr = do x <- peek (castPtr ptr) y <- peekElemOff (castPtr ptr ) 1 z <- peekElemOff (castPtr ptr ) 2 return (V3 x y z) peekPixel4 :: (Storable a) => Ptr x -> IO (V4 a) peekPixel4 ptr = do V3 x y z <- peekPixel3 ptr w <- peekElemOff (castPtr ptr ) 3 return (V4 x y z w) instance BufferFormat (B Int32) where type HostFormat (B Int32) = Int32 toBuffer = toBufferB getGlType _ = GL_INT peekPixel = const peekPixel1 getGlPaddedFormat _ = GL_RED_INTEGER instance BufferFormat (B Word32) where type HostFormat (B Word32) = Word32 toBuffer = toBufferB getGlType _ = GL_UNSIGNED_INT peekPixel = const peekPixel1 getGlPaddedFormat _ = GL_RED_INTEGER instance BufferFormat (BPacked Word16) where type HostFormat (BPacked Word16) = Word16 toBuffer = let ToBuffer a b c _ = toBufferB :: ToBuffer Word16 (B Word16) in arr BPacked . ToBuffer a b c AlignPackedIndices getGlType _ = GL_UNSIGNED_SHORT peekPixel = const peekPixel1 getGlPaddedFormat _ = GL_RED_INTEGER instance BufferFormat (BPacked Word8) where type HostFormat (BPacked Word8) = Word8 toBuffer = let ToBuffer a b c _ = toBufferB :: ToBuffer Word8 (B Word8) in arr BPacked . ToBuffer a b c AlignPackedIndices getGlType _ = GL_UNSIGNED_BYTE peekPixel = const peekPixel1 getGlPaddedFormat _ = GL_RED_INTEGER instance BufferFormat (B Float) where type HostFormat (B Float) = Float toBuffer = toBufferB getGlType _ = GL_FLOAT peekPixel = const peekPixel1 getGlPaddedFormat _ = GL_RED instance BufferFormat (B2 Int32) where type HostFormat (B2 Int32) = V2 Int32 toBuffer = toBufferB2 getGlType _ = GL_INT peekPixel = const peekPixel2 getGlPaddedFormat _ = GL_RG_INTEGER instance BufferFormat (B2 Int16) where type HostFormat (B2 Int16) = V2 Int16 toBuffer = toBufferB2 getGlType _ = GL_SHORT peekPixel = const peekPixel2 getGlPaddedFormat _ = GL_RG_INTEGER instance BufferFormat (B2 Word32) where type HostFormat (B2 Word32) = V2 Word32 toBuffer = toBufferB2 getGlType _ = GL_UNSIGNED_INT peekPixel = const peekPixel2 getGlPaddedFormat _ = GL_RG_INTEGER instance BufferFormat (B2 Word16) where type HostFormat (B2 Word16) = V2 Word16 toBuffer = toBufferB2 getGlType _ = GL_UNSIGNED_SHORT peekPixel = const peekPixel2 getGlPaddedFormat _ = GL_RG_INTEGER instance BufferFormat (B2 Float) where type HostFormat (B2 Float) = V2 Float toBuffer = toBufferB2 getGlType _ = GL_FLOAT peekPixel = const peekPixel2 getGlPaddedFormat _ = GL_RG instance BufferFormat (B3 Int32) where type HostFormat (B3 Int32) = V3 Int32 toBuffer = toBufferB3 getGlType _ = GL_INT peekPixel = const peekPixel3 getGlPaddedFormat _ = GL_RGB_INTEGER instance BufferFormat (B3 Int16) where type HostFormat (B3 Int16) = V3 Int16 toBuffer = toBufferB3 getGlType _ = GL_SHORT peekPixel = const peekPixel3 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B3 Int8) where type HostFormat (B3 Int8) = V3 Int8 toBuffer = toBufferB3 getGlType _ = GL_BYTE peekPixel = const peekPixel3 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B3 Word32) where type HostFormat (B3 Word32) = V3 Word32 toBuffer = toBufferB3 getGlType _ = GL_UNSIGNED_INT peekPixel = const peekPixel3 getGlPaddedFormat _ = GL_RGB_INTEGER instance BufferFormat (B3 Word16) where type HostFormat (B3 Word16) = V3 Word16 toBuffer = toBufferB3 getGlType _ = GL_UNSIGNED_SHORT peekPixel = const peekPixel3 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B3 Word8) where type HostFormat (B3 Word8) = V3 Word8 toBuffer = toBufferB3 getGlType _ = GL_UNSIGNED_BYTE peekPixel = const peekPixel3 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B3 Float) where type HostFormat (B3 Float) = V3 Float toBuffer = toBufferB3 getGlType _ = GL_FLOAT peekPixel = const peekPixel3 getGlPaddedFormat _ = GL_RGB instance BufferFormat (B4 Int32) where type HostFormat (B4 Int32) = V4 Int32 toBuffer = toBufferB4 getGlType _ = GL_INT peekPixel = const peekPixel4 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B4 Int16) where type HostFormat (B4 Int16) = V4 Int16 toBuffer = toBufferB4 getGlType _ = GL_SHORT peekPixel = const peekPixel4 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B4 Int8) where type HostFormat (B4 Int8) = V4 Int8 toBuffer = toBufferB4 getGlType _ = GL_BYTE peekPixel = const peekPixel4 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B4 Word32) where type HostFormat (B4 Word32) = V4 Word32 toBuffer = toBufferB4 getGlType _ = GL_UNSIGNED_INT peekPixel = const peekPixel4 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B4 Word16) where type HostFormat (B4 Word16) = V4 Word16 toBuffer = toBufferB4 getGlType _ = GL_UNSIGNED_SHORT peekPixel = const peekPixel4 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B4 Word8) where type HostFormat (B4 Word8) = V4 Word8 toBuffer = toBufferB4 getGlType _ = GL_UNSIGNED_BYTE peekPixel = const peekPixel4 getGlPaddedFormat _ = GL_RGBA_INTEGER instance BufferFormat (B4 Float) where type HostFormat (B4 Float) = V4 Float toBuffer = toBufferB4 getGlType _ = GL_FLOAT peekPixel = const peekPixel4 getGlPaddedFormat _ = GL_RGBA