ios: drive draws from CADisplayLink instead of a polling render thread#646
Open
benface wants to merge 1 commit into
Open
ios: drive draws from CADisplayLink instead of a polling render thread#646benface wants to merge 1 commit into
benface wants to merge 1 commit into
Conversation
5788583 to
eea80f9
Compare
The iOS launch path spawned a background thread that ran
`loop { try_recv messages, performSelectorOnMainThread(setNeedsDisplay),
yield_now }` with MTKView in manual-redraw mode
(`setPaused: YES` + `setEnableSetNeedsDisplay: YES`). `yield_now()`
isn't a sleep, so the thread pinned ~100 % of one CPU core
continuously — iOS thermal-throttling the GPU clock down on real
devices after ~a minute of use, and pinning `setPreferredFramesPerSecond`
nowhere near its hint because frames only happened when the thread
nagged the view.
Switch MTKView to continuous-draw (`setPaused: NO`,
`setEnableSetNeedsDisplay: NO`). `CADisplayLink` drives
`drawInMTKView:` on the main thread at the display rate. Channel
receivers move into the `IosDisplay` payload (main-thread-only) and
get drained at the start of each `drawInMTKView:`, with messages
dispatched inline via a new `dispatch_message` helper — no more
main → channel → render thread → main round-trip through
`performSelectorOnMainThread:processMessage:`. The
`processMessage:` selector and `MainThreadState::cur_msg` field
disappear with the thread.
Same-app comparison on iPhone 17 Pro: CPU 140 % → 50 %, FPS held
at 60, Energy Impact "Very High" → "High", phone no longer heats
up during play.
Also rolls in a Rust 2024 `static_mut_refs` lint fix on
`RUN_ARGS.take()` via `&raw mut`.
eea80f9 to
c6d90c8
Compare
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.
The iOS launch path spawned a background thread that ran a hot polling loop —
performSelectorOnMainThread:setNeedsDisplay+yield_now()— to drive frames intoMTKView(configured in manual-redraw mode withsetPaused: YES).yield_now()isn't a sleep, so the thread pinned ~100 % of one CPU core continuously. On a real iOS device that triggers thermal throttling, which clamps the GPU clock and drops effective FPS within a minute or two of use.Switch
MTKViewto continuous-draw mode (setPaused: NO,setEnableSetNeedsDisplay: NO) soCADisplayLinkdrivesdrawInMTKView:at the display rate. Channel receivers move into theIosDisplaypayload (main-thread access) and get drained at the start of eachdrawInMTKView:, with messages dispatched inline via a newdispatch_messagehelper — the main → channel → render thread → main round-trip throughperformSelectorOnMainThread:processMessage:is no longer needed since events arrive on the main thread to begin with.processMessage:andMainThreadState::cur_msgdisappear with the thread.Reproduction
Run any sample on a real iPhone for a minute or two; watch the frame rate drop as the device heats up. In Xcode's Activity Monitor, sustained ~100 % CPU usage shows up on a non-main thread with "Very High" Energy Impact, both of which disappear after the switch.
Also rolls in a Rust 2024
static_mut_refslint fix onRUN_ARGS.take()via&raw mut.