HANDOFF §8.10: pop-in root causes (paint-cap drop, scan storm, black window) + test hygiene
This commit is contained in:
parent
b9317e7d23
commit
99d155e859
72
HANDOFF.md
72
HANDOFF.md
|
|
@ -862,3 +862,75 @@ un-granted: settings card → open Settings → recheck on Resume); test the
|
||||||
passgo-style flow on a fresh install. (M4 viewer is DONE, 2026-10-29 —
|
passgo-style flow on a fresh install. (M4 viewer is DONE, 2026-10-29 —
|
||||||
see the M4 entry in §9 for the verification notes + emulator pinch
|
see the M4 entry in §9 for the verification notes + emulator pinch
|
||||||
injection recipe.)
|
injection recipe.)
|
||||||
|
|
||||||
|
### 8.10 Thumbnail pop-in — root causes & fix (2026-10-30, CLOSED, `b9317e7`)
|
||||||
|
|
||||||
|
User report: on the warm 8580-file DB the thumbnails "snap in" slowly
|
||||||
|
after a flick; also requested a test of the COLD launch (fresh scan,
|
||||||
|
streamed list). Both turned out to be drain-tick / scan-pipeline bugs,
|
||||||
|
not render bugs.
|
||||||
|
|
||||||
|
**Bug A — paint-cap drop (warm + cold).** The drain tick paints at most
|
||||||
|
`MAX_PAINT_CELLS_PER_TICK` (2) cells/tick; finished decodes beyond the
|
||||||
|
cap were **silently dropped** (removed from `inflight`, never added to
|
||||||
|
`painted`). FILL then re-submitted them → the same cell re-decoded
|
||||||
|
3–6× per viewport; a new viewport's ~21 cells took 500–850 ms to fill
|
||||||
|
after a flick. The flick bursts themselves were clean 120 Hz — all the
|
||||||
|
"late frames" were in the settle window.
|
||||||
|
Fix: excess results hold in `app.ready` (bounded `READY_CAP=48`,
|
||||||
|
`app.ready_paths` keeps FILL from re-submitting) and paint on a later
|
||||||
|
tick at zero re-decode cost. Cap is adaptive: 2 during grid motion
|
||||||
|
(frame budget), 4 when settled.
|
||||||
|
|
||||||
|
**Bug B — cold-start scan storm.** Every 512-entry scan batch re-sorted
|
||||||
|
the WHOLE accumulated list and `set_list` did a full model reset
|
||||||
|
(`set_vec`) + generation bump + state clears. Sorted prefixes are NOT
|
||||||
|
stable as the set grows (new entries interleave by mtime), so each
|
||||||
|
batch orphaned every queued/in-flight decode (arrived marked stale →
|
||||||
|
dropped → re-submitted): a 17× re-decode storm (max observed dups per
|
||||||
|
file = 18 = batch count) plus full row churn per batch.
|
||||||
|
Fix: batches now stream in APPEND order (each 512-group mtime-sorted,
|
||||||
|
groups concatenated → stable prefix); `set_list` detects appends
|
||||||
|
(element-wise prefix match) and extends the model in place
|
||||||
|
(`extend_from_slice` — existing rows/textures untouched) with NO
|
||||||
|
reset/generation-bump. The final global sort (DB order) is ONE reset at
|
||||||
|
scan end; the viewport re-fills from the disk cache (~2 ms/cell).
|
||||||
|
Measured on phone (cold, fresh DB): scan 7637 ms → ~4.3–4.9 s, decode
|
||||||
|
dups 18 → 2–3, grid renders with thumbnails while streaming.
|
||||||
|
|
||||||
|
**Bug C — black window after pause (found while testing A/B).** The OS
|
||||||
|
can invalidate our surface (system dialog activity, app switch, screen
|
||||||
|
lock) with NO window event and no pending model change; nothing ever
|
||||||
|
sets `pending_redraw` again → the window comes back black and stays
|
||||||
|
black until the next content mutation.
|
||||||
|
Fix: (1) `request_redraw()` unconditionally in `RenderingSetup`;
|
||||||
|
(2) drain-tick **window-liveness watchdog**: window visible but
|
||||||
|
`LAST_PRESENT_US` (updated in `AfterRendering`) stale >2.5 s → one
|
||||||
|
forced redraw. `now_us`/`NOW_T0` are no longer `perf`-gated (the
|
||||||
|
watchdog needs them in all builds).
|
||||||
|
|
||||||
|
**Also: `debuggable = false`** (was `true` for simpleperf during the
|
||||||
|
jank hunt). A debuggable app shows the system **16 KB compatibility**
|
||||||
|
dialog on every cold launch; that dialog is its own activity, it
|
||||||
|
paused us, and its inconsistent lifecycle (TerminateWindow or not,
|
||||||
|
depending on timing) was the main black-window trigger in testing.
|
||||||
|
NOTE: the .so is 4 KB-aligned (fine on this 4 KB-page kernel); on a
|
||||||
|
16 KB-page kernel a non-debuggable build would be refused — revisit if
|
||||||
|
targeting such devices.
|
||||||
|
|
||||||
|
**Phone verification (cold, 8580 files, fresh DB, exclusive phone):**
|
||||||
|
clean launch (449 ms to window, no dialog), grid streams in with
|
||||||
|
thumbnails, two fast flicks render 120 Hz (median 8.3–8.5 ms, 0–2 late
|
||||||
|
frames over ~220 frames), viewport fully painted after the flicks,
|
||||||
|
grid intact after an app switch (Termux 4 s) — no black window.
|
||||||
|
|
||||||
|
**Test hygiene learned the hard way:** the phone is a shared personal
|
||||||
|
device — Fossify apps / Termux / the lock screen / the notification
|
||||||
|
shade kept stealing foreground mid-test, which masquerades as "app
|
||||||
|
not processing input / black screen" (the input lands on the other
|
||||||
|
app). Always: verify `ResumedActivity` before AND after each
|
||||||
|
measurement, keep swipes mid-screen (y 700–2100; bottom-edge = lock,
|
||||||
|
top-edge = shade), and re-pull the perf file from a recorded byte
|
||||||
|
offset (`stat -c %s` before launch) — the file accumulates across
|
||||||
|
processes. `nev=` in IT lines under-counts motion (a visual scroll is
|
||||||
|
the ground truth).
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user