v0.2.19 — framerate suspension guard (Android #35)
What's Changed
The fix
In the floating-keyboard IME (and under any process starvation — GC, occlusion, debugger), a frame stall folded its wall-clock gap into the framerate sampling window. LP_FRAMERATE collapsed → Steps() shrank → every post-stall frame zoomed a large fraction at once, and the decaying average recovered slowly — the reported lag, then jump, then having to zoom back.
RecordFrame now treats >250ms inter-frame gaps as suspensions once calibrated: the partial window is dropped and the pre-stall zoom pace survives. Design points hammered out over four review loops:
- Calibration gate — the guard never fires before the first window completes (dropping the initial calibration froze the estimate at its default; caught by the low-memory CI suite)
- Time-weighted budget (~1s of guardable gap per window) — bursty throttling (3 long gaps + 1 normal frame, the timer-coalescing shape) exhausts it and the true cadence is measured; a consecutive-counter variant froze forever under that pattern
- Forward-time resets restore the budget — a genuine pause gets fresh protection; backwards-stamped stale input events (harness pathology) deliberately don't
- Genuine sustained slowness (<4fps) is still measured — verified: steady 3.3fps converges the estimate 6249→333 centi-fps
9-case regression suite (test_framerate.cpp); full matrix green.
What's Changed
- fix(framerate): a frame stall must not collapse the zoom-rate estimate (Dasher-Android #35) by @willwade in #80
Full Changelog: v0.2.18...v0.2.19