content: {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE CPP                 #-}
  {-# LANGUAGE DataKinds           #-}
  {-# LANGUAGE MagicHash           #-}
  {-# LANGUAGE ScopedTypeVariables #-}
  {-# LANGUAGE Strict              #-}
  {-# LANGUAGE TypeApplications    #-}
  {-# LANGUAGE TypeFamilies        #-}
  -- | This module allows to load vulkan symbols at runtime.
  --
  --   It is based on Vulkan API function
  --   <https://www.khronos.org/registry/vulkan/specs/1.0/man/html/vkGetInstanceProcAddr.html vkGetInstanceProcAddr>
  --   that is a part of Vulkan core 1.0.
  --   Also, have a look at
  --   <https://vulkan.lunarg.com/doc/view/1.1.70.1/windows/loader_and_layer_interface.html#user-content-instance-versus-device Vulkan loader>
  --   page to see other reasons to load symbols manually.
  --
  --   All FFI functions are present in two variants:
  --   @xxxUnsafe@ and @xxxSafe@, the names stand for @foreign import unsafe xxx@
  --   @foreign import safe xxx@ respectively.
  --   In particular, that does not mean that @vkGetXxxProcSafe@ function cannot fail;
  --   it does error if the symbol is not present in the implementation!
  module Graphics.Vulkan.Marshal.Proc
    ( VulkanProc (..)
    , vkGetInstanceProc, vkGetInstanceProcUnsafe, vkGetInstanceProcSafe
    , vkLookupInstanceProc, vkLookupInstanceProcUnsafe, vkLookupInstanceProcSafe
    , vkGetDeviceProc, vkGetDeviceProcUnsafe, vkGetDeviceProcSafe
    , vkLookupDeviceProc, vkLookupDeviceProcUnsafe, vkLookupDeviceProcSafe
    , vkGetProc, vkGetProcUnsafe, vkGetProcSafe
    , vkLookupProc, vkLookupProcUnsafe, vkLookupProcSafe
      -- * Re-export `Foreign.Ptr`
    , FunPtr, nullFunPtr
    ) where
  
  import           Control.Monad                 (when)
  import           Data.Void                     (Void)
  import           Foreign.C.String              (CString, peekCString)
  import           Foreign.ForeignPtr            (ForeignPtr, newForeignPtr,
                                                  withForeignPtr)
  import           Foreign.Marshal.Alloc         (alloca)
  import           Foreign.Ptr                   (FunPtr, nullFunPtr, nullPtr)
  import           Foreign.Storable              (peek)
  import           GHC.Ptr                       (Ptr (..))
  import           GHC.TypeLits                  (Symbol)
  import           System.IO.Unsafe              (unsafePerformIO)
  
  
  
  import           Graphics.Vulkan.Types.Handles (VkDevice, VkInstance)
  
  -- | Some of the vulkan functions defined in vulkan extensions are not
  --   available at the program linking time.
  --   These functions should be discovered at runtime.
  --   Vulkan api provides special functions for this,
  --     called @vkGetInstanceProcAddr@ and @vkGetDeviceProcAddr@.
  --   This class provides a simpler discovery mechanism based on that function.
  --   For example, you can get @vkCreateDebugReportCallbackEXT@ function
  --   as follows:
  --
  --   > vkGetInstanceProc @VkCreateDebugReportCallbackEXT vkInstance
  class VulkanProc (proc :: Symbol) where
      -- | Haskell signature for the vulkan function
      type VkProcType proc
      -- | Name of the vulkan function
      vkProcSymbol :: CString
      -- | Convert C function pointer to an ordinary haskell function.
      --   Use unsafe FFI (@foreign import unsafe "dynamic" ...@).
      unwrapVkProcPtrUnsafe :: FunPtr (VkProcType proc) -> VkProcType proc
      -- | Convert C function pointer to an ordinary haskell function.
      --   Use safe FFI (@foreign import safe "dynamic" ...@).
      unwrapVkProcPtrSafe :: FunPtr (VkProcType proc) -> VkProcType proc
  
  
  
  --------------------------------------------------------------------------------
  -- Unsafe FFI version
  --------------------------------------------------------------------------------
  
  
  
  
  
  -- | An alternative to @vkGetInstanceProcAddr@ with type inference
  --   and protection against typos.
  --
  --   Note, this is an unsafe function;
  --   it does not check if the result of @vkGetInstanceProcAddr@
  --   is a null function pointer.
  vkGetInstanceProcUnsafe :: forall proc . VulkanProc proc
                          => VkInstance -> IO (VkProcType proc)
  vkGetInstanceProcUnsafe i
    = unwrapVkProcPtrUnsafe @proc
    <$> c'vkGetInstanceProcAddrUnsafe i (vkProcSymbol @proc)
  {-# INLINE vkGetInstanceProcUnsafe #-}
  
  -- | An alternative to @vkGetInstanceProcAddr@ with type inference
  --   and protection against typos.
  vkLookupInstanceProcUnsafe :: forall proc . VulkanProc proc
                             => VkInstance -> IO (Maybe (VkProcType proc))
  vkLookupInstanceProcUnsafe i
      = f <$> c'vkGetInstanceProcAddrUnsafe i (vkProcSymbol @proc)
    where
      f p = if p == nullFunPtr then Nothing else Just (unwrapVkProcPtrUnsafe @proc p)
  {-# INLINE vkLookupInstanceProcUnsafe #-}
  
  
  -- | An alternative to @vkGetDeviceProcAddr@ with type inference
  --   and protection against typos.
  --
  --   Note, this is an unsafe function;
  --   it does not check if the result of @vkGetInstanceProcAddr@
  --   is a null function pointer.
  vkGetDeviceProcUnsafe :: forall proc . VulkanProc proc
                        => VkDevice -> IO (VkProcType proc)
  vkGetDeviceProcUnsafe i
    = unwrapVkProcPtrUnsafe @proc
    <$> c'vkGetDeviceProcAddrUnsafe i (vkProcSymbol @proc)
  {-# INLINE vkGetDeviceProcUnsafe #-}
  
  -- | An alternative to @vkGetDeviceProcAddr@ with type inference
  --   and protection against typos.
  vkLookupDeviceProcUnsafe :: forall proc . VulkanProc proc
                           => VkDevice -> IO (Maybe (VkProcType proc))
  vkLookupDeviceProcUnsafe i
      = f <$> c'vkGetDeviceProcAddrUnsafe i (vkProcSymbol @proc)
    where
      f p = if p == nullFunPtr then Nothing else Just (unwrapVkProcPtrUnsafe @proc p)
  {-# INLINE vkLookupDeviceProcUnsafe #-}
  
  
  -- | Locate Vulkan symbol dynamically at runtime using platform-dependent machinery,
  --   such as @dlsym@ or @GetProcAddress@.
  --   This function throws an error on failure.
  --
  --   Consider using `vkGetDeviceProc` or `vkGetInstanceProc` for loading a symbol,
  --    because they can return a more optimized version of a function.
  --   Also note, you are likely not able to lookup an extension funcion using
  --   `vkGetProc`, because a corresponding symbol is simply not present in the
  --   vulkan loader library.
  vkGetProcUnsafe :: forall proc . VulkanProc proc => IO (VkProcType proc)
  vkGetProcUnsafe = alloca $ \errPtr -> do
      fp <- withForeignPtr _vkDlHandle $ \h ->
        c'vkdll_dlsym h (vkProcSymbol @proc) errPtr
      when (fp == nullFunPtr) $ peek errPtr >>= peekCString >>= fail .
          ("An error happened while trying to load vulkan symbol dynamically: " ++)
      return $ unwrapVkProcPtrUnsafe @proc fp
  {-# INLINE vkGetProcUnsafe #-}
  
  -- | Locate Vulkan symbol dynamically at runtime using platform-dependent machinery,
  --   such as @dlsym@ or @GetProcAddress@.
  --   This function returns @Nothing@ on failure ignoring an error message.
  --
  --   Consider using `vkGetDeviceProc` or `vkGetInstanceProc` for loading a symbol,
  --    because they can return a more optimized version of a function.
  --   Also note, you are likely not able to lookup an extension funcion using
  --   `vkLookupProc`, because a corresponding symbol is simply not present in the
  --   vulkan loader library.
  vkLookupProcUnsafe :: forall proc . VulkanProc proc => IO (Maybe (VkProcType proc))
  vkLookupProcUnsafe = alloca $ \errPtr -> do
      fp <- withForeignPtr _vkDlHandle $ \h ->
        c'vkdll_dlsym h (vkProcSymbol @proc) errPtr
      return $ if fp == nullFunPtr then Nothing else Just (unwrapVkProcPtrUnsafe @proc fp)
  {-# INLINE vkLookupProcUnsafe #-}
  
  
  
  --------------------------------------------------------------------------------
  -- Safe FFI version
  --------------------------------------------------------------------------------
  
  
  
  -- | An alternative to @vkGetInstanceProcAddr@ with type inference
  --   and protection against typos.
  --
  --   Note, this is an unsafe function;
  --   it does not check if the result of @vkGetInstanceProcAddr@
  --   is a null function pointer.
  vkGetInstanceProcSafe :: forall proc . VulkanProc proc
                    => VkInstance -> IO (VkProcType proc)
  vkGetInstanceProcSafe i
    = unwrapVkProcPtrSafe @proc
    <$> c'vkGetInstanceProcAddrSafe i (vkProcSymbol @proc)
  {-# INLINE vkGetInstanceProcSafe #-}
  
  -- | An alternative to @vkGetInstanceProcAddr@ with type inference
  --   and protection against typos.
  vkLookupInstanceProcSafe :: forall proc . VulkanProc proc
                       => VkInstance -> IO (Maybe (VkProcType proc))
  vkLookupInstanceProcSafe i
      = f <$> c'vkGetInstanceProcAddrSafe i (vkProcSymbol @proc)
    where
      f p = if p == nullFunPtr then Nothing else Just (unwrapVkProcPtrSafe @proc p)
  {-# INLINE vkLookupInstanceProcSafe #-}
  
  
  -- | An alternative to @vkGetDeviceProcAddr@ with type inference
  --   and protection against typos.
  --
  --   Note, this is an unsafe function;
  --   it does not check if the result of @vkGetInstanceProcAddr@
  --   is a null function pointer.
  vkGetDeviceProcSafe :: forall proc . VulkanProc proc
                  => VkDevice -> IO (VkProcType proc)
  vkGetDeviceProcSafe i
    = unwrapVkProcPtrSafe @proc
    <$> c'vkGetDeviceProcAddrSafe i (vkProcSymbol @proc)
  {-# INLINE vkGetDeviceProcSafe #-}
  
  -- | An alternative to @vkGetDeviceProcAddr@ with type inference
  --   and protection against typos.
  vkLookupDeviceProcSafe :: forall proc . VulkanProc proc
                     => VkDevice -> IO (Maybe (VkProcType proc))
  vkLookupDeviceProcSafe i
      = f <$> c'vkGetDeviceProcAddrSafe i (vkProcSymbol @proc)
    where
      f p = if p == nullFunPtr then Nothing else Just (unwrapVkProcPtrSafe @proc p)
  {-# INLINE vkLookupDeviceProcSafe #-}
  
  
  -- | Locate Vulkan symbol dynamically at runtime using platform-dependent machinery,
  --   such as @dlsym@ or @GetProcAddress@.
  --   This function throws an error on failure.
  --
  --   Consider using `vkGetDeviceProc` or `vkGetInstanceProc` for loading a symbol,
  --    because they can return a more optimized version of a function.
  --   Also note, you are likely not able to lookup an extension funcion using
  --   `vkGetProc`, because a corresponding symbol is simply not present in the
  --   vulkan loader library.
  vkGetProcSafe :: forall proc . VulkanProc proc => IO (VkProcType proc)
  vkGetProcSafe = alloca $ \errPtr -> do
      fp <- withForeignPtr _vkDlHandle $ \h ->
        c'vkdll_dlsym h (vkProcSymbol @proc) errPtr
      when (fp == nullFunPtr) $ peek errPtr >>= peekCString >>= fail .
          ("An error happened while trying to load vulkan symbol dynamically: " ++)
      return $ unwrapVkProcPtrSafe @proc fp
  {-# INLINE vkGetProcSafe #-}
  
  -- | Locate Vulkan symbol dynamically at runtime using platform-dependent machinery,
  --   such as @dlsym@ or @GetProcAddress@.
  --   This function returns @Nothing@ on failure ignoring an error message.
  --
  --   Consider using `vkLookupDeviceProc` or `vkLookupInstanceProc` for loading a symbol,
  --    because they can return a more optimized version of a function.
  --   Also note, you are likely not able to lookup an extension funcion using
  --   `vkLookupProc`, because a corresponding symbol is simply not present in the
  --   vulkan loader library.
  vkLookupProcSafe :: forall proc . VulkanProc proc => IO (Maybe (VkProcType proc))
  vkLookupProcSafe = alloca $ \errPtr -> do
      fp <- withForeignPtr _vkDlHandle $ \h ->
        c'vkdll_dlsym h (vkProcSymbol @proc) errPtr
      return $ if fp == nullFunPtr then Nothing else Just (unwrapVkProcPtrSafe @proc fp)
  {-# INLINE vkLookupProcSafe #-}
  
  
  
  --------------------------------------------------------------------------------
  -- Default FFI version
  --------------------------------------------------------------------------------
  
  
  
  -- | An alternative to @vkGetInstanceProcAddr@ with type inference
  --   and protection against typos.
  --
  --   Note, this is an unsafe function;
  --   it does not check if the result of @vkGetInstanceProcAddr@
  --   is a null function pointer.
  vkGetInstanceProc :: forall proc . VulkanProc proc
                    => VkInstance -> IO (VkProcType proc)
  vkGetInstanceProc =
  #ifdef UNSAFE_FFI_DEFAULT
    vkGetInstanceProcUnsafe @proc
  #else
    vkGetInstanceProcSafe @proc
  #endif
  {-# INLINE vkGetInstanceProc #-}
  
  -- | An alternative to @vkGetInstanceProcAddr@ with type inference
  --   and protection against typos.
  vkLookupInstanceProc :: forall proc . VulkanProc proc
                       => VkInstance -> IO (Maybe (VkProcType proc))
  vkLookupInstanceProc =
  #ifdef UNSAFE_FFI_DEFAULT
    vkLookupInstanceProcUnsafe @proc
  #else
    vkLookupInstanceProcSafe @proc
  #endif
  {-# INLINE vkLookupInstanceProc #-}
  
  
  -- | An alternative to @vkGetDeviceProcAddr@ with type inference
  --   and protection against typos.
  --
  --   Note, this is an unsafe function;
  --   it does not check if the result of @vkGetInstanceProcAddr@
  --   is a null function pointer.
  vkGetDeviceProc :: forall proc . VulkanProc proc
                  => VkDevice -> IO (VkProcType proc)
  vkGetDeviceProc =
  #ifdef UNSAFE_FFI_DEFAULT
    vkGetDeviceProcUnsafe @proc
  #else
    vkGetDeviceProcSafe @proc
  #endif
  {-# INLINE vkGetDeviceProc #-}
  
  -- | An alternative to @vkGetDeviceProcAddr@ with type inference
  --   and protection against typos.
  vkLookupDeviceProc :: forall proc . VulkanProc proc
                     => VkDevice -> IO (Maybe (VkProcType proc))
  vkLookupDeviceProc =
  #ifdef UNSAFE_FFI_DEFAULT
    vkLookupDeviceProcUnsafe @proc
  #else
    vkLookupDeviceProcSafe @proc
  #endif
  {-# INLINE vkLookupDeviceProc #-}
  
  
  -- | Locate Vulkan symbol dynamically at runtime using platform-dependent machinery,
  --   such as @dlsym@ or @GetProcAddress@.
  --   This function throws an error on failure.
  --
  --   Consider using `vkGetDeviceProc` or `vkGetInstanceProc` for loading a symbol,
  --    because they can return a more optimized version of a function.
  --   Also note, you are likely not able to lookup an extension funcion using
  --   `vkGetProc`, because a corresponding symbol is simply not present in the
  --   vulkan loader library.
  vkGetProc :: forall proc . VulkanProc proc => IO (VkProcType proc)
  vkGetProc =
  #ifdef UNSAFE_FFI_DEFAULT
    vkGetProcUnsafe @proc
  #else
    vkGetProcSafe @proc
  #endif
  {-# INLINE vkGetProc #-}
  
  -- | Locate Vulkan symbol dynamically at runtime using platform-dependent machinery,
  --   such as @dlsym@ or @GetProcAddress@.
  --   This function returns @Nothing@ on failure ignoring an error message.
  --
  --   Consider using `vkGetDeviceProc` or `vkGetInstanceProc` for loading a symbol,
  --    because they can return a more optimized version of a function.
  --   Also note, you are likely not able to lookup an extension funcion using
  --   `vkLookupProc`, because a corresponding symbol is simply not present in the
  --   vulkan loader library.
  vkLookupProc :: forall proc . VulkanProc proc => IO (Maybe (VkProcType proc))
  vkLookupProc =
  #ifdef UNSAFE_FFI_DEFAULT
    vkLookupProcUnsafe @proc
  #else
    vkLookupProcSafe @proc
  #endif
  {-# INLINE vkLookupProc #-}
  
  
  
  
  --------------------------------------------------------------------------------
  -- Utilities
  --------------------------------------------------------------------------------
  
  
  
  
  #ifdef VK_NO_PROTOTYPES
  
  c'vkGetInstanceProcAddrSafe :: VkInstance -> CString -> IO (FunPtr a)
  c'vkGetInstanceProcAddrSafe = c'vkGetInstanceProcAddr' unwrap'vkGetInstanceProcAddrSafe
  
  c'vkGetInstanceProcAddrUnsafe :: VkInstance -> CString -> IO (FunPtr a)
  c'vkGetInstanceProcAddrUnsafe = c'vkGetInstanceProcAddr' unwrap'vkGetInstanceProcAddrUnsafe
  
  c'vkGetDeviceProcAddrSafe :: VkDevice -> CString -> IO (FunPtr a)
  c'vkGetDeviceProcAddrSafe = c'vkGetDeviceProcAddr' unwrap'vkGetDeviceProcAddrSafe
  
  c'vkGetDeviceProcAddrUnsafe :: VkDevice -> CString -> IO (FunPtr a)
  c'vkGetDeviceProcAddrUnsafe = c'vkGetDeviceProcAddr' unwrap'vkGetDeviceProcAddrUnsafe
  
  c'vkGetInstanceProcAddr'
      :: ( FunPtr (VkInstance -> CString -> IO (FunPtr a))
            -> VkInstance -> CString -> IO (FunPtr a)
         )
      -> VkInstance -> CString -> IO (FunPtr a)
  c'vkGetInstanceProcAddr' k = unsafePerformIO $ alloca $ \errPtr -> do
      fp <- withForeignPtr _vkDlHandle $ \h ->
        c'vkdll_dlsym h (Ptr "vkGetInstanceProcAddr"#) errPtr
      when (fp == nullFunPtr) $
        peek errPtr >>= peekCString >>= fail .
          ("Could not load 'vkGetInstanceProcAddr' C function from vulkan library dynamically: " ++)
      return $ k fp
  
  c'vkGetDeviceProcAddr'
      :: ( FunPtr (VkDevice -> CString -> IO (FunPtr a))
            -> VkDevice -> CString -> IO (FunPtr a)
         )
      -> VkDevice -> CString -> IO (FunPtr a)
  c'vkGetDeviceProcAddr' k = unsafePerformIO $ alloca $ \errPtr -> do
      fp <- withForeignPtr _vkDlHandle $ \h ->
        c'vkdll_dlsym h (Ptr "vkGetDeviceProcAddr"#) errPtr
      when (fp == nullFunPtr) $ peek errPtr >>= peekCString >>= fail .
          ("Could not load 'vkGetDeviceProcAddr' C function from vulkan library dynamically: " ++)
      return $ k fp
  
  foreign import ccall safe "dynamic"
    unwrap'vkGetInstanceProcAddrSafe
      :: FunPtr (VkInstance -> CString -> IO (FunPtr a))
      -> VkInstance -> CString -> IO (FunPtr a)
  
  foreign import ccall safe "dynamic"
    unwrap'vkGetDeviceProcAddrSafe
      :: FunPtr (VkDevice -> CString -> IO (FunPtr a))
      -> VkDevice -> CString -> IO (FunPtr a)
  
  foreign import ccall unsafe "dynamic"
    unwrap'vkGetInstanceProcAddrUnsafe
      :: FunPtr (VkInstance -> CString -> IO (FunPtr a))
      -> VkInstance -> CString -> IO (FunPtr a)
  
  foreign import ccall unsafe "dynamic"
    unwrap'vkGetDeviceProcAddrUnsafe
      :: FunPtr (VkDevice -> CString -> IO (FunPtr a))
      -> VkDevice -> CString -> IO (FunPtr a)
  
  #else
  
  foreign import ccall safe "vkGetInstanceProcAddr"
    c'vkGetInstanceProcAddrSafe :: VkInstance -> CString -> IO (FunPtr a)
  
  foreign import ccall safe "vkGetDeviceProcAddr"
    c'vkGetDeviceProcAddrSafe :: VkDevice -> CString -> IO (FunPtr a)
  
  foreign import ccall unsafe "vkGetInstanceProcAddr"
    c'vkGetInstanceProcAddrUnsafe :: VkInstance -> CString -> IO (FunPtr a)
  
  foreign import ccall unsafe "vkGetDeviceProcAddr"
    c'vkGetDeviceProcAddrUnsafe :: VkDevice -> CString -> IO (FunPtr a)
  
  
  #endif
  
  
  foreign import ccall safe "_vkdll_dlinit"
    c'vkdll_dlinit :: Ptr CString -> IO (Ptr Void)
  
  foreign import ccall safe "_vkdll_dlsym"
    c'vkdll_dlsym :: Ptr Void -> CString -> Ptr CString -> IO (FunPtr a)
  
  foreign import ccall safe "&_vkdll_dlclose"
    p'vk_dlclose :: FunPtr (Ptr Void -> IO ())
  
  _vkDlHandle :: ForeignPtr Void
  _vkDlHandle = unsafePerformIO $ alloca $ \errPtr -> do
    handle <- c'vkdll_dlinit errPtr
    if handle == nullPtr
    then
      peek errPtr >>= peekCString >>= fail .
        ("An error happened while trying to load vulkan library dynamically: " ++)
    else
      newForeignPtr p'vk_dlclose handle
  {-# NOINLINE _vkDlHandle #-}
  