Make view cones fast on the web - #251
Conversation
On the web there is no native library, so every cone runs the Dart query. It had fallen behind ish_query: it cast rays at seams and beside corners a wall runs straight through, kept every collinear outline point, and hashed Offsets and doubles, which is slow once compiled to JavaScript. Port ish_query's events, sector culling and same-edge outline simplification, index vertices once instead of hashing them, remember which vertices are seams until the wall mask changes, and test each tree node's bounds once per ray. A dragged Lotus cone's query drops from 9.3 ms to 1.7 ms (median) in Edge, and from 14.5 ms to 1.8 ms on Breeze. Over 256 Lotus queries the Dart and native cones cast the same rays and draw the same outline. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Flutter's web engine drops every built path at the end of a frame, so a path made of lineTo calls is rebuilt point by point, one call into CanvasKit each, every frame it is drawn. The map's playable-area clip has thousands of points, which cost about 3 ms a frame while a cone was dragged. addPolygon hands each ring over in one call; the shape is the same, and screenshots of all four test maps match main pixel for pixel. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Dart query found wall crossings and vertex topology on its first cone, a 25 to 45 ms stall the first time a cone was dragged on each map. Build them when the map's sightlines load instead, only when the native library is unavailable. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (5)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change prepares Dart visibility queries when native acceleration is unavailable. It updates wall-mask caching, vertex topology, ray-event processing, and traversal. Cone paths now use polygons, and tests cover seam ray counts and visibility results when reusing a model across camera heights. ChangesSVG height visibility
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~45 minutes Change: Refactor Merge Risk: ⚪ Minimal · up to No concrete issue remains that would prevent merging after normal checks. Security Architecture ReviewSecurity architecture risk: ⚪ Minimal · up to The reviewed initialization and cache changes preserve map-data validation and existing access boundaries. No material security risk was found in these changes. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Matches what the native query and the old Dart query report. The seam test now checks the return to the lower eye against a fresh model too, so a cache that only resets when walls stop blocking would fail it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On the web, view cones drag at 60 fps or below on Lotus and Breeze. The browser has no native cone library, so every cone runs the Dart query, and that query had fallen behind the native one. On top of that, Flutter's web engine rebuilt the map's clip path point by point on every frame.
What changed
ish_queryinnative/height): the same vertex events, sector culling and same-edge outline simplification. Vertices are indexed once instead of hashed per query. HashingOffsets and doubles was the main cost once compiled to JavaScript. Seam checks are remembered until the set of blocking walls changes, and each tree node's bounds are tested once per ray, not twice. Across 256 random Lotus cones, the Dart and native queries cast the same rays and draw the same outline (area difference 0).addPolygon. Flutter's web engine drops every built path at the end of a frame, so a path made oflineTocalls is rebuilt with one call into CanvasKit per point, every frame. The playable-area clip has thousands of points, which cost about 3 ms per frame.addPolygonpasses each ring in one call. The shape is unchanged.Numbers
Release-mode profile web build, headless Edge (RTX 4070, D3D11). A Jett with a 180° max-length cone is dragged over the same 8-second path on each map, using real
PlacedWidgetBuilderwidgets and pointer events. Frame gaps are capped by the ~144 Hz frame clock (6.9 ms).Screenshots of the cone on all four maps match main pixel for pixel.
What's left
archive). The browser'sDecompressionStreamcould do this natively. Also its own PR.Testing
test/svg_height_visibility_test.dart's touching-pieces test now compares ray counts, not outline point counts. Same-edge simplification shortens one whole wall's outline, while each piece keeps its own edge.🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Performance
No blocking issues were established; the reviewed changes are safe to merge on the available evidence.
What we checked:
Summary
The PR ports the cone-query event and outline algorithm to Dart, prepares fallback geometry during map loading, and batches polygon construction for web rendering. No actionable regression was established.
Reviews (1) · Last reviewed commit: "Count a ray that misses every wall as on..."