Swapchain assumes exactly 3 images: minImageCount ignores surface caps, images[] can overflow #160

Open
opened 2026-10-06 13:25:46 +00:00 by jorijnvdgraaf · 0 comments

Summary

Swapchain creation assumes the driver returns exactly Window::numFrames (3) images. It never compares that against the surface capabilities, and it reads the images into a fixed 3-element array. Line numbers refer to 6aa0338.

Details

  • implementations/Crafter.Graphics-Window.cpp:1061: swapchainCI.minImageCount = numFrames;. The capabilities queried at :1027 are used for extent and transform only; minImageCount / maxImageCount are ignored.
  • :1102-1105: the count returned by the first vkGetSwapchainImagesKHR call is passed straight to the second, which writes into VkImage images[numFrames] (interfaces/Crafter.Graphics-Window.cppm:261). Every other per-image array (command buffers, fences, image view infos, the per-frame heap copies) is sized numFrames as well.

Two consequences:

  1. Out-of-bounds write whenever the swapchain has more than 3 images. A driver may create more images than minImageCount asks for.
  2. Invalid create info when the surface minimum is above 3: the spec requires minImageCount to be at least the surface's minimum (or the minimum for the chosen present mode, when that is queried and passed along).

Concrete case

Mesa Turnip on phoc (Fairphone 6, Mesa 26.2.2) reports:

minImageCount
surface in general 4
FIFO (per present mode, VK_KHR_surface_maintenance1) 3
MAILBOX 4

Present modes offered: MAILBOX, FIFO (no IMMEDIATE).

  • Default FIFO path: 3 requested, Mesa creates 3, the UI renders. Verified on the device. The request is still below the general surface minimum; the phone has no validation layer, so I did not see whether it reports that.
  • CRAFTER_PRESENT_IMMEDIATE (:1076-1087) falls back to MAILBOX there, which needs 4 images. Not run; by the numbers above it overflows images[].

The descriptor-set fallback change (pending) adds a guard that throws when the image count differs from numFrames. That turns the overflow into a clear error; it does not lift the limitation.

Suggested fix

  • Query the minimum for the present mode actually used (VkSurfacePresentModeKHR chained into vkGetPhysicalDeviceSurfaceCapabilities2KHR) and request max(numFrames, thatMinimum), clamped to maxImageCount.
  • Decouple "swapchain image count" from "frames in flight": size the per-image arrays from the count the driver returns, and keep numFrames for the per-frame resources only.
## Summary Swapchain creation assumes the driver returns exactly `Window::numFrames` (3) images. It never compares that against the surface capabilities, and it reads the images into a fixed 3-element array. Line numbers refer to `6aa0338`. ## Details - `implementations/Crafter.Graphics-Window.cpp:1061`: `swapchainCI.minImageCount = numFrames;`. The capabilities queried at `:1027` are used for extent and transform only; `minImageCount` / `maxImageCount` are ignored. - `:1102-1105`: the count returned by the first `vkGetSwapchainImagesKHR` call is passed straight to the second, which writes into `VkImage images[numFrames]` (`interfaces/Crafter.Graphics-Window.cppm:261`). Every other per-image array (command buffers, fences, image view infos, the per-frame heap copies) is sized `numFrames` as well. Two consequences: 1. **Out-of-bounds write** whenever the swapchain has more than 3 images. A driver may create more images than `minImageCount` asks for. 2. **Invalid create info** when the surface minimum is above 3: the spec requires `minImageCount` to be at least the surface's minimum (or the minimum for the chosen present mode, when that is queried and passed along). ## Concrete case Mesa Turnip on phoc (Fairphone 6, Mesa 26.2.2) reports: | | `minImageCount` | |---|---| | surface in general | 4 | | `FIFO` (per present mode, `VK_KHR_surface_maintenance1`) | 3 | | `MAILBOX` | 4 | Present modes offered: `MAILBOX`, `FIFO` (no `IMMEDIATE`). - Default `FIFO` path: 3 requested, Mesa creates 3, the UI renders. Verified on the device. The request is still below the general surface minimum; the phone has no validation layer, so I did not see whether it reports that. - `CRAFTER_PRESENT_IMMEDIATE` (`:1076-1087`) falls back to `MAILBOX` there, which needs 4 images. Not run; by the numbers above it overflows `images[]`. The descriptor-set fallback change (pending) adds a guard that throws when the image count differs from `numFrames`. That turns the overflow into a clear error; it does not lift the limitation. ## Suggested fix - Query the minimum for the present mode actually used (`VkSurfacePresentModeKHR` chained into `vkGetPhysicalDeviceSurfaceCapabilities2KHR`) and request `max(numFrames, thatMinimum)`, clamped to `maxImageCount`. - Decouple "swapchain image count" from "frames in flight": size the per-image arrays from the count the driver returns, and keep `numFrames` for the per-frame resources only.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Catcrafts/Crafter.Graphics#160
No description provided.