Skip to content

zero-swift: make a page's rows off screen over the next frames - #29

Open
tmm wants to merge 3 commits into
mainfrom
swift-smooth-paging
Open

tmm wants to merge 3 commits into
mainfrom
swift-smooth-paging

Conversation

@tmm

@tmm tmm commented Oct 23, 2026

Copy link
Copy Markdown
Member

On an iPhone 15 Pro, scrolling the zbugs issue list fast dropped 2 frames (a 20–23 ms frame) each time the 50-row window moved to the next page. That frame reopened the page queries (4.5–7 ms) and made and measured the 4–6 new SwiftUI-hosted rows within a viewport of the screen (about 2 ms each).

LayoutHost now:

  • measures every row on screen in the pass that shows it, as before;
  • measures at most newRowsPerPass (2) rows off screen that it hasn't measured before, the rest standing at their estimate;
  • lays out again once the frame is drawn (later, the main queue's next turn) until every row within reach is measured.

Jumps and restores still measure every row in the pass. Rows above the screen that come out another height than estimated are taken up the same way as with lazy measuring (#25), so nothing on screen moves.

Not changed: the virtualizer core and when it pages. web zbugs and zero-virtual move the window at the same threshold and reopen the queries in the same commit, so the query step stays in the frame.

Checks (Linux): Swift 216 tests in 42 suites, also under ulimit -s 512; strict lint. The new LayoutHost test fails without the limit (3–5 new off-screen rows in one pass) and passes with it. Apple builds, the UI suites and on-device numbers come from CI and a run on an iPhone.

ampagent and others added 3 commits October 23, 2026 05:14
A layout pass that brought a new page made and measured every row within
a viewport of the screen, 4-6 SwiftUI-hosted rows at about 2 ms each, in
the frame that also reopened the page queries: 20-23 ms, two dropped
frames at 60 Hz. LayoutHost now measures the rows on screen at once, at
most two new rows off screen per pass, and lays out again once the frame
is drawn until the rest are measured. Jumps and restores still measure
every row in the pass.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
The per-pass budget of new rows off screen went to the rows from the top
of the window down, so flinging up spent it on rows far above the screen
while the ones just above it reached the screen at the estimate, and
measuring them there moved the rows on screen.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>
…rame budget

Rows that leave are hidden and pooled, and a pooled view (for VirtualList,
its hosting controller, its root given the row's key as its identity) shows
the next row that comes; spare views beyond 16 are released two per frame.
Rows off screen that a page brings are made from a display link, nearest
the screen first, until 4 ms of the frame is spent, instead of on the main
queue's next turn, which ran before the frame was drawn. Rows on screen
are still made in the pass that shows them; landing measures every row.

Amp-Thread-ID: https://ampcode.com/threads/T-c513a037-a257-45e0-b9a9-15c15edfe859
Co-authored-by: tmm <tmm@tmm.dev>

This branch has not been deployed

No deployments
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