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:
phys : VkPhysicalDevice
queue_family : uint
surface : SurfaceKHR
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:
device : Device
sc : Swapchain
render_pass : RenderPass
- 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:
device : Device
phys : VkPhysicalDevice
surface : SurfaceKHR
width : int
height : int
preferred_format : VkFormat
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:
sc : Swapchain
device : Device
phys : VkPhysicalDevice
surface : SurfaceKHR
render_pass : RenderPass
width : int
height : int
- 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:
device : Device
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:
cmd : CommandBuffer
src : Image
src_layout : VkImageLayout
src_width : int
src_height : int
sc : Swapchain
target : VkImage
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:
device : Device
queue : VkQueue
sc : Swapchain
pool : CommandPool
sync : FrameSync
clear : VkClearValue
dynamic_viewport : bool
blk : block<(cmd: CommandBuffer):void>
- 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:
device : Device
queue : VkQueue
sc : Swapchain
pool : CommandPool
sync : FrameSync
blk : block<(cmd: CommandBuffer;target: VkImage;image_index:uint):void>
- 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:
cmd : CommandBuffer
sc : Swapchain
render_pass : RenderPass
image_index : int
clear : VkClearValue
dynamic_viewport : bool
blk : block<(cmd: CommandBuffer):void>
- 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:
cmd : CommandBuffer
sc : Swapchain
image_index : int
clear : VkClearValue
dynamic_viewport : bool
blk : block<(cmd: CommandBuffer):void>
- set_dynamic_viewport(cmd: CommandBuffer; width: int; height: int )
def set_dynamic_viewport (cmd: CommandBuffer; width: int; height: int)
- Arguments:
cmd : CommandBuffer
width : int
height : int
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.
- 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:
acquired : VkResult
- 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:
device : Device
queue : VkQueue
sc : Swapchain
pool : CommandPool
sync : FrameSync
raw_cmd : VkCommandBuffer
image_index : uint
wait_stage : VkPipelineStageFlags2
- 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:
device : Device
queue : VkQueue
sc : Swapchain
pool : CommandPool
sync : FrameSync
raw_cmd : VkCommandBuffer
image_index : uint
wait_stage : VkPipelineStageFlags