mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-12 13:15:10 +00:00
The first version slowed small brushes by about a third (reviewer's measurement: a 20 px soft zigzag 856 → 1736 ms over 6000 dabs), for three reasons, all gone: - `CoverageMap::accumulate` looked the dab's tiles up by walking the whole tile map (`iter_mut().filter_map`) on every dab, so a long stroke's thousands of tiles taxed every small dab. The touched tiles now leave the map (`remove`), accumulate, and go back: one to four lookups for a small tip. - The composite allocated a fresh 64 KB buffer per tile per frame (6872 `brk` calls per 3000 dabs against main's 542): the heap grew and trimmed every frame. The buffers live in a pool on the renderer (`RegionPool`, emptied on clone) and are reused from composite to composite. - The row / tile bodies passed as closures to the helpers cost a 20 px tip's `rasterize` half as much again (7.5 → 11.6 µs). They are `#[inline(always)]` steps on `Copy` structs (`RoundTip::row`, `DabValues::tile`), called from plain loops below the threshold and from rayon above it; `rasterize` is back to main's time. `examples/stroke_bench.rs` times a long zigzag stroke of one tip, push + composite per point (best of four, loaded machine; main → now): 20 px 667 → 706 ms, 50 px 1805 → 1734 ms, 150 px 11 017 → 5163 ms. Comments: the per-tile copy of `src` in the composite is the intended reading (a tile without per-dab colour only occurs in strokes without per-dab colour, where `src` is never rewritten), and `set_parallel_min_pixels` changes a process-wide static harmlessly. `parallel()` and the helpers' `pixels` are `cfg`'d so the wasm build has no unused warnings. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>