packages feed

gl-block-1.1: src/Graphics/Gl/Block.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

{-|
Copyright:  (c) 2023-206 IC Rainbow
            (c) 2014-2019 Edward Kmett
License : BSD-2-Clause OR Apache-2.0
Maintainer: IC Rainbow <aenor.realm@gmail.com>
Stability: experimental
Portability: non-portable

OpenGL std140 and std430 support
-}
module Graphics.Gl.Block
  ( Block (..)
  , GBlock (..)
  , Packed (..)
  , Std140 (..)
  , Std430 (..)
  , roundUp
  ) where

import Control.Monad.IO.Class (MonadIO (..))
import Data.Data (Data, Typeable)
import Data.Int (Int16, Int32, Int64)
import Data.Proxy (Proxy (..))
import Data.Word (Word16, Word32, Word64)
import Foreign.C.Types
import Foreign.Ptr (Ptr)
import Foreign.Ptr.Diff (Diff (..), peekDiffOff, pokeDiffOff)
import Foreign.Storable (Storable (..))
import GHC.Generics (C, D, Generic, K1 (..), M1 (..), Rep, S, U1 (..), from, to, (:*:) (..))
import GHC.TypeLits (KnownNat, Nat, natVal, type (+))
import Numeric.Half (Half)

newtype Packed a = Packed {getPacked :: a}
  deriving (Data, Typeable, Generic, Functor, Foldable, Traversable, Eq, Ord, Show, Read)

instance (Block a) => Storable (Packed a) where
  alignment _ = 1
  sizeOf _ = sizeOfPacked (Proxy :: Proxy a)
  peekByteOff p o = Packed <$> readPacked p (Diff o)
  pokeByteOff p o = writePacked p (Diff o) . getPacked

newtype Std140 a = Std140 {getStd140 :: a}
  deriving (Data, Typeable, Generic, Functor, Foldable, Traversable, Eq, Ord, Show, Read)

instance (Block a) => Storable (Std140 a) where
  alignment _ = alignment140 (Proxy :: Proxy a)
  sizeOf _ = sizeOf140 (Proxy :: Proxy a)
  peekByteOff p o = Std140 <$> read140 p (Diff o)
  pokeByteOff p o = write140 p (Diff o) . getStd140

newtype Std430 a = Std430 {getStd430 :: a}
  deriving (Data, Typeable, Generic, Functor, Foldable, Traversable, Eq, Ord, Show, Read)

instance (Block a) => Storable (Std430 a) where
  alignment _ = alignment430 (Proxy :: Proxy a)
  sizeOf _ = sizeOf430 (Proxy :: Proxy a)
  peekByteOff p o = Std430 <$> read430 p (Diff o)
  pokeByteOff p o = write430 p (Diff o) . getStd430

{- | This describes how to load and store primitives
through a uniform/shader storage blocks according to
OpenGL Std140 and Std430.

There are lots of fiddly little constants around, beware.
-}
class Block b where
  -- | As per 'Storable' 'alignment', but matching OpenGL Std140.
  alignment140 :: proxy b -> Int
  default alignment140 :: (GBlock (Rep b)) => proxy b -> Int
  alignment140 _ = galignment140 (Proxy :: Proxy (Rep b))

  -- | As per 'Storable' 'sizeOf', but matching OpenGL Std140.
  sizeOf140 :: proxy b -> Int
  default sizeOf140 :: (GBlock (Rep b)) => proxy b -> Int
  sizeOf140 _ = gsizeOf140 (Proxy :: Proxy (Rep b))

  -- | Structures get smashed up to a minimum of a vec4 alignment in 140 mode
  isStruct :: proxy b -> Bool
  isStruct _ = True

  read140 :: (MonadIO m) => Ptr a -> Diff a b -> m b
  default read140 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> m b
  read140 p (Diff o) = liftIO $ to <$> gread140 p o

  write140 :: (MonadIO m) => Ptr a -> Diff a b -> b -> m ()
  default write140 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> b -> m ()
  write140 p (Diff o) b = liftIO $ gwrite140 p o (from b)

  -- | As per 'Storable' 'alignment', but matching OpenGL Std430.
  alignment430 :: proxy b -> Int
  default alignment430 :: (GBlock (Rep b)) => proxy b -> Int
  alignment430 _ = galignment430 (Proxy :: Proxy (Rep b))

  -- | As per 'Storable' 'sizeOf', but matching OpenGL Std430.
  sizeOf430 :: proxy b -> Int
  default sizeOf430 :: (GBlock (Rep b)) => proxy b -> Int
  sizeOf430 _ = gsizeOf430 (Proxy :: Proxy (Rep b))

  read430 :: (MonadIO m) => Ptr a -> Diff a b -> m b
  default read430 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> m b
  read430 p (Diff o) = liftIO $ to <$> gread430 p o

  write430 :: (MonadIO m) => Ptr a -> Diff a b -> b -> m ()
  default write430 :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> b -> m ()
  write430 p (Diff o) b = liftIO $ gwrite430 p o (from b)

  type PackedSize b :: Nat
  type PackedSize b = GPackedSize (Rep b)

  -- | As per 'Storable' 'sizeOf', but without padding and no alignment
  sizeOfPacked :: proxy b -> Int
  default sizeOfPacked :: (KnownNat (PackedSize b)) => proxy b -> Int
  sizeOfPacked _ = fromInteger $! natVal (Proxy :: Proxy (PackedSize b))

  readPacked :: (MonadIO m) => Ptr a -> Diff a b -> m b
  default readPacked :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> m b
  readPacked p (Diff o) = liftIO $ to <$> greadPacked p o

  writePacked :: (MonadIO m) => Ptr a -> Diff a b -> b -> m ()
  default writePacked :: (MonadIO m, Generic b, GBlock (Rep b)) => Ptr a -> Diff a b -> b -> m ()
  writePacked p (Diff o) b = liftIO $ gwritePacked p o (from b)

-- | Automatically derive Std140 and Std430 alignment using GHC Generics
class GBlock f where
  type GPackedSize f :: Nat
  galignment140 :: p f -> Int
  galignment430 :: p f -> Int
  gsizeOf140 :: p f -> Int
  gsizeOf430 :: p f -> Int
  gsizeOfPacked :: p f -> Int

  {- | The base offset of the member that would follow this subtree, given the
  subtree itself begins at base offset @o@. Threads the offset through the
  product left-to-right: each scalar leaf rounds @o@ up to its /own/ base
  alignment and adds its size.

  This is the member-offset rule from the OpenGL spec, §7.6.2.2 (Standard
  Uniform Block Layout, std140; std430 relaxes the same rule):

  > A structure and each structure member have a base offset and a base
  > alignment [...]. The base offset of the first member of a structure is
  > taken from the aligned offset of the structure itself. The base offset of
  > all other structure members is derived by taking the offset of the last
  > basic machine unit consumed by the previous member and adding one. Each
  > structure member is stored in memory at its aligned offset [the base
  > offset rounded up to a multiple of its base alignment].

  Rounding to each member's /own/ alignment -- rather than to a whole
  subtree's maximum alignment -- is what stops a low-alignment field from
  being over-padded when a later sibling in the same product has a higher
  alignment.
  -}
  gnextOffset140 :: p f -> Int -> Int

  gnextOffset430 :: p f -> Int -> Int

  gread140 :: Ptr a -> Int -> IO (f b)
  gread430 :: Ptr a -> Int -> IO (f b)
  greadPacked :: Ptr a -> Int -> IO (f b)
  gwrite140 :: Ptr a -> Int -> f b -> IO ()
  gwrite430 :: Ptr a -> Int -> f b -> IO ()
  gwritePacked :: Ptr a -> Int -> f b -> IO ()

instance GBlock U1 where
  type GPackedSize U1 = 0
  galignment140 _ = 1
  gsizeOf140 _ = 0
  galignment430 _ = 1
  gsizeOf430 _ = 0
  gsizeOfPacked _ = 0
  gnextOffset140 _ o = o
  gnextOffset430 _ o = o
  gread140 _ _ = return U1
  gread430 _ _ = return U1
  greadPacked _ _ = return U1
  gwrite140 _ _ U1 = return ()
  gwrite430 _ _ U1 = return ()
  gwritePacked _ _ U1 = return ()

instance (GBlock f, GBlock g) => GBlock (f :*: g) where
  type GPackedSize (f :*: g) = GPackedSize f + GPackedSize g

  gsizeOfPacked _ =
    gsizeOfPacked (Proxy :: Proxy f) + gsizeOfPacked (Proxy :: Proxy g)

  galignment140 _ =
    max
      (galignment140 (Proxy :: Proxy f))
      (galignment140 (Proxy :: Proxy g))

  galignment430 _ =
    max
      (galignment430 (Proxy :: Proxy f))
      (galignment430 (Proxy :: Proxy g))

  -- The content extent is the offset reached after laying out both halves from
  -- offset 0 (each leaf rounds itself to its own alignment along the way).
  gsizeOf140 _ = gnextOffset140 (Proxy :: Proxy (f :*: g)) 0
  gsizeOf430 _ = gnextOffset430 (Proxy :: Proxy (f :*: g)) 0

  gnextOffset140 _ o = gnextOffset140 (Proxy :: Proxy g) (gnextOffset140 (Proxy :: Proxy f) o)
  gnextOffset430 _ o = gnextOffset430 (Proxy :: Proxy g) (gnextOffset430 (Proxy :: Proxy f) o)

  gread140 p o =
    (:*:)
      <$> gread140 p o
      <*> gread140 p (gnextOffset140 (Proxy :: Proxy f) o)

  gread430 p o =
    (:*:)
      <$> gread430 p o
      <*> gread430 p (gnextOffset430 (Proxy :: Proxy f) o)

  greadPacked p o =
    (:*:)
      <$> greadPacked p o
      <*> greadPacked p (o + gsizeOfPacked (Proxy :: Proxy f))

  gwrite140 p o (a :*: b) = do
    gwrite140 p o a
    gwrite140 p (gnextOffset140 (Proxy :: Proxy f) o) b

  gwrite430 p o (a :*: b) = do
    gwrite430 p o a
    gwrite430 p (gnextOffset430 (Proxy :: Proxy f) o) b

  gwritePacked p o (a :*: b) = do
    gwritePacked p o a
    gwritePacked p (o + gsizeOfPacked (Proxy :: Proxy f)) b

instance (GBlock f) => GBlock (M1 S c f) where
  type GPackedSize (M1 S c f) = GPackedSize f
  galignment140 _ = galignment140 (Proxy :: Proxy f)
  galignment430 _ = galignment430 (Proxy :: Proxy f)
  gsizeOf140 _ = gsizeOf140 (Proxy :: Proxy f)
  gsizeOf430 _ = gsizeOf430 (Proxy :: Proxy f)
  gsizeOfPacked _ = gsizeOfPacked (Proxy :: Proxy f)
  gnextOffset140 _ = gnextOffset140 (Proxy :: Proxy f)
  gnextOffset430 _ = gnextOffset430 (Proxy :: Proxy f)
  gread140 p o = M1 <$> gread140 p o
  gread430 p o = M1 <$> gread430 p o
  greadPacked p o = M1 <$> greadPacked p o
  gwrite140 p o (M1 a) = gwrite140 p o a
  gwrite430 p o (M1 a) = gwrite430 p o a
  gwritePacked p o (M1 a) = gwritePacked p o a

instance (GBlock f) => GBlock (M1 C c f) where
  type GPackedSize (M1 C c f) = GPackedSize f
  galignment140 _ = roundUp (galignment140 (Proxy :: Proxy f)) 16 -- std140 rule 9: round the struct's alignment up to a vec4
  galignment430 _ = galignment430 (Proxy :: Proxy f) -- std140 rule 9, relaxed by std430
  gsizeOf140 _ = roundUp (gsizeOf140 (Proxy :: Proxy f)) align -- std140 rule 9: pad to the struct's own (vec4-rounded) alignment
    where
      align = roundUp (galignment140 (Proxy :: Proxy f)) 16
  gsizeOf430 _ = roundUp (gsizeOf430 (Proxy :: Proxy f)) (galignment430 (Proxy :: Proxy f)) -- std140 rule 9, relaxed by std430
  gsizeOfPacked _ = gsizeOfPacked (Proxy :: Proxy f)
  gnextOffset140 _ = gnextOffset140 (Proxy :: Proxy f) -- a constructor is only ever the top of a Rep, never a product child
  gnextOffset430 _ = gnextOffset430 (Proxy :: Proxy f)
  gread140 p o = M1 <$> gread140 p o
  gread430 p o = M1 <$> gread430 p o
  greadPacked p o = M1 <$> greadPacked p o
  gwrite140 p o (M1 a) = gwrite140 p o a
  gwrite430 p o (M1 a) = gwrite430 p o a
  gwritePacked p o (M1 a) = gwritePacked p o a

instance (GBlock f) => GBlock (M1 D c f) where
  type GPackedSize (M1 D c f) = GPackedSize f
  galignment140 _ = galignment140 (Proxy :: Proxy f)
  galignment430 _ = galignment430 (Proxy :: Proxy f)
  gsizeOf140 _ = gsizeOf140 (Proxy :: Proxy f)
  gsizeOf430 _ = gsizeOf430 (Proxy :: Proxy f)
  gsizeOfPacked _ = gsizeOfPacked (Proxy :: Proxy f)
  gnextOffset140 _ = gnextOffset140 (Proxy :: Proxy f)
  gnextOffset430 _ = gnextOffset430 (Proxy :: Proxy f)
  gread140 p o = M1 <$> gread140 p o
  gread430 p o = M1 <$> gread430 p o
  greadPacked p o = M1 <$> greadPacked p o
  gwrite140 p o (M1 a) = gwrite140 p o a
  gwrite430 p o (M1 a) = gwrite430 p o a
  gwritePacked p o (M1 a) = gwritePacked p o a

instance (Block c) => GBlock (K1 i c) where
  type GPackedSize (K1 i c) = PackedSize c
  galignment140 _ = alignment140 (Proxy :: Proxy c)
  galignment430 _ = alignment430 (Proxy :: Proxy c)
  gsizeOf140 _ = sizeOf140 (Proxy :: Proxy c)
  gsizeOf430 _ = sizeOf430 (Proxy :: Proxy c)
  gsizeOfPacked _ = sizeOfPacked (Proxy :: Proxy c)
  gnextOffset140 _ o = roundUp o (alignment140 (Proxy :: Proxy c)) + sizeOf140 (Proxy :: Proxy c)
  gnextOffset430 _ o = roundUp o (alignment430 (Proxy :: Proxy c)) + sizeOf430 (Proxy :: Proxy c)
  gread140 p o = K1 <$> read140 p (Diff (roundUp o (alignment140 (Proxy :: Proxy c))))
  gread430 p o = K1 <$> read430 p (Diff (roundUp o (alignment430 (Proxy :: Proxy c))))
  greadPacked p o = K1 <$> readPacked p (Diff o)
  gwrite140 p o (K1 a) = write140 p (Diff (roundUp o (alignment140 (Proxy :: Proxy c)))) a
  gwrite430 p o (K1 a) = write430 p (Diff (roundUp o (alignment430 (Proxy :: Proxy c)))) a
  gwritePacked p o (K1 a) = writePacked p (Diff o) a

toBool :: Int32 -> Bool
toBool 0 = False
toBool _ = True

fromBool :: Bool -> Int32
fromBool False = 0
fromBool True = 1

instance Block Bool where
  type PackedSize Bool = 4
  alignment140 _ = 4
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 p (Diff d) = fmap toBool $ peekDiffOff p (Diff d)
  write140 p (Diff d) = pokeDiffOff p (Diff d) . fromBool
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Int16 where
  type PackedSize Int16 = 2
  alignment140 _ = 2
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block CShort where
  type PackedSize CShort = 2
  alignment140 _ = 2
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Word16 where
  type PackedSize Word16 = 2
  alignment140 _ = 2
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block CUShort where
  type PackedSize CUShort = 2
  alignment140 _ = 2
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Int32 where
  type PackedSize Int32 = 4
  alignment140 _ = 4
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block CInt where
  type PackedSize CInt = 4
  alignment140 _ = 4
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Word32 where
  type PackedSize Word32 = 4
  alignment140 _ = 4
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block CUInt where
  type PackedSize CUInt = 4
  alignment140 _ = 4
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Float where
  type PackedSize Float = 4
  alignment140 _ = 4
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block CFloat where
  type PackedSize CFloat = 4
  alignment140 _ = 4
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

-- | Half-precision float (GLSL @float16_t@): a 2-byte scalar, base alignment 2.
instance Block Half where
  type PackedSize Half = 2
  alignment140 _ = 2
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Int64 where
  type PackedSize Int64 = 8
  alignment140 _ = 8
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Word64 where
  type PackedSize Word64 = 8
  alignment140 _ = 8
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block Double where
  type PackedSize Double = 8
  alignment140 _ = 8
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

instance Block CDouble where
  type PackedSize CDouble = 8
  alignment140 _ = 8
  sizeOf140 = sizeOfPacked
  alignment430 = alignment140
  sizeOf430 = sizeOf140
  isStruct _ = False
  read140 = peekDiffOff
  write140 = pokeDiffOff
  read430 = read140
  write430 = write140
  readPacked = read140
  writePacked = write140
  {-# INLINE alignment140 #-}
  {-# INLINE sizeOf140 #-}
  {-# INLINE alignment430 #-}
  {-# INLINE sizeOf430 #-}
  {-# INLINE isStruct #-}
  {-# INLINE read140 #-}
  {-# INLINE write140 #-}
  {-# INLINE read430 #-}
  {-# INLINE write430 #-}
  {-# INLINE readPacked #-}
  {-# INLINE writePacked #-}

{- | Reference layout for a fixed-size array member, per std140 rule 4

Both share one formula:
the array's base alignment is the element's rounded up to a vec4, and the
per-element stride is the element size padded to that alignment. std430 (the
rule 4\/9 relaxation) drops the vec4 rounding and keeps the element's own
alignment. A real instance needs a sized-vector element type (e.g. @linear@'s
@V n@), which this library does not depend on; see @Spec.Array@ (@Array n a@
over @Data.Vector.Storable@) in the test suite for a working implementation.
-}
{-
instance (Dim n, Block a) => Block (V n a) where
  isStruct _ = True -- an array member is always at least vec4-aligned in std140
  -- rule 4: round the element alignment up to a vec4 for scalar and vector
  -- elements too, not only structs (that omission was the original bug here).
  alignment140 _ = roundUp (alignment140 (Proxy :: Proxy a)) 16
  alignment430 _ = alignment430 (Proxy :: Proxy a)
  -- size = stride * length; the stride pads the element to the array alignment,
  -- so the whole array is an exact number of strides (no extra tail padding).
  sizeOf140 _ = roundUp (sizeOf140 (Proxy :: Proxy a)) (roundUp (alignment140 (Proxy :: Proxy a)) 16) * reflectDim (Proxy :: Proxy n)
  sizeOf430 _ = roundUp (sizeOf430 (Proxy :: Proxy a)) (alignment430 (Proxy :: Proxy a)) * reflectDim (Proxy :: Proxy n)
  read140 p (Diff o) = liftIO $ sequence $ tabulate \i -> read140 p $ Diff (o + i*d) where
    d = roundUp (sizeOf140 (Proxy :: Proxy a)) (roundUp (alignment140 (Proxy :: Proxy a)) 16)
  write140 p (Diff o) v = liftIO $ iforM_ v \i -> write140 p (Diff (o + i*d)) where
    d = roundUp (sizeOf140 (Proxy :: Proxy a)) (roundUp (alignment140 (Proxy :: Proxy a)) 16)
  read430 p (Diff o) = liftIO $ sequence $ tabulate \i -> read430 p $ Diff (o + i*d) where
    d = roundUp (sizeOf430 (Proxy :: Proxy a)) (alignment430 (Proxy :: Proxy a))
  write430 p (Diff o) v = liftIO $ iforM_ v \i -> write430 p (Diff (o + i*d)) where
    d = roundUp (sizeOf430 (Proxy :: Proxy a)) (alignment430 (Proxy :: Proxy a))
-}

-- | @roundUp k n@ rounds up k up to an integral multiple of n
roundUp :: Int -> Int -> Int
roundUp k n = k + mod (n - k) n
{-# INLINEABLE roundUp #-}

instance (Block a, Block b, KnownNat (PackedSize a + PackedSize b)) => Block (a, b)
instance (Block a, Block b, Block c, KnownNat (PackedSize a + (PackedSize b + PackedSize c))) => Block (a, b, c)