fix(apple/stage2): disable layer vsync wait to kill fullscreen stutter
apple / swift (push) Failing after 28s
ci / web (push) Failing after 47s
ci / rust (push) Failing after 1m19s
ci / docs-site (push) Failing after 33s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Failing after 12s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
docker / deploy-docs (push) Has been skipped
rpm / build-publish (push) Failing after 13s
deb / build-publish (push) Failing after 44s
apple / swift (push) Failing after 28s
ci / web (push) Failing after 47s
ci / rust (push) Failing after 1m19s
ci / docs-site (push) Failing after 33s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 4s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Failing after 12s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 5s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 5s
docker / deploy-docs (push) Has been skipped
rpm / build-publish (push) Failing after 13s
deb / build-publish (push) Failing after 44s
The experimental stage-2 presenter (CAMetalLayer + display link) stuttered badly in fullscreen but ran fine windowed. render() runs on the display-link / MAIN thread and calls layer.nextDrawable(), which blocks that thread until a drawable frees. With the layer's own displaySyncEnabled left on (default), present also waits for the hardware vsync, so the block serializes the main thread to the display — windowed, the WindowServer's looser compositing hides it; fullscreen's tighter, more-direct path exposes it as judder. (Apple dev-forum guidance: displaySync off measurably reduces nextDrawable() blocking.) - displaySyncEnabled = false (macOS-only): the display link is already the per- vsync pacing source, so the layer's redundant vsync wait only adds the stall. - maximumDrawableCount = 3 (explicit): more in-flight headroom before nextDrawable() has to block on the main thread. Swift-only (no core/ABI change → no xcframework rebuild). Validated: swift build; swift test (39 passed, 0 failures). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -80,6 +80,19 @@ public final class MetalVideoPresenter {
|
||||
layer.pixelFormat = .bgra8Unorm
|
||||
layer.framebufferOnly = true
|
||||
layer.isOpaque = true
|
||||
// Triple-buffer: more in-flight drawables before `nextDrawable()` (called on the
|
||||
// display-link / MAIN thread) has to block waiting for one to free.
|
||||
layer.maximumDrawableCount = 3
|
||||
#if os(macOS)
|
||||
// The display link already paces exactly one present per vsync. Leaving the layer's
|
||||
// own vsync wait on means `commandBuffer.present` ALSO blocks for the hardware vsync,
|
||||
// so `nextDrawable()` stalls the MAIN thread until a drawable frees — windowed, the
|
||||
// WindowServer's looser compositing hides it; FULLSCREEN's tighter, more-direct path
|
||||
// serializes the main thread to the display and the stall surfaces as bad judder.
|
||||
// Disabling the layer-level sync lets present return promptly (the display link is the
|
||||
// pacing source), which is what fixes the fullscreen stutter. macOS-only property.
|
||||
layer.displaySyncEnabled = false
|
||||
#endif
|
||||
self.layer = layer
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user