Skip to content

gpui: count primitives into draw order instead of comparing them - #50

Merged
sunli829 merged 2 commits into
mainfrom
scene-counting-sort
Oct 9, 2026
Merged

sunli829 merged 2 commits into
mainfrom
scene-counting-sort

Conversation

@sunli829

@sunli829 sunli829 commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

What

Scene::finish puts every kind of primitive in drawing order. It sorted an
index vector with sort_unstable_by_key, reading each primitive's key out of
the 112–168-byte primitive on every comparison, then gathered.

Draw orders are few and small (one past the greatest among the primitives a
primitive overlaps, or its layer's): a frame of workspace-quotes has 3324
glyphs over 21 orders. So, in fast/scene.rs only:

  • Primitives other than sprites are counted into their order's run, one
    pass to count and one to place, and left alone when already in order.
    Orders spread more than four times wider than the primitives are numerous
    are sorted as packed u64 keys instead, so the counts stay short.
  • Sprites sort one packed u128 of order, texture, tile and index, and
    are left alone when already in order.

Both give exactly the order the old sort did, so batches and drawing are
unchanged. No upstream file changes.

Correctness

  • cargo test -p gpui --features test-support --lib: 646 passed. New tests
    compare both sorts with a reference stable sort over random scenes (empty,
    one item, in order, counted, spread too wide), check which way each case
    was sorted and the threshold at its edge, and that the packed sprite key
    keeps every bit of order, texture and tile.
  • gpui_perf --headless --verify: all 55 checks identical.
  • script/check-upstream: 0 violations.

Performance

The sort on real keys dumped from frames (single core, hot cache):

Keys Before After
Real window, glyphs (1871) 37.8 µs ~20 µs
Real window, quads (491) 9.1 µs 3.9 µs

Headless, main-thread instructions per frame, retained views on, all 42
scenarios: every one lower, median −2.3%.

Scenario Change
list-filter −17%
settings-toast / form-hover −11% / −10%
list-variable-scroll / list-uniform-scroll −9%
chat-scroll −4.6%
workspace-* −1.9% to −3.0%
layout-*, table-ticks-many −0.1% to −0.6%

Real window (--auto --only WorkspaceQuotes, 3 alternating runs of 600
frames): instructions p50 16.5M → 16.1M with retention on, 22.7M → 22.3M
off. Wall-clock CPU time on this machine is noisier than the difference.

🤖 Generated with Claude Code

A finished scene sorted each kind of primitive by sorting indices with a
key read from the primitive on every comparison. Draw orders are few and
small, so primitives other than sprites are now counted into their
order's run (falling back to sorting packed keys when orders spread more
than four times wider than the primitives are numerous), and left alone
when already in order. Sprites sort one packed u128 of order, texture,
tile and index. Both give exactly the order the old sort did.
@sunli829
sunli829 merged commit 883560c into main Oct 9, 2026
4 checks passed
@sunli829
sunli829 deleted the scene-counting-sort branch October 9, 2026 08:47
trancong12102 added a commit to aislopware/gpui-fast that referenced this pull request Oct 9, 2026
Takes longbridge#50 (count primitives into draw order instead of comparing them)
as history only: crates/gpui/src/fast/scene.rs keeps the fork's sort.
The fork's 4105616 already packs every kind's keys into u64s and sorts
them by an 11-bit LSD radix sort (a single counting pass while orderings
stay under 2048, as longbridge#50 counts them), sprites included where longbridge#50 sorts
u128 keys by comparison, skips kinds already in order, and reuses last
frame's order when the keys repeat; fast::scene::primitive depends on the
gathered flags it keeps. longbridge#50's tests cover its own functions and go with
them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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