HORIZON HASKELLDocslts/ghc-9.14.x3e2d5682026-10-11Search names, modules, packages, or :: a typeCtrl K

GHC 9.14.1 · lts/ghc-9.14.x · 3e2d568 · 2026-10-11

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.

  • 3 types
  • 17 values
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.

valueacquireFrameImage
  1. :: MonadIO m
  2. => VulkanContext rr
  3. -> Frame rr
  4. -> m (Result, Word32)
#

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).

The canonical windowed frame extras.

Wait image-available; signal this image's render-finished and the host timeline at the frame index — the shape queueSubmitFrame hard-codes, for drivers that assemble their own submits.

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.