The unpainted band during a fast fling is a 200 ms record() on the WebKit main thread, and no prepaint knob bridges it
scope: generic · severity: finding · confidence: proven · subsystem: graphics
The question — “I scroll fast and half the screen goes white, then the content slowly fills back in.” Which part of the pipeline is late?
The answer — none of it is late. The compositor delivers every frame on
time; the frames have nothing in them. One CoordinatedPlatformLayer::record
call of 186-200 ms blocks the WebKit main thread while the scrolling thread
keeps scrolling at 60 Hz, and the backing store has no tiles for the area that
has just come into view.
page record max longest blank band compositorWikipedia, 79000 px 44.5 ms 0.096 0 droppedYouTube watch page 198.0 ms 0.683 0 droppedThe client’s worst frame in the same arm is 204-222 ms, which is that record
plus the frame it delayed. record() walks the render tree for the dirty
bounding box, on the main thread, holding the layer lock.
Why no prepaint knob fixes it — the prepaint budget at the default cover multiplier of 2.0 is half a viewport ahead of the scroll. A fling travels more than that in 200 ms. So the question is not where the budget sits, it is how big it is:
| knob | what it does | result |
|---|---|---|
WEBKIT_SKIA_RECORD_SPLIT_RATIO 2, 1.2 |
record dirty tiles separately when the bounding box is mostly clean | null, per 1000 px scrolled: 113 / 148 / 134 ms |
WEBKIT_LAYERS_COVER_LEAD 0.85 |
the same budget, ahead of the scroll instead of around it | null, 20.7 janks either way at n=3 |
WEBKIT_LAYERS_COVER_MULTIPLIER 4 |
triple the budget | works – and costs 740 -> 1415 MB of tile memory against a scope capped at 1500M |
The one that works is the one that buys more tiles, and it cannot be afforded.
What this rules out — that the blank areas are dropped frames (zero, every
arm); that the compositor is the problem (59.6-63.8 fps throughout); that the
layout storm is still happening (4 calls, 4 ms per Wikipedia drag, against 289
calls and 1629 ms before the r61 patches); that the browser chrome resizing the
web view is behind the whole-viewport blanks (--kiosk-mode removes the chrome
and still produces them); and both prepaint knobs above.
How it was established — tools/repro/scroll-blank/arm.sh: the longest
contiguous flat BAND in the frame (not the total flat fraction, which on a text
page is mostly paragraph gaps), the client’s own commit record, and uprobes on
the render phases, in one arm that refuses to report unless the page moved.
Every arm reads back the WEBKIT_ variables from /proc/<web process>/environ,
because the-webkit-session-env-was-lost-with-the-home-directory is what
this campaign started as.
What would overturn it — a build where record is off the main thread, or
a page where the band appears with record under ~50 ms. Both would say the
mechanism above is not the whole story.
Where it goes next — upstream. Recording a display list for a scrolled layer is main-thread work in every port; the GTK port has no incremental path for it. The cheap local mitigations are exhausted.
