-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Performance of style mutations not good enough for hover interactivity #2492
Comments
This is related to automatic batching. cc @mourner |
Hmm, this is actually pretty surprising since the current code is batching style mutations to happen once per frame — you can see how that works in style.js. Can you set up a minimal JSFiddle test case that reproduces the slowdown? That would make it easier to see what's going on in your case. |
@kristfal, are you asking for mutations to be deduplicated such that if 10 calls to |
@lucaswoj this is how it works currently, so there most be some catch here... |
Here is a JSFiddle that should illustrate the problem. |
Thanks for a great test case!
They're not queued — they happen exactly once when you move from one cell to the other, and you can confirm this by putting I found that the underlying problem is not batching updates, it's that even with proper batching, updating layers is too slow — on each such update, workers have to reparse all GeoJSON tiles in view and repopulate all buffers. @ansis @lucaswoj @jfirebaugh I'm not sure how to approach this. It's looking like |
Here's how the worker profiler looks after a few hovers: The perf warning is "not optimized: optimized too many times" (more info). |
@mourner Not to push some other framework, but for the use cases where I've had problems with setFilter being too slow I've personally resorted to projecting the coords to pixel coords and creating an SVG layer for the feature on the fly in my mousemove callback with D3, a la https://bl.ocks.org/shimizu/5f4cee0fddc7a64b55a9. This is fast enough to be able to hover over the features without a noticable delay. While I am not recommending that as a solution, doing something similar may be an acceptable workaround (Drawing the feature to a canvas). If it would be possible to fire an event when the setFilter has finished updating all the layers one could choose not doing any new setFilters until the first one is finished which would at least prohibit the "queue" effect. |
@filiphhh I sometimes also fall back to d3/SVG, but in the case of mapbox-gl this has so far mostly been related to (lack of) dynamic styling. Couldn't you achieve a setup similar to what you describe with two separate sources? One for the backdrop and one that you update with only the features shown when hovering? In that case the expensive parsing @mourner mentions does not have to happen and you make full use of geospatial indexes for the hovering. The only expensive part in this approach is the setData() call on the second source, especially if the GeoJSON is large/complex, but I've used this approach with up to tens of thousands of features with reasonable performance, and as I understand more effective means of setData() may be on the way. I have several use cases involving filtering where I use this technique instead of setFilter(), such as: |
@filiphhh I forked your fiddle and updated it with the approach I suggested.. and yes.. the performance gain is huge.. |
I ran into this issue today originally using the setFilter approach but now swapping this out with the approach from @averas at #2492 (comment). It's still not perfect (the hovered feature lags behind the cursor on slow machines or if you move the cursor really fast, or with many tiny polygons), but it seems better than the setFilter approach. Perhaps the example at https://www.mapbox.com/mapbox-gl-js/example/hover-styles/ should be changed? EDIT: I think I spoke too soon. The GeoJSON setData approach doesn't really work at tile boundaries because |
The can be worked around using map.querySourceFeatures to try to collect all the tile clipped geometries for the feature and using turf.union to join them back together. A bit of a hack, but it mostly works. |
Would be nice to add a section into the docs for @averas solution. This issue is buried pretty deep here, though this is a relatively common problem. |
@Scarysize What do you think the best way to surface this would be? We've got the docs, examples, and guides. We can also fold this use case into the core API #200 |
We did have the issue in an situation exactly like this: https://www.mapbox.com/mapbox-gl-js/example/hover-styles/ and additionally with a point overlay (geojson) with ~5k points. I guess the latter one is the more extreme, so I would recommend an example with a lot of point features which can be hovered. EDIT: Adding this feature to the core API would also be much appreciated 😃 |
Much faster way to highlight features proposed in mapbox#2492 discussion
Issue
Style mutations are expensive, and can be quite slow on mobile devices. When applying new style mutations faster than the worker is able to process an ongoing mutation, the new mutation is queued and applied after the previous one has completed.
This happens even when the same style
property
in the samelayer
is changed multiple times such assetFilter('foo', [==, 'bar', 5])
followed bysetFilter('foo', [==, 'bar', 3])
followed bysetFilter('foo', [==, 'bar', 11])
and so on...If this process is repeated too quickly for an ongoing period in a performance constrained environment (mobile devices), browsers start freezing and crashing due to memory issues.
Proposal: New style mutations should be debounced, and only the latest change should be applied for a given layer and property.
mapbox-gl-js version:
earcut final
rebase 20th of AprilSteps to Trigger Behavior
foo
to the map,setFilter('foo', [==, 'bar', 5])
followed bysetFilter('foo', [==, 'bar', 3])
followed bysetFilter('foo', [==, 'bar', 11])
..Expected Behavior
The map should apply the latest style mutation for a given mutation type and layer, and skip any previous queued mutations for the given layer and type to prevent the buildup of a "mutation backlog".
Actual Behavior
The map queues and applies all mutations, quickly degrading the performance of the map and if mutations are continued, the browser crashes.
The text was updated successfully, but these errors were encountered: