Repository navigation
renderer: an off-main present begins a Core Animation transaction only for a frame it shows - #79
Conversation
…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.
…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.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the
|
|
Review (independent subagent): SHIP at 7f2dcbf I read the diff of each commit myself and did not use the PR description. I did not build anything locally. The CI evidence comes from the fork runs. Line numbers refer to 1. Is the decision the same as before? Yes. The order and the return values match on every path.
2. Lock, begin and commit order.
3. Could the contents change land in an implicit transaction? No.
4. Does the test prove the fix?
5. Other findings. None that block. The sequence numbers in the test are correct:
Review (independent subagent): SHIP at 7f2dcbf |
…he sync The release line moved again while the sync was open; keep its head (pinned by feat-cmux-next b8c044032) an ancestor of main.
Summary
IOSurfaceLayer.setSurfaceOffMainbegan 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:cmux FRAME OWNER REFACTOR S4 measured this with Time Profiler (cmux step
4b41c42c, held keys-stress, 14 terminals,motion.frameOwner.terminalson): 405 ms ofCA::Context::commit_transactionunder 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'screate_surfacewas 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
694f7ffb7addshost present: an off-main present that does not show its frame commits no transaction. It counts the presenting thread's commits with the existingCommitOrderswizzle, 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.host present:filter only (scratch branches):694f7ffb7, run 41953548703: 110/111 tests pass. Only the new test fails, atIOSurfaceLayer.zig:862,expected 0, found 1(a layer that shows another size: the off-main attempt committed an empty transaction).7f2dcbf36, run 41953551466: 111/111 tests pass, and lib-vt 97/97.