HORIZON HASKELLDocslts/ghc-10.0.x8fef28d2026-11-02Search packagesCtrl K
Theme: |

GHC 10.0.0.20260917 · lts/ghc-10.0.x · 8fef28d · 2026-11-02

Modulevulkan-utils-0.5.11.0Haskell2010

Vulkan.Utils.Frame

Per-frame state and the recycling-Frame loop. Each frame owns a binary image-available semaphore and a command pool — those are RecycledResources that get handed back to a channel in VulkanContext once the frame's GPU work has completed. (The present-wait/render-finished semaphore is per swapchain image, on the Swapchain, because it is only safe to reuse once its image is re-acquired — not when the frame's render finishes.)

The host-side timeline semaphore (fHostTimeline) lives across frames: each frame increments it to its own fIndex on the GPU, and the host waits on it inside the spawned wait-and-recycle thread.

This module requires Vulkan 1.2-level timeline-semaphore support. See frameInstanceRequirements / frameDeviceRequirements for the extension/feature requirements to merge into your boot sequence.

Synopsis
datadata Frame rr
#

Constructors

  • Frame
    • fIndex :: Word64

      Monotonic, used as the timeline-semaphore signal value for this frame.

    • fSwapchain :: Swapchain

      The swapchain this frame targets. Held by reference so a frame in flight keeps its swapchain alive across recreation.

    • fRecycled :: RecycledResources rr

      This frame's image-available semaphore + command pool — borrowed from the recycle channel; returned at retire time.

    • fHostTimeline :: Semaphore

      Long-lived timeline semaphore. Each frame increments it to fIndex on the GPU; the host wait thread blocks on this.

    • fGPUWork :: IORef [(Semaphore, Word64)]

      (Timeline semaphore, value) pairs the host wait thread will block on. Appended to by queueSubmitFrame.

    • fDeferredWork :: IORef [IO ()]

      Host work the recycle thread runs before the fGPUWork wait — e.g. a frame graph's host-pass tail, which blocks on its own timeline values and signals values listed in fGPUWork. Runs in registration order, off the render thread.

    • fResources :: (ReleaseKey, InternalState)

      ResourceT scope for frame-local allocations; closed when the frame retires. The ReleaseKey lives in the outer ResourceT so the scope is freed cleanly even on early shutdown.

valuerunFrame :: VulkanContext rr -> Frame rr -> ResourceT IO a -> IO a

Run a per-frame action against this frame's per-frame ResourceT scope, then asynchronously wait for the GPU work and recycle. The deferred host work and the wait/recycle run in a forked thread so the next frame can begin recording immediately.

Anything allocated inside action is freed when the frame retires.

Allocate a primary command buffer from this frame's recycled command pool, begin it with COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, run the caller's recording action, end recording, and return the buffer ready to hand to queueSubmitFrame.

For a non-standard begin shape (secondary level, different usage flags, inheritance info) call withCommandBuffers and useCommandBuffer directly.

valuequeueSubmitFrame
  1. :: MonadIO m
  2. => VulkanContext rr
  3. -> Frame rr
  4. -> Word32

    Acquired image index (from acquireFrameImage); selects the per-image present-wait semaphore to signal.

  5. -> Vector CommandBuffer
  6. -> m ()

Submit a per-frame command buffer batch and record the timeline-wait bookkeeping the host wait thread will block on.

Builds the standard frame submit from context/frame: waits on the frame's image-available semaphore at COLOR_ATTACHMENT_OUTPUT, signals the swapchain's per-image render-finished semaphore (at imageIndex) plus its timeline value, and submits on the graphics queue.

For a non-standard submit shape (multiple submit infos, different wait stage, extra signals), call queueSubmit2 directly and append (fHostTimeline f, fIndex f) to fGPUWork f.

Acquire the next swapchain image for this frame, signalling the frame's image-available semaphore on completion.

The acquire result is returned alongside the image index so the caller can thread it into presentFrameImage, which honours SUBOPTIMAL_KHR from either side by raising ERROR_OUT_OF_DATE_KHR to drive a swapchain recreation. Timeouts and unexpected results are also translated to ERROR_OUT_OF_DATE_KHR — the main loop's swapchain-recreation path is the right place to recover.

valuepresentFrameImage
  1. :: MonadIO m
  2. => VulkanContext rr
  3. -> Frame rr
  4. -> Result
  5. -> Word32
  6. -> m ()

Present this frame's acquired image, waiting on the swapchain's per-image render-finished semaphore (at imageIndex). Presents on the graphics queue (qGraphics . vcQueues).

If either the prior acquire (passed in) or this present reports SUBOPTIMAL_KHR, raises ERROR_OUT_OF_DATE_KHR so the main loop recreates the swapchain.

valuedrainFrames :: VulkanContext rr -> Frame rr -> IO ()

Shutdown drain: spawn the unrendered current frame's wait/recycle thread, then block on the recycle channel until both this frame's and the previous in-flight frame's deposits have arrived. After this returns, every forked wait thread has run its per-frame cleanup, so the outer ResourceT is safe to tear down GPU resources.

Assumes max-in-flight is 2 (see initialFrame).

Wait for some semaphores; if the wait times out, give the device one more chance with a zero timeout. Catches the case where the host was suspended during the wait and the GPU has actually finished.

Instance-level requirements for the recycling Frame machinery. Merge with your application's other InstanceRequirements at instance creation.

Required because checking PhysicalDeviceTimelineSemaphoreFeatures at physical-device pick time goes through VkPhysicalDeviceFeatures2, which needs either Vulkan 1.1+ or this extension.