perf: cull polylines via cached projected bounding boxes & chore: prepare for v8.3.1 release#2212
Conversation
Cache the projected-space bounding box on each projected polyline (lazily, so culled fragments never compute one). This replaces two per-frame O(points) scans in aggressive culling: - world-stretch detection now compares the bbox against the world east and west edges, instead of iterating every point of every polyline that overlaps the viewport latitudes - fully-visible polylines (bbox contained in the viewport bounds) skip the per-segment aabbContainsLine scan and its sublist allocations Benchmark (benchmark/feature_layer_benchmark_test.dart, JIT): 600 polylines x 60 pts, all visible, panning: 2631 -> 2484 us/frame. Mostly-culled scenario unchanged.
Widget- and kernel-level CPU benchmarks for the polyline, polygon, and
marker layers, plus a direct getOffsetsXY benchmark. Lives in
benchmark/ (not test/) so it is opt-in and does not extend CI runtime:
flutter test benchmark/feature_layer_benchmark_test.dart
Numbers are JIT and only meaningful relative to each other (before vs
after a change on the same machine).
(cherry picked from commit ed76dc6)
|
Hey, thanks for these performance improvements! (Slightly annoyed that I left gaps in when putting all the caching stuff together lol 😂). I'm still travelling right now, but I'll try to give these a review when I'm back in a couple weeks :) |
|
Thanks @JaffaKetchup . Full disclosure these came up when I was doing some AI debugging and optimisations. To provide some verification of the claims, I put together a repo with samples There's pretty minimal improvements on this optimisation, as seen below. polylines_before_after.mp4Full repo is https://github.com/ben-milanko/flutter_map_perf_demo |
Change demo app user agent to fix tile loading Add simple AI guidelines to CONTRIBUTING.md
JaffaKetchup
left a comment
There was a problem hiding this comment.
LGTM I think, thanks again!
(I'm not super happy with the _ProjectedPolyline no longer having a const constructor and departing from symmetry with the _ProjectedPolygon, but it's still immutable which is the important thing.)
I've also hijacked this PR to prepare for a patch release (v8.3.1).
While profiling flutter_map in a production app on low-powered hardware, I found two per-frame O(points) scans in
PolylineLayer's aggressive culling:stretchesBeyondTheLimits()iterates every point of every polyline that overlaps the viewport latitudes, every frameaabbContainsLinescan (andsublistallocations), even though nothing needs to be culledThis PR caches the projected-space bounding box on
_ProjectedPolyline(computed lazily — culled fragments never compute one; cached instances are created once per zoom level alongside simplification). The world-stretch check becomes an O(1) bbox comparison, and polylines whose bbox is contained in the viewport bounds are yielded whole, skipping the segment scan entirely.Results
Measured with
flutter test benchmark/feature_layer_benchmark_test.dart(JIT, best-of-reps):Modest in this paint-dominated scenario, but the cull step itself no longer scales with point count, which matters as polyline density grows.
All existing tests pass. The benchmark harness commit is shared with #2211 (identical file content — it collapses out of this diff once either merges).