20.6. Window — surface, swapchain, frame sync, and the present loop

Windowing and presentation: create a SurfaceKHR from a native window, build and recreate a Swapchain (with its images, views, and framebuffers), set up per-frame FrameSync primitives, and run the acquire → record → submit → present loop via draw_frame. Pair with create_graphics_pipeline_simple(..., dynamic_viewport = true) for resize-friendly pipelines.

20.6.1. Constants

UINT64_MAX = 0xffffffffffffffff

UINT64_MAX:uint64 const

20.6.2. Structures

Swapchain
Fields:
  • handle : SwapchainKHR - < the VkSwapchainKHR

  • format : VkFormat - < color format of the swapchain images

  • color_space : VkColorSpaceKHR - < color space the surface presents in

  • width : int - < current extent width, in pixels

  • height : int - < current extent height, in pixels

  • images : array< VkImage> - < owned by the swapchain object itself; not destroyed here

  • views : array< ImageView> - < created + owned here

  • framebuffers : array< Framebuffer> - < created + owned here (after build_swapchain_framebuffers)

  • readback_supported : bool - < true if the images carry TRANSFER_SRC (host readback is possible)

FrameSync

struct FrameSync

20.6.3. Surfaces

create_surface(instance: Instance; native_window: void?; native_display: void? = null ): SurfaceKHR

def create_surface (instance: Instance; native_window: void?; native_display: void? = null) : SurfaceKHR

Arguments:
  • instance : Instance

  • native_window : void?

  • native_display : void?

queue_family_supports_present(phys: VkPhysicalDevice; queue_family: uint; surface: SurfaceKHR ): bool

def queue_family_supports_present (phys: VkPhysicalDevice; queue_family: uint; surface: SurfaceKHR) : bool

Arguments:

20.6.4. Swapchain

build_swapchain_framebuffers(device: Device; sc: Swapchain; render_pass: RenderPass )

def build_swapchain_framebuffers (device: Device; var sc: Swapchain; render_pass: RenderPass)

Arguments:
create_swapchain(device: Device; phys: VkPhysicalDevice; surface: SurfaceKHR; width: int; height: int; preferred_format: VkFormat = vulkan::VkFormat.UNDEFINED ): Swapchain

def create_swapchain (device: Device; phys: VkPhysicalDevice; surface: SurfaceKHR; width: int; height: int; preferred_format: VkFormat = vulkan::VkFormat.UNDEFINED) : Swapchain

Arguments:

20.6.4.1. recreate_swapchain

recreate_swapchain(sc: Swapchain; device: Device; phys: VkPhysicalDevice; surface: SurfaceKHR; render_pass: RenderPass; width: int; height: int )

recreate_swapchain for the render-pass frame loop: also rebuilds one framebuffer per image against render_pass.

Arguments:
recreate_swapchain(sc: Swapchain; device: Device; phys: VkPhysicalDevice; surface: SurfaceKHR; width: int; height: int )

20.6.5. Frame sync

create_frame_sync(device: Device ): FrameSync

def create_frame_sync (device: Device) : FrameSync

Arguments:

20.6.6. Frame loop

blit_to_swapchain(cmd: CommandBuffer; src: Image; src_layout: VkImageLayout; src_width: int; src_height: int; sc: Swapchain; target: VkImage )

The blit-present body for a frame rendered offscreen: target (the acquired image, UNDEFINED) goes to TRANSFER_DST_OPTIMAL sourced at blit (the transfer stage present_frame’s acquire wait lands on), src in src_layout (TRANSFER_SRC_OPTIMAL or GENERAL) is blitted over the swapchain extent, target leaves in PRESENT_SRC_KHR.

Arguments:

20.6.6.1. draw_frame

draw_frame(device: Device; queue: VkQueue; sc: Swapchain; pool: CommandPool; sync: FrameSync; clear: VkClearValue; dynamic_viewport: bool = false; blk: block<(cmd:CommandBuffer):void> ): bool

acquire -> record (record_swapchain_rendering around the user block) -> vkQueueSubmit2 -> present, then wait idle; false when the swapchain is out of date or lost (caller recreates). Pipelines come from the color_format creators with sc.format, dynamic_viewport ones with a per-frame viewport+scissor.

Arguments:
draw_frame(device: Device; queue: VkQueue; sc: Swapchain; render_pass: RenderPass; pool: CommandPool; sync: FrameSync; clear: VkClearValue; dynamic_viewport: bool = false; blk: block<(cmd:CommandBuffer):void> ): bool

present_frame(device: Device; queue: VkQueue; sc: Swapchain; pool: CommandPool; sync: FrameSync; blk: block<(cmd:CommandBuffer;target:VkImage;image_index:uint):void> ): bool

acquire -> record a RAW command block (no render pass) -> submit -> present, then wait idle. Sibling of draw_frame; the block gets (cmd, acquired VkImage, index) and MUST leave the image — UNDEFINED on entry — in PRESENT_SRC_KHR. Waits at the transfer stage. Returns false if the swapchain is stale.

Arguments:
record_swapchain_render_pass(cmd: CommandBuffer; sc: Swapchain; render_pass: RenderPass; image_index: int; clear: VkClearValue; dynamic_viewport: bool; blk: block<(cmd:CommandBuffer):void> )

The render-pass body of a frame on swapchain image image_index: blk records inside record_render_pass on the image’s framebuffer (build_swapchain_framebuffers first); the pass’s final layout leaves the image in PRESENT_SRC_KHR.

Arguments:
record_swapchain_rendering(cmd: CommandBuffer; sc: Swapchain; image_index: int; clear: VkClearValue; dynamic_viewport: bool; blk: block<(cmd:CommandBuffer):void> )

The dynamic-rendering body of a frame on swapchain image image_index: UNDEFINED -> COLOR_ATTACHMENT_OPTIMAL sourced at color_attachment_output (the stage the acquire wait lands on), blk inside record_rendering on the image’s view (a full-extent viewport+scissor first when dynamic_viewport), then -> PRESENT_SRC_KHR.

Arguments:
set_dynamic_viewport(cmd: CommandBuffer; width: int; height: int )

def set_dynamic_viewport (cmd: CommandBuffer; width: int; height: int)

Arguments:

20.6.7. Frame steps

acquire_frame_image(device: Device; sc: Swapchain; sync: FrameSync; image_index: uint& ): VkResult

vkAcquireNextImageKHR on sc, signalling sync.image_available. image_index is valid only when frame_acquired says so of the result.

Arguments:
frame_acquired(acquired: VkResult ): bool

True when an acquire_frame_image result carries a usable image index: SUCCESS, or SUBOPTIMAL_KHR, which still presents. Anything else means the swapchain is out of date or lost - recreate it.

Arguments:
submit2_and_present(device: Device; queue: VkQueue; sc: Swapchain; pool: CommandPool; sync: FrameSync; raw_cmd: VkCommandBuffer; image_index: uint; wait_stage: VkPipelineStageFlags2 ): VkResult

submit_and_present through vkQueueSubmit2 (synchronization2): wait_stage is the 64-bit VkPipelineStageFlags2 the image-available wait lands on.

Arguments:
submit_and_present(device: Device; queue: VkQueue; sc: Swapchain; pool: CommandPool; sync: FrameSync; raw_cmd: VkCommandBuffer; image_index: uint; wait_stage: VkPipelineStageFlags ): VkResult

Ends raw_cmd, submits it through vkQueueSubmit - waiting on sync.image_available at wait_stage, signalling sync.render_finished - then presents image_index, waits for the queue to idle and frees the buffer. Returns the present result.

Arguments: