packages feed

keid-core-0.1.4.0: src/Resource/CommandBuffer.hs

module Resource.CommandBuffer
  ( allocatePools
  , withPools
  , oneshot_
  ) where

import RIO

import Data.Vector qualified as Vector
import UnliftIO.Resource (MonadResource)
import UnliftIO.Resource qualified as Resource
import Vulkan.Core10 qualified as Vk
import Vulkan.CStruct.Extends (SomeStruct(..))
import Vulkan.Utils.QueueAssignment (QueueFamilyIndex(..))
import Vulkan.Zero (zero)

import Engine.Vulkan.Types (HasVulkan(..), MonadVulkan, Queues(..))

allocatePools
  :: (HasVulkan context, Resource.MonadResource m)
  => context
  -> m (Resource.ReleaseKey, Queues Vk.CommandPool)
allocatePools context = do
  -- context <- ask
  bootstrapQueues <- for (getQueues context) \(QueueFamilyIndex ix, _queue) -> do
    let
      commandPoolCI = Vk.CommandPoolCreateInfo
        { flags            = zero
        , queueFamilyIndex = ix
        }
    Vk.withCommandPool (getDevice context) commandPoolCI Nothing Resource.allocate

  bqKey <- Resource.register $
    traverse_ (Resource.release . fst) bootstrapQueues
  pure (bqKey, fmap snd bootstrapQueues)

withPools
  :: (MonadVulkan env m, MonadResource m)
  => (Queues Vk.CommandPool -> m a)
  -> m a
withPools action = do
  context <- ask
  bracket (allocatePools context) (Resource.release . fst) (action . snd)

-- | Scratch command buffer for transfer operations.
-- The simple fence makes it unusable for rendering.
oneshot_
  :: (HasVulkan context, MonadUnliftIO m)
  => context
  -> Queues Vk.CommandPool
  -> (forall a . Queues a -> a)
  -> (Vk.CommandBuffer -> m ())
  -> m ()
oneshot_ context pools pickQueue action =
  Vk.withCommandBuffers device commandBufferAllocateInfo bracket \case
    (Vector.toList -> [buf]) -> do
      let
        oneTime :: Vk.CommandBufferBeginInfo '[]
        oneTime = zero
          { Vk.flags = Vk.COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
          }

      Vk.useCommandBuffer buf oneTime $ action buf

      Vk.withFence device zero Nothing bracket \fence -> do
        Vk.queueSubmit queue (wrap buf) fence
        Vk.waitForFences device (pure fence) True maxBound >>= \case
          Vk.SUCCESS ->
            -- traceM "oneshot_: queue finished"
            pure ()
          err ->
            error $ "oneshot_ failed: " <> show err
    _ ->
      error "assert: exactly the requested buffer was given"
  where
    device = getDevice context
    queue = snd . pickQueue $ getQueues context

    commandBufferAllocateInfo :: Vk.CommandBufferAllocateInfo
    commandBufferAllocateInfo = zero
      { Vk.commandPool        = pickQueue pools
      , Vk.level              = Vk.COMMAND_BUFFER_LEVEL_PRIMARY
      , Vk.commandBufferCount = 1
      }

    wrap buf = Vector.singleton $ SomeStruct zero
      { Vk.commandBuffers =
          Vector.singleton (Vk.commandBufferHandle buf)
      }