 -- |
 -- Copy the given number of elements from the first device array (source) to the
 -- second (destination). The copied areas may not overlap. This operation is
--- asynchronous with respect to host, but will never overlap with kernel
--- execution.
+-- asynchronous with respect to the host, and may be associated with a
+-- particular stream.
 --
 {-# INLINEABLE copyArrayAsync #-}
 copyArrayAsync :: Storable a => Int -> DevicePtr a -> DevicePtr a -> Maybe Stream -> IO ()
 copyArrayAsync !n !src !dst !mst =
   memcpyAsync (useDevicePtr dst) (useDevicePtr src) n DeviceToDevice mst
+
+
+-- |
+-- Copy a 2D memory area from the first device array (source) to the second
+-- (destination). The copied areas may not overlap. This operation is
+-- asynchronous with respect to the host, but will not overlap other device
+-- operations.
+--
+{-# INLINEABLE copyArray2D #-}
+copyArray2D
+    :: Storable a
+    => Int                      -- ^ width to copy (elements)
+    -> Int                      -- ^ height to copy (elements)
+    -> DevicePtr a              -- ^ source array
+    -> Int                      -- ^ source array width
+    -> DevicePtr a              -- ^ destination array
+    -> Int                      -- ^ destination array width
+    -> IO ()
+copyArray2D !w !h !src !sw !dst !dw =
+  memcpy2D (useDevicePtr dst) dw (useDevicePtr src) sw w h DeviceToDevice
+
+
+-- |
+-- Copy a 2D memory area from the first device array (source) to the second
+-- device array (destination). The copied areas may not overlay. This operation
+-- is asynchronous with respect to the host, and may be associated with a
+-- particular stream.
+--
+{-# INLINEABLE copyArray2DAsync #-}
+copyArray2DAsync
+    :: Storable a
+    => Int                      -- ^ width to copy (elements)
+    -> Int                      -- ^ height to copy (elements)
+    -> DevicePtr a              -- ^ source array
+    -> Int                      -- ^ source array width
+    -> DevicePtr a              -- ^ destination array
+    -> Int                      -- ^ destination array width
+    -> Maybe Stream
+    -> IO ()
+copyArray2DAsync !w !h !src !sw !dst !dw !mst =
+  memcpy2DAsync (useDevicePtr dst) dw (useDevicePtr src) sw w h DeviceToDevice mst
 
 
 --

    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



-- |
-- Copy a 2D memory area onto the device. This is a synchronous operation.
--
{-# INLINEABLE pokeArray2D #-}
pokeArray2D
    :: Storable a
    => Int                      -- ^ width to copy (elements)
    -> Int                      -- ^ height to copy (elements)
    -> Ptr a                    -- ^ source array
    -> Int                      -- ^ source array width
    -> DevicePtr a              -- ^ destination array
    -> Int                      -- ^ destination array width
    -> IO ()
pokeArray2D !w !h !hptr !dw !dptr !hw =
  memcpy2D (useDevicePtr dptr) dw hptr hw w h HostToDevice


-- |
-- Copy a 2D memory area onto the device asynchronously, possibly associated
-- with a particular stream. The source array must be page locked.
--
{-# INLINEABLE pokeArray2DAsync #-}
pokeArray2DAsync
    :: Storable a
    => Int                      -- ^ width to copy (elements)
    -> Int                      -- ^ height to copy (elements)
    -> HostPtr a                -- ^ source array
    -> Int                      -- ^ source array width
    -> DevicePtr a              -- ^ destination array
    -> Int                      -- ^ destination array width
    -> Maybe Stream
    -> IO ()
pokeArray2DAsync !w !h !hptr !hw !dptr !dw !mst =
  memcpy2DAsync (useDevicePtr dptr) dw (useHostPtr hptr) hw w h HostToDevice mst

-- |
-- Write a list of storable elements into a device array. The array must be
-- sufficiently large to hold the entire list. This requires two marshalling
-- operations
