-
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
Optimize "Map#setFilter" performance for "hover effect" use case #2874
Comments
This is a specific proposal for #200 |
What would the shader do based on the uniform value? Is the idea that it would implement style syntax similar to #200 (comment)? |
The shader would not draw a feature unless the feature's id (i.e. The shader implementation would look roughly like this: if (u_feature_id_filter === a_feature_id) {
gl_FragColor = vec4(0.0);
} This could be a stepping stone to the syntax proposed in #200 or a standalone feature. The style syntax in #200 is more ergonomic for our users and offers less room for a fast implementation. |
Got it. So at least at first it's purely an optimization for existing functionality -- Looking at our current hover example, it wouldn't be able to benefit from this optimization, because it uses the "name" property rather than id. It also appears to be fast enough as currently implemented. Have we seen a lot of other cases where this pathway would be used? |
@lucaswoj It's possible that the with some small changes, the properties-only-update primitives over in #2812 could also be used for a (definitely less good, but possibly more expedient?) temporary solution to the hover problem: you could have a style layer like Maybe this is just as much work as the approach you're proposing here, I dunno. Just mentioning it because I was considering this hack just this morning |
Right. We'd have to modify the example to assign ids to features and filter based on id instead of name.
On on a state-of-the-art Macbook Pro and a data set containing 50 features, the Hover implementations that must work with larger data sets use the |
👍 We should implement mapbox/mapbox-gl-style-spec#391 before or alongside this, and use the "official" Vector Tile / GeoJSON |
tagging on to @lucaswoj 's comment, the existing |
@peterqliu Looks like most of the time in that example is spent in |
@jfirebaugh I was surprised to hear this, but it looks like it's because the raw pbf data is getting continually reparsed. Reason: the style update triggers a source update => reload tiles => the tiles' feature indexes lose their cached Vector Tile. This ticket's proposal notwithstanding, it does seem hover styling could get a pretty good improvement in the meantime if that cycle could be eliminated. |
@peterqliu @jfirebaugh Confirmed: here's a version of that example with one (admittedly odd) change: add a second source update: my apologies -- I also forgot that the speedup included a tweak I made locally to gl-js itself to filter the sources queried by |
@anandthakker Did you save this code, looks the same as the one @peterqliu linked to. However if I make your changes as described, I notice a performance improvement although it's still too slow as the hover lags behind the cursor with a noticeable trail (something not seen with the |
Ah, sorry about that. Here's the saved one: https://jsfiddle.net/7meh7d50/2/ (also updated in my comment above) |
Yeah, it's a bit faster but still not quite there if you zoom out. I like the shader filter-by-id idea coupled with mapbox/mapbox-gl-style-spec#391 (to use feature-level ids rather than a property). Let's try it out. |
Argh -- much more importantly, I also forgot that the speedup included a tweak I made locally to gl-js itself, not just the example: in style.js, queryRenderedFeatures queries every source, even if a |
+1 on using feature-level ids rather than a property id. I wasn’t aware of the difference. |
I can't tell if this is a more recent regression (I think it is), but this does not seem to be the case anymore. When using the state hovering example, I get a ton of lag behind the pointer (top of the line macbook, modern chrome). I recorded a video (which is not totally a fair representation as the act of recording slows things down further): We think the behavior got worse sometime since 0.21.0, but honestly cannot say for sure. Let me know if this is worth tracking in a separate issue. That being said, a more performant way to do hover effects would be hugely beneficial for our use cases. |
@ekelleyv currently the most performant way to do this is |
One issue with this approach is if the filter style changes the stroke, queryRenderedFeature only returns the tile clipped geometry which means the stroke doesn't always match the full feature. This can be worked around by doing a subsequent querySourceFeatures and using say turf.union and of course then redoing this on map move or zoom end because camera changes might bring in new tiles which include the feature hovered. I've implemented this and although it works most of the time it's certainly not the nicest solution or simplest thing to implement. |
This is exactly what we had implemented as well. We switched to Purely for our own planning purposes, do you have any concept of where this fits on your roadmap? Any way we could be of help? |
While we're still investigating in-shader filtering for hover effects, I'll try to find out what exactly regressed in setFilter performance. Maybe we can fight some of it back. |
I just merged a PR that should improve For cases where the source is big (e.g. a few megabytes of county shapes), there are two remaining bottlenecks:
|
Retitled this issue to better reflect the work happening & avoid specifying a particular implementation. |
I ran into this issue today on v0.32.1 with a 3MB |
@ezheidtmann that sounds surprising. Would you mind setting up a JSFiddle with that GeoJSON and setFilter usage so we could take a quick look? |
Sure thing. PR has possible fix. Fiddle showing slowness is here: http://jsfiddle.net/vk2knag7/1/ |
Are repeated calls to If you zoom in a little (so you at least see the state labels), and move your mouse in a circular motion you can see the queuing I'm talking about. Though this is react-driven, and not pure JS, I'm pretty sure Note: Here the calls to |
Another observation, the responsiveness seems to slow down if more features go 'offscreen'. |
|
Updated my lib (and example) to (optionally) use the I totally understand that the webgl-based rendering presents different tradeoffs, and I don't think there is anything to 'fix' here. But I definitely await |
In the above-linked example, Tennessee breaks at certain zoom/pan, because turf/union cannot join the strange polygons that are being passed back from |
any updates on this? |
@benderlidze As a workaround until this ticket progresses check out #5040 (comment) as there is a trick of duplicating the source which improves performance of setFilter based hovers. |
→ #6022 |
Creating performant hover effects in GL JS is hard. One solution to this problem is making our primitives (
Map#queryRenderedFeatures
,Map#setFilter
,GeoJSONSource#setData
, ...) faster. Another solution is adding a primitive optimized for the hover usecase.Suppose we allowed filtering features by id within the shaders. With a buffer size increase equivalent to that of a single data-driven styling property, we could have zero-overhead filtering on even the largest data sets.
A single filter id could be passed to the shader as a
uniform
, with some common boilerplate encapsulated within#pragma
.We could use this optimized filtering pathway automatically when a filter in the form
['==', 'id', *]
is used or provide a newfilterId
style property.cc @jfirebaugh @mourner @peterqliu @tmcw @tristen
The text was updated successfully, but these errors were encountered: