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 —
|
||||
see the M4 entry in §9 for the verification notes + emulator pinch
|
||||
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