render: glyph populate walk and scroll still cost the whole grid per frame #422
Labels
No labels
priority/P0
priority/P1
priority/P2
release/v0.1.0
status/blocked
status/planned
type/bug
type/design
type/test-gap
type/tracker
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
natsukium/felis#422
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Split from #403.
#403's change (branch
render/incremental-instances) rebuilds and uploads the cell instances of only the rows the client's shadow marked. Two whole-grid costs are left on the per-frame path:Renderer::populate_glyphs(GlyphIndex::populate, orpopulate_grid_with_featureswhen features or clusters are live) walks and, with features, shapes the whole grid each frame to prime the atlas and build theShapeFrame. In the #403 profile (188x93, one key per 50 ms) it was about 6% of the main thread. Walking only the marked rows needs theShapeFrameto keep the unmarked rows' entries across frames.Done when both are measured before and after on a keystroke and on a scroll flood, and each either lands or is shown not to pay.
The populate walk half is #431. The scroll half (rotating per-row offsets so a scroll rebuilds only the rows it brings in) does not pay today, so I did not build it. It can only act on a
Scrolleddirective the client receives, and in every workload I measured the client got close to none. The rows arrive asRowDeltas, and each one marks its row, so no row is left to rotate.Linux, NVIDIA, release, 212x93, origin/main (
43ac9b14) instrumented with the frame cost, the count of shadow rows marked per frame, and a log line perScrolledthe shadow applies:Scrolledreceivedseq -wflood, 6 runsprintf "\n..."), 3 runsless,jevery 30 msvim -u NONE,^Eevery 30 ms, 2 runsThe daemon has three reasons for this:
scroll_region_upthen finds the region dirty and ships the whole region as rows. That is the documented choice inediting.rs, which says a directive on top would double the per-frame cost.lessrewrites its prompt on the bottom row before it scrolls, so its region is dirty for the same reason.ESC[1;28r ESC[H ESC[M ESC[r, captured from a pty). DL is not routed throughscroll_region_up, so it never produces a directive.Rotation on the client would pay only after the daemon changes. DL/IL at the top margin would have to ship as
Scrolled, and a mixed tick would have to ship a directive plus the rows it touched. The second change reopens the doubled-cost trade-off inediting.rs. If either lands, the client design from the issue still applies: per-slot y offsets in a uniform, the shadow rotating its damage bits instead of marking the region, and the row cache andShapeFramerotating slots. The uniform is needed because storage buffers are not guaranteed in vertex shaders on downlevel adapters. The measurement above shows what it would save on those frames: roughly the whole 430-2300 us build+upload, against one or two rebuilt rows.