feat(window): multi-frame-in-flight frame pacing (#40) #81
No reviewers
Labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Catcrafts/Crafter.Graphics!81
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "claude/issue-40"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The renderer was effectively single-buffered despite allocating triple-buffered infrastructure (
numFrames=3command buffers, swapchain images, per-image descriptor heaps):Render()ended with an unconditionalvkQueueWaitIdleafter present — zero CPU/GPU overlap.(presentComplete, renderComplete)semaphore pair, baked into a sharedsubmitInfo, with zero fences, was shared for the Window's lifetime.This reworks the frame-pacing model as one coherent change so up to
numFramesframes overlap.What changed
The deciding constraint is that
drawCmdBuffers, the per-frame descriptor-heap slots, and the swapchain images are all keyed by the acquired image index —UIRenderer::WriteSwapchainDescriptorsbakes heap slotito writeimageViews[i], so the index that selects a command buffer / heap slot must equal the acquired image index. The sync objects follow from that:VkFence— signaled by the submit, waited + reset before that image's command buffer / heap slot is re-recorded. Created signaled so the first use of each image passes through. This (not a queue drain) bounds frames-in-flight and gives the overlap.VUID-vkQueueSubmit-pSignalSemaphores-00067.numFrames+1: in-flight depth is bounded by the per-image fences, sonumFrames+1distinct acquire semaphores guarantee the one being reused has no pending operation (VUID-vkAcquireNextImageKHR-semaphore-01779).vkQueueWaitIdle; kept it on the resize /OUT_OF_DATE/ teardown paths.Testing
crafter-build test— 4 passed (MouseScroll, PushConstantRewrite, BLASBuildOptions, and the new FrameLoopSync).New
tests/FrameLoopSyncdrives the real frame loop against the live Wayland compositor for 60 frames (much more than the in-flight slot count) and asserts: the CPU frame counter advanced once perRender(), the swapchain rotated across multiple images (3/3), and the validation layer reported zero errors. This is the load-bearing check — the old single-pair design only avoided being an active race because the wait-idle masked it.Also ran the VulkanTriangle example end-to-end (0 validation errors).
Screenshots
VulkanTriangle rendering under the new multi-frame-in-flight loop:
Resolves #40