fix(vulkan): clear startup validation errors on native triangle

Two Vulkan validation errors fired on startup of every native (Vulkan)
example, reported in #5:

1. vkCreateDevice enabledLayerCount != 0. Device layers are deprecated
   and ignored since Vulkan 1.0; passing them is a spec violation
   (VUID-VkDeviceCreateInfo-enabledLayerCount-12384). The device-layer
   enumeration/match block in Device::Initialize is removed and
   enabledLayerCount is pinned to 0 — layers are enabled at the instance
   only.

2. vkQueueSubmit layout transition on a presentable image that "has not
   been acquired". StartInit() and RecreateSwapchainAndImages() eagerly
   transitioned every swapchain image UNDEFINED -> PRESENT_SRC_KHR before
   any vkAcquireNextImageKHR, which the spec forbids (a presentable image
   may only be touched after acquire). Those pre-transitions are removed.
   Each image's first layout transition now happens lazily in Render(),
   after acquire, from UNDEFINED; subsequent frames transition from
   PRESENT_SRC_KHR. A per-image `imageInitialised` flag (reset in
   CreateSwapchain) selects the correct oldLayout.

Verified under sway (headless, GPU renderer) + VK_LAYER_KHRONOS_validation:
the original code reproduces both errors on HelloUI; the fixed build emits
zero validation messages across initial render and swapchain recreation.

Resolves #5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
catbot 2026-05-31 20:59:10 +00:00
commit cac433ee09
3 changed files with 41 additions and 94 deletions

View file

@ -239,6 +239,14 @@ export namespace Crafter {
VkColorSpaceKHR colorSpace;
VkImage images[numFrames];
VkImageViewCreateInfo imageViews[numFrames];
// Tracks whether each swapchain image has been rendered (and thus
// left in PRESENT_SRC_KHR) at least once. Freshly created swapchain
// images start in VK_IMAGE_LAYOUT_UNDEFINED, so the first per-frame
// barrier must transition from UNDEFINED, not PRESENT_SRC_KHR.
// Reset in CreateSwapchain(). A presentable image may only be touched
// after it has been acquired, so this initial transition happens
// lazily in Render() (post-acquire) rather than up front.
std::array<bool, numFrames> imageInitialised{};
std::thread thread;
VkCommandBuffer drawCmdBuffers[numFrames];
VkSubmitInfo submitInfo;