Skip to content

text: Publish selection snapshots once per frame - #3686

Merged
huacnlee merged 2 commits into
longbridge:mainfrom
lurenjia534:fix/3626-text-selection-frame-publication
Oct 26, 2026
Merged

huacnlee merged 2 commits into
longbridge:mainfrom
lurenjia534:fix/3626-text-selection-frame-publication

Conversation

@lurenjia534

Copy link
Copy Markdown
Contributor

Fixes #3626

Description

Dragging a text selection on GPUI Fast updates the shared window-selection entity as each selectable text registers. Each registration republishes snapshots for all participants, producing quadratic frame work and invalidating unrelated retained views.

Record registrations through a read of the window entity, resolve the registering participant's own snapshot immediately, and publish changed snapshots once at the end of the frame. Keep automatic document order across partial redraws, recovering it with one full refresh when topology changes require it. Retire departed endpoints without resurrecting their selection when retained entities remount. Defer touch-edge and held-cursor updates until frame completion.

The code diff contains four Base files: 683 insertions and 235 deletions, including four retained-view regressions. Existing imports and public signatures remain available.

Following the previous contribution (#3673), Markdown reports, raw logs, fixtures, screenshots and reproduction instructions are preserved at an immutable commit on a separate fork evidence branch. Evidence files are not included in this code PR.

Before and after

The same retained-view regression fails before the fix: four pointer moves increase an unrelated view's render count from 4 to 8. It passes after the fix without additional unrelated renders.

Measured with the same real TextView fixture, GPUI Fast 0.1.10 and pointer path:

TextViews Median CPU time before Median CPU time after Median unrelated view renders before / after
16 2.19 ms 0.50 ms 28 / 0
64 11.83 ms 1.16 ms 124 / 0
164 51.94 ms 2.58 ms 324 / 0

Linux x86_64 / Rust 1.98.0, repository dev profile including its GPUI optimization overrides. Each version/size has three trials, each with 10 warm-up and 60 recorded moves, for 180 samples. Timing covers input dispatch, window.draw and deferred processing until parked. One pointer step can produce multiple draws. Screenshot readback, GPU completion and display presentation are excluded; these are not FPS or the macOS frame timings from the issue.

CPU time and unrelated render comparison

Screenshot

Same 64-TextView fixture and selection endpoint, captured using GPUI's real GPU offscreen renderer. The files are byte-identical: the selection appearance is preserved while the render counts and CPU work change.

Before After
Before After

Separate actual desktop-window screenshots and acceptance records cover forward/reverse cross-text selection, system clipboard copying, selection handles and edit-menu Copy, native Wayland scrolling, and endpoint removal/remount. Native mouse/keyboard input used the authorized KDE RemoteDesktop EIS interface; screenshots were captured with Spectacle.

Touch-selection UI was exercised through the demo's Long press button and pointer handle dragging. Physical touchscreen input, macOS and Windows were not verified. Full details, including unsuccessful input attempts, are recorded in the evidence.

Public API

gpui-base

Existing signatures with changed internal publication/lifecycle behavior:

impl GlobalState {
    pub fn begin_selection_frame(&self)
}

The existing hidden method remains callable as a compatibility no-op; automatic TextView order is now maintained by window-local selection state.

impl TextSelectionHandle {
    pub fn register(
        &self,
        registration: TextSelectionRegistration,
        window: &mut Window,
        cx: &mut App,
    )
}

Registers participant geometry and resolves its own snapshot immediately; shared frame publication and paint-driven selection notifications are deferred until frame completion.

Breaking Changes

No public signatures or imports are removed. Existing-method behavior changes are recorded here as required by CONTRIBUTING:

- GlobalState::global(cx).begin_selection_frame(); // resets automatic paint order
+ // Automatic order is maintained by window-local selection frame publication.
+ // Existing begin_selection_frame() calls still compile and are harmless.
- handle.register(registration, window, cx); // active registration republishes all participants
+ handle.register(registration, window, cx); // own snapshot is immediate; shared publication follows the frame

Consumers must not depend on registration publishing other participants or delivering paint-driven selection events before frame completion.

How to Test

Validated against baseline 7b14e03f2ed4b46fc4149a8a523c7f91bcad6560 plus code commit 9b42fb299a50610d83c5fb484cf4db390871ac28:

cargo test -p gpui-base --features gpui-fast,test-support --lib text_selection --locked
cargo test -p gpui-base --features test-support --lib text_selection --locked
cargo test -p gpui-kit --features gpui-fast,test-support --test integration selection --locked
cargo clippy -p gpui-base --features gpui-fast,test-support --lib --locked -- --deny warnings
cargo fmt --all --check
git diff --check

The Fast selection filter reports 56 passed, the regular GPUI filter 53 passed, and Kit's selection integration filter 78 passed. Base Fast library Clippy, formatting and the code diff check passed.

The new regressions cover unrelated retained views, linear snapshot visits, selected-text scrolling without order rebuilds, and partial-frame order through insertion, reordering, endpoint removal and remounting.

The complete Base Fast suite was also compared:

cargo test -p gpui-base --features gpui-fast,test-support --lib --locked
  • Before: 1,454 passed, 35 failed, including the transplanted retained-view regression.
  • After: 1,469 passed, 23 failed.
  • All 23 after-fix failure names also occur before the fix; no new failing names were observed. Full-suite success is not claimed. Raw logs and complete failure comparison.

Both native text_selection and touch_selection examples were built with GPUI Fast and exercised on Linux. Forward/reverse clipboard contents match exactly. Native Wayland scrolling preserves the selected text and moves the handles with it. Removing a selected endpoint changes has_selection to false; remounting the retained endpoint keeps it false. The full Story Gallery was not run.

The evidence branch includes reproduce.py for rebuilding the expected baseline failure/fixed success and optionally the GPU fixture. The script was syntax-checked during publication; its complete rebuild was not rerun. The archived executions are the measured evidence.

AI Assistance

AI assisted with patch analysis, line-by-line review, regression validation, CPU/render-count measurements, native-window acceptance, bilingual Markdown evidence, and commit/PR preparation. The production fix and added regressions originated in the supplied patch. Human maintainer review remains required.

Checklist

  • Read CONTRIBUTING and followed the repository's coding and design guidance.
  • Reviewed the focused implementation and AI-assisted validation evidence.
  • Targeted regressions, integration checks, formatting and Base library Clippy passed.
  • Built and exercised the affected native selection examples on Linux.
  • Complete Base suite passes; 23 failures also occur on the baseline.
  • Physical touchscreen, macOS and Windows runtime verification.

lurenjia534 and others added 2 commits October 25, 2026 17:54
Use rendered-element identity instead of bounds changes to detect reparenting. Keep new-participant and paint-order inversion recovery, and cover ancestor scrolling with and without a selection beside an unrelated retained TextView.

AI-assisted implementation and regression validation.
@huacnlee
huacnlee enabled auto-merge (squash) October 26, 2026 03:04
@huacnlee
huacnlee merged commit 278e78d into longbridge:main Oct 26, 2026
19 checks passed
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.

text_selection: a moving selection draws every selectable text again on GPUI Fast

2 participants