Skip to content

Read Android touch input between frames when drawing falls behind vsync - #931

Merged
Tryanks merged 1 commit into
Tryanks:mainfrom
guivieiras:topic/android-frame-backlog
Oct 22, 2026
Merged

Tryanks merged 1 commit into
Tryanks:mainfrom
guivieiras:topic/android-frame-backlog

Conversation

@guivieiras

Copy link
Copy Markdown
Contributor

On Android, a short flick in a long chat could fling thousands of pixels, and a swipe made during a running fling would stop it and seem to stick.

Since #897, Choreographer queues a Frame host event every vsync, and process_host_events drew one frame per queued event. When a frame takes longer than a vsync (about 11 ms against 8.3 ms at 120 Hz on a Galaxy S20 FE), the backlog grows with each drain. The loop then draws frame after frame without reading input. Touch moves waited up to 100 ms and reached GPUI microseconds apart. GPUI timestamps velocity samples when they arrive, so it read those bursts as near-cap release speeds.

A drain now draws at most once, so input is polled between frames, as the loop did before #897. Frame pacing is unchanged.

No test: the loop runs only on a device, and no headless entry point drives it.

Checks run:

  • cargo fmt --all --check
  • cargo check -p tcode-android --target aarch64-linux-android --locked with RUSTFLAGS='-D warnings'
  • clippy for gpui-android on aarch64-linux-android with -D warnings
  • On the S20 FE, with temporary logging of each motion event's timestamp and the time it reached GPUI: moves now reach GPUI within 18 ms, where they took up to 100 ms before. Flicks and swipes during a fling feel right. That device build also forwarded only the latest move of each input batch, a separate local change that is not part of this PR.

Not run: the workspace clippy and test suite. Nothing they compile changed.

  • This change alters the wire protocol, so the next release needs a
    PROTOCOL_VERSION bump: a note was added under "Unreleased" above the
    constant in crates/protocol/src/lib.rs (the number itself changes only
    when the release is cut — CONTRIBUTING.md, principle 9).

🤖 Generated with Claude Code

Choreographer queues a Frame host event every vsync, and the drain drew one
frame per queued event. Whenever drawing outlasted a vsync (about 11 ms
against 8.3 ms at 120 Hz), the backlog grew with every drain, and the loop
drew frame after frame without reading input. On a Galaxy S20 FE, touch
moves waited up to 100 ms and then reached GPUI microseconds apart. GPUI
times its velocity samples on arrival, so a short drag flung far, and a
swipe made during a fling stopped it while its own moves arrived late.

A drain now draws once, as the loop did before frames followed Choreographer.
Measured on the same device, touch moves reach GPUI within 18 ms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@guivieiras
guivieiras marked this pull request as ready for review October 22, 2026 02:41
@Tryanks

Tryanks commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Let's hold this until two upstream gpui-base fixes land: longbridge/gpui-kit#3631 and longbridge/gpui-kit#3632.

The sticking after a swipe during a running fling matches a bug in gpui-base's Overscroll that affects every platform. When a drag is released while still moving, it throws the edge even in the middle of the list. A gesture that starts within the next half second or so then grabs that phantom stretch instead of scrolling (#3631). A second path has the same effect: f32 rounding at large scroll offsets leaves a sub-pixel stretch behind (#3632).

The frame backlog this PR fixes inflates release speeds. Touch has no stretch limit, so on Android that makes the phantom throw bigger: this PR shrinks the symptom but can't remove it. Once both fixes are in, we can see what this change fixes on its own. The short flick that flings thousands of pixels should still come from the backlog.

@Tryanks
Tryanks merged commit 7b00201 into Tryanks:main Oct 22, 2026
7 checks passed
@Tryanks

Tryanks commented Oct 22, 2026

Copy link
Copy Markdown
Owner

Update to my earlier comment: we're no longer waiting on upstream. #933 works around both gpui-base behaviours inside Tcode: page viewports let their content scroll itself, so ScrollBounce stretches only by what is left over at an end. With that merged, this PR went in on its own merits: it fixes the Android frame backlog.

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.

2 participants