{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE Strict #-} {-# LANGUAGE UnboxedTuples #-} {-# LANGUAGE UnliftedFFITypes #-} -- | This module is not part of auto-generated code based on vk.xml. -- Instead, it is hand-written to provide common types and classes. -- -- DANGER! -- This is an internal module; it can change a lot between package versions; -- it provides low-level functions, most of which have user-friendly analogues. module Graphics.Vulkan.Marshal.Internal ( VulkanMarshalPrim (..) , fromForeignPtr# , toForeignPtr#, toPlainForeignPtr# , touchVkData# , cmpBytes# , newVkData# , mallocVkData#, mallocVkDataArray# , peekVkData#, pokeVkData# ) where import Foreign.C.Types (CInt (..), CSize (..)) import Foreign.ForeignPtr (ForeignPtr, newForeignPtr_) import Foreign.Storable (Storable (..)) import GHC.Base (Addr#, ByteArray#, IO (..), Int (..), Int#, byteArrayContents#, copyAddrToByteArray#, eqAddr#, isTrue#, minusAddr#, newAlignedPinnedByteArray#, touch#, unsafeCoerce#, unsafeFreezeByteArray#, (*#), (+#), (>=#)) import GHC.Ptr (Ptr (..)) import GHC.ForeignPtr (ForeignPtr (..), ForeignPtrContents (..)) -- | This class gives low-level access to memory location occupied by Vulkan data. -- -- Meant for internal use only. class VulkanMarshalPrim a where -- | Get address of vulkan structure. -- Note, the address is only valid as long as a given vulkan structure exists. -- Structures created with newVkData are stored in pinned byte arrays, -- so their memory is maintained by Haskell GC. unsafeAddr :: a -> Addr# -- | Get a @ByteArray#@ that keeps the data. -- -- Note, the data structure does not necessarily starts at zero offset. unsafeByteArray :: a -> ByteArray# -- | Combine a vulkan structure from ByteArray and an offset in this array. unsafeFromByteArrayOffset :: Int# -> ByteArray# -> a -- | Create a `ByteArray#`-based type from `ForeignPtr`. -- Try to not copy data, but do it if necessary. fromForeignPtr# :: forall a . (Storable a, VulkanMarshalPrim a) => ForeignPtr a -> IO a fromForeignPtr# (ForeignPtr addr PlainForeignPtr{}) | I# n <- sizeOf (undefined :: a) , I# a <- alignment (undefined :: a) = IO (\s0 -> case newAlignedPinnedByteArray# n a s0 of (# s1, mba #) -> case copyAddrToByteArray# addr mba 0# n s1 of s2 -> case unsafeFreezeByteArray# mba s2 of (# s3, ba #) -> (# s3, unsafeFromByteArrayOffset 0# ba #) ) fromForeignPtr# (ForeignPtr addr (MallocPtr mba _)) = IO (\s0 -> case unsafeFreezeByteArray# mba s0 of (# s1, ba #) -> (# s1, unsafeFromByteArrayOffset (minusAddr# addr (byteArrayContents# ba)) ba #) ) fromForeignPtr# (ForeignPtr addr (PlainPtr mba)) = IO (\s0 -> case unsafeFreezeByteArray# mba s0 of (# s1, ba #) -> (# s1, unsafeFromByteArrayOffset (minusAddr# addr (byteArrayContents# ba)) ba #) ) {-# INLINE fromForeignPtr# #-} -- | Create a `ForeignPtr` referencing the structure without copying data. toForeignPtr# :: VulkanMarshalPrim a => a -> IO (ForeignPtr a) toForeignPtr# x | a <- unsafeAddr x , b <- unsafeByteArray x = do ForeignPtr _ (PlainForeignPtr r) <- newForeignPtr_ (Ptr a) IO (\s -> (# s, ForeignPtr a (MallocPtr (unsafeCoerce# b) r) #)) {-# INLINE toForeignPtr# #-} -- | Create a `ForeignPtr` referencing the structure without copying data. -- This version of a pointer carries no finalizers. -- -- It is not possible to add a finalizer to a ForeignPtr created with -- @toPlainForeignPtr@. -- Attempts to add a finalizer to a ForeignPtr created this way, or to -- finalize such a pointer, will throw an exception. toPlainForeignPtr# :: VulkanMarshalPrim a => a -> IO (ForeignPtr a) toPlainForeignPtr# x | a <- unsafeAddr x , b <- unsafeByteArray x = IO (\s -> (# s, ForeignPtr a (PlainPtr (unsafeCoerce# b)) #)) {-# INLINE toPlainForeignPtr# #-} -- | Make sure the region of memory is not collected at this moment in time. touchVkData# :: VulkanMarshalPrim a => a -> IO () touchVkData# a = IO (\s -> (# touch# (unsafeByteArray a) s, () #)) {-# INLINE touchVkData# #-} newVkData# :: forall a . (Storable a, VulkanMarshalPrim a) => (Ptr a -> IO ()) -> IO a newVkData# f | I# n <- sizeOf (undefined :: a) , I# a <- alignment (undefined :: a) = IO (\s0 -> case newAlignedPinnedByteArray# n a s0 of (# s1, mba #) -> case unsafeFreezeByteArray# mba s1 of (# s2, ba #) -> case f (Ptr (byteArrayContents# ba)) of IO k -> case k s2 of (# s3, () #) -> (# s3, unsafeFromByteArrayOffset 0# ba #) ) {-# INLINE newVkData# #-} mallocVkData# :: forall a . (Storable a, VulkanMarshalPrim a) => IO a mallocVkData# | I# n <- sizeOf (undefined :: a) , I# a <- alignment (undefined :: a) = IO (\s0 -> case newAlignedPinnedByteArray# n a s0 of (# s1, mba #) -> case unsafeFreezeByteArray# mba s1 of (# s2, ba #) -> (# s2, unsafeFromByteArrayOffset 0# ba #) ) {-# INLINE mallocVkData# #-} mallocVkDataArray# :: forall a . (Storable a, VulkanMarshalPrim a) => Int -> IO (Ptr a, [a]) mallocVkDataArray# (I# m) | I# n <- sizeOf (undefined :: a) , I# a <- alignment (undefined :: a) , nm <- n *# m = IO (\s0 -> case newAlignedPinnedByteArray# nm a s0 of (# s1, mba #) -> case unsafeFreezeByteArray# mba s1 of (# s2, ba #) -> (# s2 , ( Ptr (byteArrayContents# ba) , let go k | isTrue# (k >=# nm) = [] | otherwise = unsafeFromByteArrayOffset k ba : go (k +# n) in go 0# ) #) ) {-# INLINE mallocVkDataArray# #-} peekVkData# :: forall a . (Storable a, VulkanMarshalPrim a) => Ptr a -> IO a peekVkData# (Ptr addr) | I# n <- sizeOf (undefined :: a) , I# a <- alignment (undefined :: a) = IO (\s -> case newAlignedPinnedByteArray# n a s of (# s1, mba #) -> case copyAddrToByteArray# addr mba 0# n s1 of s2 -> case unsafeFreezeByteArray# mba s2 of (# s3, ba #) -> (# s3, unsafeFromByteArrayOffset 0# ba #) ) {-# INLINE peekVkData# #-} pokeVkData# :: forall a . (Storable a, VulkanMarshalPrim a) => Ptr a -> a -> IO () pokeVkData# (Ptr addr) x = c_memcpy addr (unsafeAddr x) (fromIntegral $ sizeOf x) {-# INLINE pokeVkData# #-} -- | Internal function used to implement Eq and Ord instances for Vulkan structs. -- Compares first n bytes of two memory areas. -- -- Uses lexicographic ordering (c memcmp inside). -- -- This is a helper that should be used in VulkanMarshal instances only. cmpBytes# :: Int -> Addr# -> Addr# -> Ordering cmpBytes# n a b | isTrue# (eqAddr# a b) = EQ | otherwise = c_memcmp a b (fromIntegral n) `compare` 0 foreign import ccall unsafe "memcmp" c_memcmp :: Addr# -> Addr# -> CSize -> CInt foreign import ccall unsafe "memcpy" c_memcpy :: Addr# -> Addr# -> CSize -> IO ()