Repository navigation
Conversation
`commit_frame` asked the view for its `currentDrawable` every frame and presented it. `currentDrawable` waits in `-[CAMetalLayer nextDrawable]` until the display lets go of one of the layer's drawables, so every frame waited for the display, even one drawn only into render targets. A frame now takes a drawable only when it begins a pass on the screen, and `commit_frame` presents one only then. An app that draws its frames off-screen and reads them back, such as a tool saving them to files, can then draw them as fast as the GPU does. A frame with no pass at all commits nothing, where it panicked on the missing command buffer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
commit_frameasks the view for itscurrentDrawableevery frame and presents it.currentDrawablewaits in-[CAMetalLayer nextDrawable]until the display lets go of one of the layer's drawables, so every frame waits for the display, even a frame drawn only into render targets.A macroquad game (CHOMP) has a capture mode that plays a level and saves its frames as PNGs. It draws each frame into a render target and reads it back, and nothing it draws goes to the window. In a
sampleof a running capture on an M2 Max, 43% of the main thread was innextDrawable, and frames came at the display's refresh rate, 120 Hz.Change
begin_passnotes when a frame begins a pass on the screen, which is what takes one of the view's drawables.commit_frametakes and presents a drawable only for such a frame. A frame drawn only off-screen commits its command buffer without one.Nothing changes for a frame that draws to the screen.
On macOS, frames are also paced by the display link, and macroquad clears the screen at the start of every frame. So an app needs all three changes before its off-screen frames stop waiting for the display:
swap_interval: Some(0)on macOS;Conf::clear_screento turn that clear off.Measurements
These are the capture's times for 2,400 frames, saving 8 of them, on an M2 Max with a 120 Hz screen. Every frame came out byte-identical to the frames before the changes. Changes on their own made no difference:
swap_interval: Some(0), screen still clearedThrough OpenGL, which needs only
swap_interval: Some(0)and the B36E screen clear off, the same frames took 22.2 s before and 5.2 s after.Verification
cargo checkpasses foraarch64-apple-darwin,aarch64-apple-iosandx86_64-unknown-linux-gnu.🤖 Generated with Claude Code