vulkan-utils-0.5.11.0: src/Vulkan/Utils/VulkanContext.hs
{-| Application-static Vulkan handles plus the recycle channel ends used by
the recycling 'Vulkan.Utils.Frame.Frame' machinery.
Constructed once at boot, never modified. Sub-systems (swapchain, frame loop,
window loop) accept a 'VulkanContext' so they don't need their own copies of
device/queues plumbing.
-}
module Vulkan.Utils.VulkanContext
( VulkanContext (..)
, RecycledResources (..)
, mkVulkanContext
) where
import Control.Concurrent.Chan.Unagi
import qualified Vulkan.Core10 as Vk
import Vulkan.Utils.QueueAssignment (QueueFamilyIndex)
import Vulkan.Utils.Queues (Queues (..))
{- | A bunch of long-lived handles that the application carries around. The
recycle channel ends carry per-frame 'RecycledResources' between the frame
loop and the wait-and-recycle thread.
-}
data VulkanContext rr = VulkanContext
{ vcInstance :: Vk.Instance
, vcPhysicalDevice :: Vk.PhysicalDevice
, vcDevice :: Vk.Device
, vcQueues :: Queues (QueueFamilyIndex, Vk.Queue)
, vcRecycleBin :: (RecycledResources rr) -> IO ()
{- ^ Drop a frame's reusable bits back into the pool. Called from the
per-frame wait thread once the GPU is done with the frame.
-}
, vcRecycleNib :: IO (Either (IO (RecycledResources rr)) (RecycledResources rr))
{- ^ Pull a frame's reusable bits out. 'Right' if available immediately;
'Left' is a blocking read.
-}
}
{- | The bits of state recycled between frames: a binary image-available
semaphore (signalled by image acquisition, waited on by the frame's submit)
and the command pool the frame's commands are recorded into.
The render-finished / present-wait semaphore is /not/ here — it is per
swapchain image (see 'Vulkan.Utils.Swapchain.sRenderFinished'), because a
present-wait semaphore is only safe to reuse once its image is re-acquired,
not when the frame's render completes.
-}
data RecycledResources a = RecycledResources
{ rrImageAvailable :: Vk.Semaphore
, rrCommandPools :: Queues Vk.CommandPool
{- ^ One command pool per queue role, reset when the frame retires. Roles
sharing a queue family share the pool handle, so a frame holds one pool
per distinct family — pools are expensive to create and cheap to reset,
which is the whole point of recycling them.
-}
, rrData :: a
-- ^ Double-buffered data of the application to ping-pong around updating and rendering.
}
{- | Assemble a 'VulkanContext' from already-constructed handles. Builds the
recycle channel internally; the channel starts empty and is populated by
'Vulkan.Utils.Frame.initialFrame'.
-}
mkVulkanContext
:: Vk.Instance
-> Vk.PhysicalDevice
-> Vk.Device
-> Queues (QueueFamilyIndex, Vk.Queue)
-> IO (VulkanContext rr)
mkVulkanContext vcInstance vcPhysicalDevice vcDevice vcQueues = do
(binW, binR) <- newChan
let
vcRecycleBin = writeChan binW
vcRecycleNib = do
(try, block) <- tryReadChan binR
maybe (Left block) Right <$> tryRead try
pure VulkanContext{..}