fix(mapbox): MapboxOverlay: render deck layers in batches#9939
fix(mapbox): MapboxOverlay: render deck layers in batches#9939
Conversation
|
Hi, I'm still working on small, minimal repro of actual bug but seems like it's same issue as #8919. Basically MapboxOverlay is rendering layers (including all pre/post-render effect system) one by one, whereas GoogleMapsOverlay does it in one shot and all extensions based on pre/post-effects are working as expected. What we have found, if we have following layers:
That if they are rendered in separate Giving that MapboxOverlay is very fragile, i've hidden this "feature" behind property, so we don't change behavior for all users until solution is proved ok. Note - i tried to achieve more-or-less same result that is "batching" renders by moving all layers under CompositeLayer, but seems like this is very tricky as
This made me believe that only proper solution is proper rendering management inside |
There was a problem hiding this comment.
Thank you for this. Interesting finding - up until now I assumed there weren't any negative implications of drawing each layer in their own "batch", but it makes sense that these layers coupled by extensions should draw together.
For some background - this module basically creates a MapboxLayer for each deck layer so that they could be re-ordered with beforeId / slot if the user wanted to, but this PR suggests this isn't necessary and now I'm thinking batched drawing may be a better way to go as the default behavior since long standing features rely on it, and this solution preserves the re-ordering feature.
@Pessimistress are there any benefits to having 1:1 mapbox layer:deck layer?
If there aren't, I'm in favor of replacing resolveLayers and MapboxLayer's internals with a grouped implementation entirely.
I'd just ask that we add a remark in the docs about go to correctly group coupled layers, and explain why features like masks won't work across groups.
modules/mapbox/src/types.ts
Outdated
| triggerRepaint(): void; | ||
| } | ||
|
|
||
| export type OverlayLayer = Layer<{ |
There was a problem hiding this comment.
| export type OverlayLayer = Layer<{ | |
| export type MapLayer = Layer<{ |
Maybe?
There was a problem hiding this comment.
Well in this context "Map" is mapbox/libre Map and this type is specifically a deck Layer with specific props.
In this context i think it's more important to disntinguish
Full name would DeckLayerWithOverlayProps - which is too much.
I'll revert to Layer<LayerOverlayProps> which is long but also very good description of actual type.
If you're ok with this then great i can update PR. My context is that i don't understand if that API is good enough for all cases w.r.t extensions so i leaned towards "safe experiment". But after brief research i am still not sure if this is "robust" enough. I see at least two issues: 1 2
I don't know what can possibly go wrong if we what we do - and we do that in That means that full, robust solution would need to look like this:
I would still propose to make it experiment. |
| const layers = flatten(newLayers, Boolean) as OverlayLayer[]; | ||
|
|
||
| function getGroupId(layer: OverlayLayer): string { | ||
| return `deck-layer-group:${layer.props.beforeId}:${layer.props.slot}`; |
There was a problem hiding this comment.
Can slot and beforeId be used simultaneously? I understood it as you'd use one or the other, but I don't have experiencing using slot yet.
There was a problem hiding this comment.
Can slot and beforeId be used simultaneously
I don't know. In that sense this is not different than current solution, also not sure how it will behave.
I'll change it to something clearly indicate which layer group comes from manual ordering (beforeId) and which is assigned to slot.
|
|
||
| /* Mapbox custom layer methods */ | ||
|
|
||
| onAdd(map: Map, gl: WebGL2RenderingContext): void { |
There was a problem hiding this comment.
What about onRemove, etc?
Also, in reviewing the MapboxLayer I'm seeing some guards to prevent rendering prior to initialization and something about id needing to be immutable - were these intentionally omitted from the grouped version?
There was a problem hiding this comment.
MapboxLayer registered itself in deck instance - we don't do that for MapboxLayerGroup, it's not needed.
(notice, we don't add it to "deck.userData")
Groups are just holders of beforeId/slot - they render all layers registered in deck that match this beforeId/slot - again no need to register anywhere/update/remove.
74fabb3 to
b1a5ee3
Compare
| The constructor additionally accepts the following options: | ||
|
|
||
| - `interleaved` (boolean) - If `false`, a dedicated deck.gl canvas is added on top of the base map. If `true`, deck.gl layers are inserted into mapbox-gl's layer stack, and share the same `WebGL2RenderingContext` as the base map. Default is `false`. Note that interleaving with basemaps such as mapbox-gl-js v1 that only support WebGL 1 is not supported, see [compatibility](./overview#interleaved-renderer-compatibility). | ||
| - `_renderLayersInGroups` (boolean, _experimental_) In interleaved mode, render layers in batches to enable cross-layer extension handling. Default is `false`. |
There was a problem hiding this comment.
I think having this be opt-in is the way to go, we can turn it on by default in the future, but I would only do that as part of a release
d3eff73 to
5e6282e
Compare
5e6282e to
cf731c1
Compare
|
@chrisgervang @felixpalmer I've applied suggestions and added unit-tests. |
modules/mapbox/src/mapbox-overlay.ts
Outdated
|
|
||
| map.on('styledata', this._handleStyleChange); | ||
| resolveLayers(map, this._deck, [], this._props.layers); | ||
| this._resolveLayers([], this._props.layers); |
There was a problem hiding this comment.
I see what you're doing, but honestly I would keep the first 2 parameters.
It's asking for trouble when later you have:
private _onRemoveInterleaved(map: Map): void {
map.off('styledata', this._handleStyleChange);
this._resolveLayers(this._props.layers, []);
removeDeckInstance(map);
}
what if in the future someone passes in a different map to _onRemoveInterleaved(map)?
6f62dd7 to
34947e7
Compare
@chrisgervang can you link those long standing features that rely on this for visibility? |
|
@charlieforward9 mask and collision extension are mentioned in the description. We don't have a list of extensions that use pre/post render effects but they can be found on the extensions module. Users can also implement custom extensions that need layers drawn together |
Related #8919
Background
Extensions like CollisionExtension/MaskExtension rely heavily on multi-pass rendering and (seem) to require that "all affected" layers using extension to be rendered in one
deck._drawLayerscall.This PR experiments with assumptions that bugs related to extensions are largely caused by MapboxOverlay rendering each deck layer in complete separation from other deck layer even if they are in same "batch" (by slot or beforeId).
MapboxOverlay new rendering mode
_renderLayersInGroups: true- means that deck layers in same group are rendered in one "batch"Change List