module Graphics.Vulkan.Marshal
( FlagType (..), FlagMask, FlagBit
, VulkanMarshal (..), VulkanMarshalPrim ()
, VulkanPtr (..)
, VkPtr (..)
, pattern VK_NULL_HANDLE, pattern VK_NULL
, clearStorable, withPtr
, HasField (..), CanReadField (..), CanWriteField (..)
, CanReadFieldArray (..), CanWriteFieldArray (..)
, IsFieldArray, IndexInBounds
, mallocForeignPtr, withForeignPtr, addForeignPtrFinalizer
, Int8, Int16, Int32, Int64
, Word8, Word16, Word32, Word64
, Ptr, FunPtr, Void, CString
, CInt (..), CSize (..), CChar (..), CWchar (..), CULong (..)
, withCStringField, unsafeCStringField
, getStringField, readStringField, writeStringField
, cmpCStrings, cmpCStringsN
) where
import Data.Data (Data)
import Data.Int (Int16, Int32, Int64, Int8)
import Data.Kind (Constraint, Type)
import Data.Void (Void)
import Data.Word (Word16, Word32, Word64,
Word8)
import Foreign.C.String (CString, peekCString)
import Foreign.C.Types (CChar (..), CWchar (..), CInt (..), CSize (..), CULong (..))
import Foreign.ForeignPtr (ForeignPtr,
addForeignPtrFinalizer,
mallocForeignPtr,
withForeignPtr)
import Foreign.Marshal.Array (pokeArray0)
import Foreign.Marshal.Utils (fillBytes)
import Foreign.Ptr (FunPtr, nullPtr, plusPtr)
import Foreign.Storable (Storable (sizeOf))
import GHC.Generics (Generic)
import GHC.Ptr (Ptr (..))
import GHC.TypeLits
import System.IO.Unsafe (unsafeDupablePerformIO)
import Graphics.Vulkan.Marshal.Internal
data FlagType = FlagMask | FlagBit
type FlagMask = 'FlagMask
type FlagBit = 'FlagBit
class VulkanMarshal a where
type StructFields a :: [Symbol]
type CUnionType a :: Bool
type ReturnedOnly a :: Bool
type StructExtends a :: [Type]
newVkData :: (Ptr a -> IO ()) -> IO a
default newVkData :: (Storable a, VulkanMarshalPrim a)
=> (Ptr a -> IO ()) -> IO a
newVkData = newVkData#
mallocVkData :: IO a
default mallocVkData :: (Storable a, VulkanMarshalPrim a) => IO a
mallocVkData = mallocVkData#
mallocVkDataArray :: Int -> IO (Ptr a, [a])
default mallocVkDataArray :: (Storable a, VulkanMarshalPrim a)
=> Int -> IO (Ptr a, [a])
mallocVkDataArray = mallocVkDataArray#
unsafePtr :: a -> Ptr a
default unsafePtr :: VulkanMarshalPrim a => a -> Ptr a
unsafePtr a = Ptr (unsafeAddr a)
fromForeignPtr :: ForeignPtr a -> IO a
default fromForeignPtr :: (Storable a, VulkanMarshalPrim a)
=> ForeignPtr a -> IO a
fromForeignPtr = fromForeignPtr#
toForeignPtr :: a -> IO (ForeignPtr a)
default toForeignPtr :: VulkanMarshalPrim a => a -> IO (ForeignPtr a)
toForeignPtr = toForeignPtr#
toPlainForeignPtr :: a -> IO (ForeignPtr a)
default toPlainForeignPtr :: VulkanMarshalPrim a => a -> IO (ForeignPtr a)
toPlainForeignPtr = toPlainForeignPtr#
touchVkData :: a -> IO ()
default touchVkData :: VulkanMarshalPrim a => a -> IO ()
touchVkData = touchVkData#
withPtr :: VulkanMarshal a => a -> (Ptr a -> IO b) -> IO b
withPtr x k = do
b <- k (unsafePtr x)
touchVkData x
return b
clearStorable :: Storable a => Ptr a -> IO ()
clearStorable p = fillBytes p 0 (sizeOf $ unptr p)
where
unptr :: Ptr b -> b
unptr ~_ = undefined
newtype VkPtr a = VkPtr Word64
deriving (Eq, Ord, Show, Storable, Generic, Data)
type role VkPtr phantom
class VulkanPtr ptr where
vkNullPtr :: ptr a
instance VulkanPtr Ptr where
vkNullPtr = nullPtr
instance VulkanPtr VkPtr where
vkNullPtr = VkPtr 0
isNullPtr :: (Eq (ptr a), VulkanPtr ptr) => ptr a -> Bool
isNullPtr = (vkNullPtr ==)
pattern VK_NULL :: (Eq (ptr a), VulkanPtr ptr) => ptr a
pattern VK_NULL <- (isNullPtr -> True)
where VK_NULL = vkNullPtr
pattern VK_NULL_HANDLE :: (Eq (ptr a), VulkanPtr ptr) => ptr a
pattern VK_NULL_HANDLE = VK_NULL
class HasField (fname :: Symbol) (a :: Type) where
type FieldType fname a :: Type
type FieldOptional fname a :: Bool
type FieldOffset fname a :: Nat
type FieldIsArray fname a :: Bool
fieldOptional :: Bool
fieldOffset :: Int
class ( HasField fname a
, IsFieldArray fname a 'False
) => CanReadField (fname :: Symbol) (a :: Type) where
getField :: a -> FieldType fname a
readField :: Ptr a -> IO (FieldType fname a)
class CanReadField fname a => CanWriteField (fname :: Symbol) (a :: Type) where
writeField :: Ptr a -> FieldType fname a -> IO ()
class ( HasField fname a
, IndexInBounds fname idx a
, IsFieldArray fname a 'True
) => CanReadFieldArray (fname :: Symbol) (idx :: Nat) (a :: Type) where
type FieldArrayLength fname a :: Nat
fieldArrayLength :: Int
getFieldArray :: a -> FieldType fname a
readFieldArray :: Ptr a -> IO (FieldType fname a)
class CanReadFieldArray fname idx a
=> CanWriteFieldArray (fname :: Symbol) (idx :: Nat) (a :: Type) where
writeFieldArray :: Ptr a -> FieldType fname a -> IO ()
instance
TypeError (ErrorNoSuchField fname a) => HasField fname a where
instance
( HasField fname a
, IsFieldArray fname a 'False
, TypeError (ErrorNotReadableField fname a)
) => CanReadField fname a where
instance
( CanReadField fname a
, TypeError (ErrorNotWritableField fname a)
) => CanWriteField fname a where
instance
( HasField fname a
, IsFieldArray fname a 'True
, IndexInBounds fname idx a
, TypeError (ErrorNotReadableField fname a)
) => CanReadFieldArray fname idx a where
instance
( CanReadFieldArray fname idx a
, TypeError (ErrorNotWritableField fname a)
) => CanWriteFieldArray fname idx a where
type IndexInBounds (s :: Symbol) (i :: Nat) (a :: Type)
= IndexInBounds' s i a (CmpNat i (FieldArrayLength s a))
type family IndexInBounds' (s :: Symbol)
(i :: Nat)
(a :: Type) (r :: Ordering) :: Constraint where
IndexInBounds' _ _ _ 'LT = ()
IndexInBounds' s i a _ = TypeError ( ErrorIndexOutOfBounds s i a )
type IsFieldArray s a e = IsFieldArray' s a (FieldIsArray s a) e
type family IsFieldArray' (s :: Symbol)
(a :: Type)
(actual :: Bool)
(expected :: Bool) :: Constraint where
IsFieldArray' _ _ 'True 'True = ()
IsFieldArray' _ _ 'False 'False = ()
IsFieldArray' s a 'True 'False = TypeError (ErrorIsArrayField s a)
IsFieldArray' s a 'False 'True = TypeError (ErrorIsNotArrayField s a)
type ErrorNoSuchField (s :: Symbol) (a :: Type)
= 'Text "Structure " ':<>: 'ShowType a
':<>: 'Text " does not have field " ':<>: 'ShowType s ':<>: 'Text "."
':$$: 'Text "Note, this structure has following fields: "
':<>: 'ShowType (StructFields a)
type ErrorIsNotArrayField (s :: Symbol) (a :: Type)
= 'Text "Field " ':<>: 'ShowType s ':<>:
'Text " of structure " ':<>: 'ShowType a ':<>:
'Text " is not an array field."
':$$: 'Text "Don't use ***FieldArray functions on it."
type ErrorIsArrayField (s :: Symbol) (a :: Type)
= 'Text "Field " ':<>: 'ShowType s ':<>:
'Text " of structure " ':<>: 'ShowType a ':<>:
'Text " is an array field."
':$$: 'Text "Use ***FieldArray functions on it."
type ErrorIndexOutOfBounds (s :: Symbol) (i :: Nat) (a :: Type)
= 'Text "Array index " ':<>: 'ShowType i ':<>:
'Text " is out of bounds for '" ':<>:
'Text s ':<>: 'Text "', member of type " ':<>: 'ShowType a ':<>: 'Text "."
':$$:
'Text "Note: the array size is "
':<>: 'ShowType (FieldArrayLength s a) ':<>: 'Text "."
type ErrorNotReadableField (s :: Symbol) (a :: Type)
= 'Text "Field " ':<>: 'ShowType s ':<>:
'Text " of structure " ':<>: 'ShowType a ':<>:
'Text " is not readable."
type ErrorNotWritableField (s :: Symbol) (a :: Type)
= 'Text "Field " ':<>: 'ShowType s ':<>:
'Text " of structure " ':<>: 'ShowType a ':<>:
'Text " is not writable."
withCStringField :: forall fname a b
. ( CanReadFieldArray fname 0 a
, FieldType fname a ~ CChar
, VulkanMarshal a
)
=> a -> (CString -> IO b) -> IO b
withCStringField x f = do
r <- f (unsafeCStringField @fname @a x)
touchVkData x
pure r
unsafeCStringField :: forall fname a
. ( CanReadFieldArray fname 0 a
, FieldType fname a ~ CChar
, VulkanMarshal a
)
=> a -> CString
unsafeCStringField x = unsafePtr x `plusPtr` fieldOffset @fname @a
getStringField :: forall fname a
. ( CanReadFieldArray fname 0 a
, FieldType fname a ~ CChar
, VulkanMarshal a
)
=> a -> String
getStringField x
= case takeForce (fieldArrayLength @fname @0 @a)
. unsafeDupablePerformIO
$ withCStringField @fname @a x peekCString of
((), s) -> s
readStringField :: forall fname a
. ( CanReadFieldArray fname 0 a
, FieldType fname a ~ CChar
, VulkanMarshal a
)
=> Ptr a -> IO String
readStringField px = do
((), s) <- takeForce (fieldArrayLength @fname @0 @a)
<$> peekCString (px `plusPtr` fieldOffset @fname @a)
return s
writeStringField :: forall fname a
. ( CanWriteFieldArray fname 0 a
, FieldType fname a ~ CChar
, VulkanMarshal a
)
=> Ptr a -> String -> IO ()
writeStringField px =
pokeArray0 '\0' (px `plusPtr` fieldOffset @fname @a)
takeForce :: Int -> String -> ((), String)
takeForce 0 _ = ((), [])
takeForce _ [] = ((), [])
takeForce n (x:xs) = seq x $ (x:) <$> takeForce (n1) xs
cmpCStrings :: CString -> CString -> Ordering
cmpCStrings a b
| a == b = EQ
| otherwise = c_strcmp a b `compare` 0
cmpCStringsN :: CString -> CString -> Int -> Ordering
cmpCStringsN a b n
| a == b = EQ
| otherwise = c_strncmp a b (fromIntegral n) `compare` 0
foreign import ccall unsafe "strncmp"
c_strncmp :: CString -> CString -> CSize -> CInt
foreign import ccall unsafe "strcmp"
c_strcmp :: CString -> CString -> CInt