Skip to content

Adapt mobile rendering to GPUI frame demand - #897

Merged
Tryanks merged 1 commit into
mainfrom
feat/adaptive-refresh
Oct 16, 2026
Merged

Tryanks merged 1 commit into
mainfrom
feat/adaptive-refresh

Conversation

@Tryanks

@Tryanks Tryanks commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Summary

Mobile rendering now follows GPUI demand instead of continuously ticking on iOS or drawing on arbitrary Android looper wakes.

invalidation / input → wake native VSync at the display maximum
animation next-frame demand → keep ticking, then short activity grace
isolated refresh / caret timer → one frame, then pause
idle / background → pause; Android clears its surface frame-rate vote

The shared Rust FramePacer owns demand coalescing, touch activity and grace. iOS enables ProMotion, reads the attached screen, recreates its display link on screen/scene changes, reports refresh timing through FFI, and connects frame_waker / schedule_frame. Android uses coalesced Choreographer callbacks, reports compatible maximum refresh, suspends on activity pause, and uses API-gated Surface.setFrameRate hints. System power and thermal policy can clamp both.

No gpui-fast patch or desktop change. In locked upstream 0.1.8, Wayland parks its idle frame loop; macOS retains a visible-window display-link subscription, Windows invalidates windows from its VSync thread, and X11 keeps a visible-window refresh timer. GPUI skips unchanged rendering on those periodic callbacks, but they are not all fully on-demand frame sources.

Evidence

  • Before: iOS requested GPUI presentation on every display-link tick, lacked the iPhone ProMotion plist key and returned no refresh interval; Android rendered scheduled demand on looper wakes rather than Choreographer.
    After: invalidation explicitly wakes the native frame source; isolated refreshes pause again, continuing animations/input retain max-rate scheduling, and both backends expose display refresh timing.
  • Four new production-policy tests cover isolated/caret refreshes, animation and grace expiry, input/touch wake, and suspension preserving pending demand. No test-only seams; no tests removed or assertions loosened. The shared policy has 52 production lines and 63 test lines.
  • Passed: mbx fmt (workspace and excluded Android backend), clippy workspace/all-targets with -D warnings, workspace nextest --test-threads 4 (1,245 passed, 13 existing skips), iOS device Release Rust build and warnings-denied device check, warnings-denied Android check with NDK 27.1.12297006/API 26, warnings-denied Web check, machete, Android Release Java compilation, and git diff --check.
  • Existing build notes: dependency block future-compatibility notice; macOS test linking reports a large unwind table; Gradle reports existing deprecations. No check failed on the final implementation.
  • Maintainer requested manual native verification: no final automated FPS measurements, simulator runs, UI driving or temporary instrumentation. 120 Hz scrolling, first-frame latency, idle power, caret and streaming behavior need the maintainer's hands-on check. Installed exact pushed PR head 121195a6db7113a4d195994f3e1bea741530817a on iPhone Air 00008150-0000459402D2401C: Rust Release + Xcode Release, unchanged com.tryanks.tcode, team 9865JAS8C7. devicectl reported successful installation; the app was not automatically launched or driven. The final executable contains no temporary adaptive-refresh instrumentation markers.

Merge Danger

Door: two-way

Blast Radius: mobile

Frame-source lifecycle and wake delivery change on iOS and Android. Rust policy and cross-target compilation pass; native smoothness and power behavior remain for the maintainer to try by hand. No schema, protocol or bundle-id changes.

@Tryanks
Tryanks merged commit 0e98e63 into main Oct 16, 2026
7 checks passed
@Tryanks
Tryanks deleted the feat/adaptive-refresh branch October 16, 2026 03:54
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