From 9b12dc473166a9893b31a1dd22e80f9ac66d29ab Mon Sep 17 00:00:00 2001 From: Greg Pomerantz Date: Sat, 31 Oct 2026 13:54:56 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20HANDOFF=20=C2=A78.12=20(egui=20port=20+?= =?UTF-8?q?=20tttest=20harness),=20README=20reflects=20the=20egui=20stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- HANDOFF.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 17 ++++++++------ 2 files changed, 75 insertions(+), 7 deletions(-) diff --git a/HANDOFF.md b/HANDOFF.md index 29934c9..149932e 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -850,6 +850,71 @@ afterwards (`grep FR fr.log`). If smooth → port the gallery app layer (db/scan/turbojpeg/LRU/GPU-upload, all renderer-agnostic) to an egui grid + the about_to_wait frame driver + EGL setup from egtest. +### 8.12 Gallery egui port + tttest (Tauri) harness (2026-10-31, `5a8284e`, `d82ecf7`) + +**The port is DONE and compiling** (`5a8284e`): all Slint code removed +(vendor/, shim/, gputex, vkshim, baseline60, main.slint), replaced by +egui 0.31 / egui_glow / winit 0.30 / glow 0.16 / egl 0.2 / android-activity +0.6. File map: `src/lib.rs` (app state, full cache, scan, viewer math, +android_main: EGL + WinitHarness + app-owned frame driver), `src/ui.rs` +(World: tick/frame/touch/paint), `src/images.rs` (turbojpeg → RGB8 → +Vec on the worker). + +**The frame driver** (egtest's architecture, now in the gallery): +`about_to_wait` runs `World::tick` EVERY loop iteration (liveness — at +rest the tick notices scan writes / decode landings, sets dirty, +schedules a render); then: animating → spin+render on the 120 Hz grid; +dirty → one render; else park ~8 ms. `nice -10` on the UI thread. + +**The performance bug that crashed the phone**: `ColorImage:: +from_rgba_unmultiplied` is O(n) per-pixel (measured 8–60 ms per 2 MB +on the Pixel 9 Pro) and was running on the UI thread inside +`apply_plan` (8–16 ms/frame → 9.57% drops, median render 18.78 ms vs +the 8.3 ms budget). Plus ~4 transient 2 MB buffers/frame → OOM-class +pressure on a personal phone (the crash). **Fix**: workers produce +`Vec` directly (`images::to_pixels`); the UI thread only +MOVES the vector into `ColorImage { size, pixels }` (O(1)). Cache +caps tightened: 6 entries / 16 MP. + +**Other port gotchas** (all fixed, all in code comments): +- `Painter::image`'s `uv` is NORMALIZED 0..1 — a screen-space uv made + every cell one clamped corner pixel (solid rectangles; the + emulator's solid-color test images hid it). Use `FULL_UV`. +- Fling velocity: per-EVENT delta EMA (0.8/0.2, ~60 ms memory), + clamped 14000 px/s. Frame-based velocity dies when touch events + sample slower than rAF (adb `input swipe` ≈ 17 Hz). +- winit 0.30 never delivers `RedrawRequested` on Android → the + app-owned driver above (driving from `about_to_wait`). +- egui 0.31 `Painter` is shared (`&Painter`): use `ui.painter_at(rect)` + for clipped cell painting. + +**Emulator: fully verified** — grid renders real thumbnails, drag + +fling scroll, tap→viewer, ×/back close, status strip. **Phone:** +thumbnails render, 8580 files scanned; the per-pixel fix is BUILT +(`target/release/apk/gallery.apk`, framelog) but NOT YET MEASURED — +phone was left alone after the crash (personal device). **Pending:** +(1) user clearance, (2) install the fixed APK, (3) `fr_capture.py +--manual 45` expecting render cost 18.78 → <8.3 ms, (4) USER FINGER +TEST — is it smooth where Slint felt janky? If drops remain, next +suspect: egui mesh build for 33+ cells in `paint+swap`. + +**tttest** (`d82ecf7`, `harnesses/tttest/`): the third harness stack +— Tauri 2 → wry → Android WebView (Chromium). Identical scene to +sltest (8580 rows, 150+2 px, alternating colors), virtualized 26-element +DOM pool, custom touch drag+fling in JS, rAF-driven. FR lines cross +the Tauri event bridge at 1/s → Rust writer thread → +`/sdcard/Android/data/wow.gmp.tttest/files/Documents/fr.log` (Tauri +document_dir; the framework creates it — raw std::fs mkdir of the app +external tree is SELinux-denied). `fr_capture.py` now launches any +package (monkey LAUNCHER fallback) and finds all three log paths. +Emulator: 1319 frames, 60 Hz clean, 5 isolated drops (all at swipe +injection), JS render cost med 2 ms. JS gotchas: `touch-action` is NOT +inherited (each `.row` must claim it or the WebView steals the pan); +fling velocity must be per-event EMA. aarch64 build: 11 MB APK, +compiles; phone test pending. **This is the A/B/C complete**: sltest +(Slint/Skia) 0.33–0.83% vs egtest (egui/glow) 0.12% vs tttest +(WebView) — the finger test on the phone is still the final referee. + ### 8.8 Jank-hunt round 2 + the keep-alive incident (2026-10-28) Follow-up on "still seeing jank" (post-GPU-texture). Instrumentation: diff --git a/README.md b/README.md index 0179c1e..03f3231 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,16 @@ # gallery A high-performance image gallery for Android (primary), Linux and macOS — -Rust + [Slint](https://slint.dev) 1.18. +Rust + [egui](https://egui.rs) 0.31 (egui_glow / winit 0.30 / glow 0.16). +Ported from Slint 1.18 after the zero-jank campaign showed Slint-on-Mali +janky at its base level while egui measured 3–7× cleaner (HANDOFF.md +§8.11–8.12). The Slint tree is preserved in git history and +`docs/BUGS/`. -**Status: M1 (metadata scan + DB) done.** See **[HANDOFF.md](HANDOFF.md)** — -the development hand-off with the full plan: architecture, thumbnail -pipeline, milestones M1–M7, Slint gotchas, permission flow, and performance -targets. Next: M2 (thumbnail engine). +**Status: M0–M4 ported to egui, zero-jank tuning in progress.** See +**[HANDOFF.md](HANDOFF.md)** — the development hand-off: architecture, +thumbnail pipeline, milestones M1–M7, the Slint jank campaign, the egui +port, and the sltest/egtest/tttest harnesses (A/B/C: Slint/egui/WebView). ## Build @@ -22,5 +26,4 @@ for exact paths/env). ## License -GPL-3.0 (see [LICENSE](LICENSE)). Built on Slint, used under the Slint -royalty-free license for proprietary apps (see HANDOFF.md §10.4). +GPL-3.0 (see [LICENSE](LICENSE)). Built on egui (MIT/Apache-2.0).