 
 
 -- |
+-- Copy a 2D memory area from the device to the host. This is a synchronous
+-- operation.
+--
+{-# INLINEABLE peekArray2D #-}
+peekArray2D
+    :: Storable a
+    => Int                      -- ^ width to copy (elements)
+    -> Int                      -- ^ height to copy (elements)
+    -> DevicePtr a              -- ^ source array
+    -> Int                      -- ^ source array width
+    -> Ptr a                    -- ^ destination array
+    -> Int                      -- ^ destination array width
+    -> IO ()
+peekArray2D !w !h !dptr !dw !hptr !hw =
+  memcpy2D hptr hw (useDevicePtr dptr) dw w h DeviceToHost
+
+
+-- |
+-- Copy a 2D memory area from the device to the host asynchronously, possibly
+-- associated with a particular stream. The destination array must be page
+-- locked.
+--
+{-# INLINEABLE peekArray2DAsync #-}
+peekArray2DAsync
+    :: Storable a
+    => Int                      -- ^ width to copy (elements)
+    -> Int                      -- ^ height to copy (elements)
+    -> DevicePtr a              -- ^ source array
+    -> Int                      -- ^ source array width
+    -> HostPtr a                -- ^ destination array
+    -> Int                      -- ^ destination array width
+    -> Maybe Stream
+    -> IO ()
+peekArray2DAsync !w !h !dptr !dw !hptr !hw !mst =
+  memcpy2DAsync (useHostPtr hptr) hw (useDevicePtr dptr) dw w h DeviceToHost mst
+
+
+-- |
 -- Copy a number of elements from the device into a new Haskell list. Note that
 -- this requires two memory copies: firstly from the device into a heap
 -- allocated array, and from there marshalled into a list

    doMemcpy :: Storable a' => a' -> Ptr a' -> IO ()
    doMemcpy x _ =
      let bytes = fromIntegral n * fromIntegral (sizeOf x) in
      nothingIfOk =<< cudaMemcpyAsync dst src bytes kind (maybe defaultStream id mst)

{-# INLINE cudaMemcpyAsync #-}
{# fun unsafe cudaMemcpyAsync

  , useStream `Stream'        } -> `Status' cToEnum #}


-- |
-- Copy a 2D memory area between the host and device. This is a synchronous
-- operation.
--
{-# INLINEABLE memcpy2D #-}
memcpy2D :: Storable a
         => Ptr a               -- ^ destination
         -> Int                 -- ^ width of destination array
         -> Ptr a               -- ^ source
         -> Int                 -- ^ width of source array
         -> Int                 -- ^ width to copy
         -> Int                 -- ^ height to copy
         -> CopyDirection
         -> IO ()
memcpy2D !dst !dw !src !sw !w !h !kind = doCopy undefined dst
  where
    doCopy :: Storable a' => a' -> Ptr a' -> IO ()
    doCopy x _ =
      let bytes = fromIntegral (sizeOf x)
          dw'   = fromIntegral dw * bytes
          sw'   = fromIntegral sw * bytes
          w'    = fromIntegral w  * bytes
          h'    = fromIntegral h
      in
      nothingIfOk =<< cudaMemcpy2D dst dw' src sw' w' h' kind

{-# INLINE cudaMemcpy2D #-}
{# fun unsafe cudaMemcpy2D
  { castPtr   `Ptr a'
  ,           `Int64'
  , castPtr   `Ptr a'
  ,           `Int64'
  ,           `Int64'
  ,           `Int64'
  , cFromEnum `CopyDirection'
  }
  -> `Status' cToEnum #}


-- |
-- Copy a 2D memory area between the host and device asynchronously, possibly
-- associated with a particular stream. The host-side memory must be
-- page-locked.
--
{-# INLINEABLE memcpy2DAsync #-}
memcpy2DAsync :: Storable a
              => Ptr a          -- ^ destination
              -> Int            -- ^ width of destination array
              -> Ptr a          -- ^ source
              -> Int            -- ^ width of source array
              -> Int            -- ^ width to copy
              -> Int            -- ^ height to copy
              -> CopyDirection
              -> Maybe Stream
              -> IO ()
memcpy2DAsync !dst !dw !src !sw !w !h !kind !mst = doCopy undefined dst
  where
    doCopy :: Storable a' => a' -> Ptr a' -> IO ()
    doCopy x _ =
      let bytes = fromIntegral (sizeOf x)
          dw'   = fromIntegral dw * bytes
          sw'   = fromIntegral sw * bytes
          w'    = fromIntegral w  * bytes
          h'    = fromIntegral h
          st    = fromMaybe defaultStream mst
      in
      nothingIfOk =<< cudaMemcpy2DAsync dst dw' src sw' w' h' kind st

{-# INLINE cudaMemcpy2DAsync #-}
{# fun unsafe cudaMemcpy2DAsync
  { castPtr   `Ptr a'
  ,           `Int64'
  , castPtr   `Ptr a'
  ,           `Int64'
  ,           `Int64'
  ,           `Int64'
  , cFromEnum `CopyDirection'
  , useStream `Stream'
  }
  -> `Status' cToEnum #}


--------------------------------------------------------------------------------
-- Combined Allocation and Marshalling
--------------------------------------------------------------------------------
