I have a SwiftUI clock-radio app on iPhone (ProMotion display). The face is a rich, layered screen (glass panels, gradients, shadows). One element is a 340×46 pt text ticker strip that scrolls continuously. Its GPU behaviour is reproducible and, to me, surprising:
Measurements (Xcode GPU gauge used only for within-session relative contrasts; cadence from an in-app frame-pacing log):
With the ticker scrolling, GPU sits at a ~50% plateau. The step is binary: one scroller or three scrollers cost the same.
Frames are never dropped: 16.7 ms median frame time, ~3% of frames over 20 ms. The GPU bills heavily for frames it renders on time.
The same strip scrolling on an otherwise blank screen costs ~0%.
The ticker visible but static costs 0%. Motion is the trigger; the cost scales with what surrounds the strip, not with the strip itself.
What I have tried:
SwiftUI offset animation of the text — baseline behaviour above.
A fully severed Canvas leaf: own layer subtree, own CADisplayLink, snapshot-fed, no view-tree publishing or subscriptions — identical cost on the full face (and literally 0% redrawing alone on a blank screen at full frame rate).
Pre-rendered bitmap content scrolled instead of text — identical.
The strip hosted in a separate UIWindow above the face — identical, measured with the strip visibly scrolling in its seat.
Pinning the strip's CADisplayLink.preferredFrameRateRange to exactly 30 Hz (minimum = maximum = preferred = 30) — no change.
.compositingGroup() on the panel subtree — a modest reduction only.
What works: removing the motion duty (scroll once, then park — cost collapses), and, elsewhere in the app, a pull-model spectrum visualiser replaced a publish-driven one and its cost vanished. Severing worked for the visualiser but not for the ticker.
Questions:
Is this step the expected consequence of the WWDC22 session 10083 rule (the display refresh rate is set by the highest-frame-rate animation in the app) composed with whole-layer-tree compositing per presented frame? Is there any dirty-region scoping that a layer structure like ours defeats?
The 30 Hz pin above was hard (min = max = preferred). Would a CAFrameRateRange in the 15–24 Hz band that Apple's ProMotion guidance suggests for small, slow animations behave materially differently, or is that distinction already settled by the 30 Hz result?
Is there a known construction for a continuously scrolling ticker that lets the GPU idle between updates?
Happy to share a minimal repro or the pacing logs if useful.
2
0
460