- drag_latency (ui-egui example): the whole app driven headless at 60 frames a second; per develop control,
the latency of each drag tick until its draft is the loupe (or, with ZOOM, the zoom window) texture, the
ticks never shown, the UI thread's frame time and the time to the full-quality render after release.
- drag_tick_bench (engine example): the UI-thread work of one drag tick outside the render.
- render_bench: ONLY=window (a 1:1 zoom window cut from the original: first window, pan, slider changes;
GPU_ONLY=1, DUMP=file for pixel A/B), ONLY=load / load1 / jpeg (+ LOADONLY=1): the steps of opening a photo.
The window rows carry no colon in their names, so `cargo xtask bench` records each of them (parser test).
- open_timeline (ui-egui example): opening raws in Detail through the real app logic, headless with the GPU.
Co-Authored-By: Claude <noreply@anthropic.com>
thumb_request_bench measures UI-thread time and allocations for grid
thumbnail requests (with and without pending work), using the same counting
allocator as the other bench examples. REAL_PHOTOS=1 samples up to four
photos from PHOTO_ROOT, which must now be set explicitly; the original
defaulted to a local photo library path.
Split from a327b98 (T0 thumbnail work).
Co-authored-by: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An unchanged grid frame used to copy every visible id, hash them twice
(date-run and layout cache keys), clone the date runs, build a full
Vec<Cell>, rebuild the stack index, loop over every cell for the
viewport test and, in Local folder views, look up every photo to count
the browsed ones for "Add N to My Photos".
- engine: Session keeps the visible list as Arc<[PhotoId]> with a
process-unique generation that changes whenever the list is
recomputed; `visible_shared()` hands both out without copying.
- grid: date runs, layout, stack index and the Local count are cached
by that generation (GridCache; replaces the id-hashing date_runs and
grid_layout caches). The layout gains a row index (GridRow), so the
visible + prefetch rows and the date headers are found by binary
search; the sticky header too.
- GridStats counters (frames, cells visited, view/layout/stack-index
builds, last frame time) for the bench and tests.
Measured with the new `cargo run --release -p lightcraft-ui-egui
--example grid_bench` (synthetic library, 1400x900, thumbs 160, missing
files so no decoding is timed, scrolled to the middle, 120 unchanged
frames; machine at load ~200-250, so medians of the whole app frame,
before/after interleaved in the same session):
photos layout source median frame ms allocs/frame cells visited
before -> after before -> after before -> after
200 photo all 1.29 -> 1.28 4747 -> 4729 200 -> 93
200 square all 1.09 -> 1.09 4771 -> 4753 200 -> 74
10 000 photo all 1.52 -> 1.41 5425 -> 4911 10000 -> 100
10 000 photo local 1.90 -> 1.42 5475 -> 4948 10000 -> 100
10 000 square local 1.62 -> 1.07 5365 -> 4838 10000 -> 76
85 000 photo all 2.41 -> 1.29 8996 -> 4729 85000 -> 93
85 000 square all 2.29 -> 1.08 9020 -> 4753 85000 -> 74
85 000 photo local 17.6-34.0 -> 1.32 9049 -> 4766 85000 -> 93
85 000 square local 16.2-25.0 -> 1.10 9073 -> 4790 85000 -> 74
Frame cost no longer depends on the number of results; what is left
(~1 ms, ~4.7k allocs) is drawing the ~75-100 cells near the screen.
Headless snapshots of the demo grid (justified with a sticky header,
after keyboard navigation, square) are byte-identical before/after.
Tests: layout rows cover every cell in order and the binary search
matches a linear scan (square + justified, groups); a 3000-photo
headless library checks that unchanged frames build nothing and visit
< 300 cells, plus date headers, scroll-to-selection, stack badges,
keyboard navigation and a rotation re-laying the grid.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>