Skip to content

Reduce iOS pointer latency by dispatching pending input before BeginFrame - #190918

Open
MTtankkeo wants to merge 3 commits into
flutter:masterfrom
MTtankkeo:ios-input-latency
Open

Reduce iOS pointer latency by dispatching pending input before BeginFrame#190918
MTtankkeo wants to merge 3 commits into
flutter:masterfrom
MTtankkeo:ios-input-latency

Conversation

@MTtankkeo

Copy link
Copy Markdown
Contributor

Description

Fixes #190917

On iOS, SmoothPointerDataDispatcher may hold the latest pointer packet until VSync to smooth irregular input delivery.

Previously, the pending packet was released from a secondary VSync callback, after the primary frame callback for that VSync had already been posted.

As a result, pointer data synchronized to VSync N could not affect the frame associated with VSync N. If processing that pointer data requested a frame, the request would instead wait for VSync N+1, adding a full display interval to the input-to-render path.

In other words, the pointer packet was synchronized to one VSync but could only be rendered by the following one.

Changes

This change adds a bounded pre-frame callback phase to VsyncWaiter.

The callback order becomes:

  1. A platform VSync fires.
  2. Pre-frame callbacks run on the UI task runner.
  3. The primary frame callback is consumed and runs.
  4. Secondary callbacks run.

SmoothPointerDataDispatcher now releases its pending pointer packet during the pre-frame phase instead of from a secondary callback.

The primary callback is intentionally left available until pre-frame work has completed. If processing the pointer packet requests a frame while the platform VSync has already fired but its primary callback has not yet been consumed, Animator registers that callback synchronously.

This allows the newly requested frame to participate in the current VSync rather than posting AwaitVSync behind the already queued primary-frame task and waiting for the following VSync.

The same-VSync registration window is deliberately narrow. It begins when the platform VSync fires and ends when the primary callback is consumed on the UI task runner. Pre-frame or secondary callbacks registered after the current callback snapshot are kept for the next VSync.

This preserves the existing smoothing behavior of SmoothPointerDataDispatcher. The change only ensures that pointer data released for a VSync can be consumed by the frame associated with that same VSync.

Before and after

Smooth pointer dispatch before and after

Before:

VSync N
  → BeginFrame N
  → release pending pointer
  → request frame
  → wait for VSync N+1

After:

VSync N
  → release pending pointer
  → request/attach frame callback
  → BeginFrame N

The pending pointer packet is still synchronized to VSync, but it no longer incurs an additional frame solely because it was dispatched after the primary callback.

Tests

Added coverage verifies that:

  • pre-frame, primary, and secondary callbacks execute in that order;
  • a primary callback requested by pre-frame work can join the same already-fired VSync without arming another VSync;
  • pre-frame work registered after the current VSync callback snapshot targets the next VSync;
  • a packet held by SmoothPointerDataDispatcher is dispatched before the primary callback and can be consumed by the frame associated with the same VSync.

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@github-actions github-actions Bot added the engine flutter/engine related. See also e: labels. label Aug 11, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a pre-frame vsync callback mechanism to the Flutter engine, allowing work such as input dispatching to execute immediately before the primary frame callback of a vsync interval. The review feedback identifies a thread-safety issue in Animator::RequestFrame where AwaitVSync could be called synchronously on a non-UI thread, and a potential race condition in VsyncWaiter under high load when using a boolean to track vsync fire progress.

Comment thread engine/src/flutter/shell/common/animator.cc Outdated
Comment thread engine/src/flutter/shell/common/vsync_waiter.h Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

engine flutter/engine related. See also e: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iOS SmoothPointerDataDispatcher adds an extra frame of pointer latency

1 participant