Skip to content

renderer: an off-main present begins a Core Animation transaction only for a frame it shows (sync line) - #80

Merged
lawrencecchen merged 2 commits into
release/cmux-sync-20261104from
fix/off-main-no-empty-commit-sync
Nov 5, 2026
Merged

lawrencecchen merged 2 commits into
release/cmux-sync-20261104from
fix/off-main-no-empty-commit-sync

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The same two commits as #79 (cherry-picked with -x), for the sync line release/cmux-sync-20261104 that cmux #35478 pins. #79 has the independent review receipt (SHIP at 7f2dcbf) and the red and green fork runs; the layer file is identical on both lines.

IOSurfaceLayer.setSurfaceOffMain began and committed a Core Animation transaction before its checks. So every frame that it did not show still committed an empty transaction on the GPU completion thread:

  • frames that go through main: a new size while the embedder presents host sizes, a frame the embedder has not committed, or another size than the shown one;
  • frames that a newer frame superseded.

cmux FRAME OWNER REFACTOR S4 measured this with Time Profiler (cmux step 4b41c42c, held keys-stress, 14 terminals, motion.frameOwner.terminals on): 405 ms of CA::Context::commit_transaction under the GPU completion queue in one round. Presents per round went from 84 (switch off) to about 850 (switch on). The new IOSurface per size cost only about +100 ms in the same round, and WindowServer's create_surface was the same with the switch off and on.

The checks now run first, read-only, under the Core Animation lock (offMainDecisionLocked). Only a frame that it shows begins a transaction. The transaction begins under the lock (a begin commits nothing), takes the contents, and commits after the unlock. So the invariant "no commit while holding the lock" still holds. Renderer mode (the default) gets the same change: a superseded frame no longer commits.

No C API change. No change to which frame is shown, or when.

Testing

  • Red first: 694f7ffb7 adds host present: an off-main present that does not show its frame commits no transaction. It counts the presenting thread's commits with the existing CommitOrder swizzle, for each refusal (no host size, another size than the host's, a layer that shows another size, a present the embedder has not committed, an older frame), and expects one commit, outside the lock, for a frame that it shows.
  • Fork CI, host present: filter only (scratch branches):
    • Red at 694f7ffb7, run 41953548703: 110/111 tests pass. Only the new test fails, at IOSurfaceLayer.zig:862, expected 0, found 1 (a layer that shows another size: the off-main attempt committed an empty transaction).
    • Green at 7f2dcbf36, run 41953551466: 111/111 tests pass, and lib-vt 97/97.
  • After merge: release build, cmux pin PR, then the 8-row gate A/B and a profile on cmux24-perf, switch off and on.

Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes IOSurfaceLayer.setSurfaceOffMain so an off-main present only begins a Core Animation transaction for a frame it actually shows. Previously every frame that went through main (new size, uncommitted embedder present, size mismatch) or was superseded still committed an empty transaction on the GPU completion thread; now the read-only checks run first under the Core Animation lock, and the transaction commits only after the unlock, preserving the no-commit-while-locked invariant. Adds a test asserting refusals commit nothing on the presenting thread while a shown frame commits exactly once, outside the lock.

Written for commit ec398e8. Summary will update on new commits.

View guided diff Turn on auto-fix

…its no Core Animation transaction (red)

cmux FRAME OWNER REFACTOR S4 profile 4b41c42c: with the embedder presenting host sizes, each frame of a new size
fails setSurfaceOffMain's checks and goes through main, but the off-main attempt first begins and commits a
transaction: one empty commit on the GPU completion thread per present (405 ms of CA::Context::commit_transaction
in a held-key round). The test counts the presenting thread's commits (the CommitOrder swizzle) for each refusal:
no host size, another size than the host's, a layer that shows another size, a present the embedder has not
committed, and an older frame. A frame it shows still commits once, outside the lock.

(cherry picked from commit 694f7ff)
…y for a frame it shows

setSurfaceOffMain began and committed a transaction before its checks, so each frame that went through main (a new
size while the embedder presents host sizes, a frame the embedder has not committed, another size than the shown
one) or that a newer frame superseded still committed an empty transaction on the GPU completion thread. cmux FRAME
OWNER REFACTOR S4 profile 4b41c42c: with motion.frameOwner.terminals on, 405 ms of CA::Context::commit_transaction in
one held-key round of 14 terminals, against main's own commit.

The checks now run first, read-only, under the Core Animation lock (offMainDecisionLocked). Only a frame it shows
begins a transaction, under the lock, takes the contents, and commits after the unlock: the no-commit-under-the-lock
invariant holds. Renderer mode (the default) gets the same: a superseded frame no longer commits.

(cherry picked from commit 7f2dcbf)
@coderabbitai

coderabbitai Bot commented Nov 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e10dcf85-844d-498e-9233-aadffa38245b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@lawrencecchen
lawrencecchen merged commit 706c696 into release/cmux-sync-20261104 Nov 5, 2026
9 checks passed
lawrencecchen added a commit that referenced this pull request Nov 5, 2026
Those PRs ported #75, #77 and #79 onto this branch while the sync was open; a
force push here briefly replaced them. Keep both lines so every pushed commit
stays reachable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant