-
Notifications
You must be signed in to change notification settings - Fork 2.2k
fix(mapbox): In interleaved, batched mode don't render layers twice #9944
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
fix(mapbox): In interleaved, batched mode don't render layers twice #9944
Conversation
modules/mapbox/src/deck-utils.ts
Outdated
| const hasNonMapboxLayers = | ||
| // In interleaved mode _all_ layers are rendered through custom mapbox layer API (either one by one or in groups, | ||
| // so assume we never have "non-mapbox-managed" layers. | ||
| !interleaved && deckLayers.some(layer => layer && !mapboxLayerIds.includes(layer.id)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like MapboxOverlay has many modes of operations and afterRender does following:
- checks if there are layers that are not "rendered by MapboxLayer"
- or some non-mapbox viewports
Now, since deprecation of MapboxLayer API (it's private since v9 as i understand) at least in interleaved mode (batched or not), all layers are always managed internally by resolveLayers and always rendered in "main render pass managed by maplibre", so actually feels like hasNonMapboxLayers is not necessary.
I looked very briefly and it looks like afterRender isn't used at all in overlaid mode.
Am i right, that whole hasNonMapboxLayers cannot be effectively true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, maybe.. what about a multi-view case where you have an minimap or an orthographic view on top of the main map with layers that only appear in those views?
Are those layer render passes still "managed" by this overlay?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If all layers are in userData.mapboxLayerIds then I think this can be simplified like you've proposed.
From a user perspective, they are always providing all layers to the overlay's layers props.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe.. what about a multi-view case
I have no experience with multiview, but this patch should multiview behavior.
If there are other views, all layers will be rendered there.
I am only removing case for layers in mapbox view (the main, the default one embedded in mapbox/libre map) ...
If all layers are in userData.mapboxLayerIds ...
Yes, this is what i believe. We cannot add MapboxLayer to "deck" in any other way than by passing normal deck Layer instance through overlay.setProps({layers: ....}) - uncontrolled map.addLayer(new MapboxLayer()) i. impossible now.
So ultimately mapboxLayerIds is equal to all layers in deck instance.
So this PR clears flow, so it's basically two steps:
- mapbox/libre renders its own layers and all deck layers (in groups or not)
afterRenderrenders other views
To complete description, this is how i understand current flow:
- (1) mapbox/libre renders own layers and some deck layers added manually with
map.add(new MapboxLayer({deck: someOtherDeckInstance})(?) - mapbox/libre all viewports, but
- for
mapboxview, it skips layers rendered in (1) renders rest on top - for other views renders all deck layers
- for
The main mystery for me is that i don't know if/how it was possible to (1) to skip some layers. It must be some legacy path that was impossible now in v9.
I believe current code has just many unusable branches that are not working at all. Possibly remnants from old incarnations of MapboxOverlay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this PR clears flow, so it's basically two steps:
- mapbox/libre renders its own layers and all deck layers (in groups or not)
- afterRender renders other views
Very interesting - is this the order it always occurs in as well? Layers and maplibre first, then other deck views (which may draw a clear color for a background)? That would explain why adding a view with a clear cannot be done before maplibre draws. Or, view draw order respect the views prop order?
I was trying to add a background color to the framebuffer while using Maplibre's globe view. Maplibre has no API for setting the background color, but deck can draw a full frame view with a clear color. I couldn't figure out why the background always drew over everything - perhaps this is why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main mystery for me is that i don't know if/how it was possible to (1) to skip some layers. It must be some legacy path that was impossible now in v9.
I believe current code has just many unusable branches that are not working at all. Possibly remnants from old incarnations of MapboxOverlay.
I can't tell you how much I appreciate your work here. There are many remnants of old MapboxLayer features in MapboxOverlay since we wanted to offer a full migration path. But I agree this seems like a feature that no longer is used and I'd like to remove it
|
I just've found that MapboxLayer docs are still available online even if it's not supported in v9 |
Yes, we hid it from the table of contents but kept it as an internal doc since the class was still in use. |
chrisgervang
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Pessimistress can you please gut check our conclusion in the comments that it's fine to remove these layer filters since it's for a feature no longer supported?
Instead of tracking MapboxLayer instances in a Set, use map.getLayer() to check if a deck layer has a corresponding MapboxLayer on the map. This simplifies the code since resolve-layers already creates MapboxLayer instances with the same ID as the deck layer. Ref: #9944 Co-Authored-By: Claude Opus 4.5 <[email protected]>
Instead of tracking MapboxLayer instances in a Set, use map.getLayer() to check if a deck layer has a corresponding MapboxLayer on the map. This simplifies the code since resolve-layers already creates MapboxLayer instances with the same ID as the deck layer. Ref: #9944 Co-Authored-By: Claude Opus 4.5 <[email protected]>
Instead of tracking MapboxLayer instances in a Set, use map.getLayer() to check if a deck layer has a corresponding MapboxLayer on the map. This simplifies the code since resolve-layers already creates MapboxLayer instances with the same ID as the deck layer. Ref: #9944 Co-Authored-By: Claude Opus 4.5 <[email protected]>
* chore(mapbox): Remove internal deck mode from MapboxLayer MapboxLayer is no longer a public API (only MapboxOverlay is exported), so the internal deck mode code paths are no longer needed. Changes: - Remove `isExternal` flag from UserData (always true now) - Remove internal deck creation branch in getDeckInstance - Remove `updateLayers` function (only used for internal mode) - Make `deck` prop required in getDeckInstance - Simplify MapboxLayer and MapboxLayerGroup to read map.__deck directly - Only MapboxOverlay calls getDeckInstance for initialization - Update tests to simulate MapboxOverlay flow Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Use getter for deck in MapboxLayer and MapboxLayerGroup Replace `this.deck` field with a getter that reads from `map.__deck` directly. This avoids potential stale references and simplifies the lifecycle - `onAdd` no longer needs to capture the deck instance. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(mapbox): Keep non-null assertions in render method Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Make __deck non-optional in MapWithDeck type Since MapboxLayer and MapboxLayerGroup are internal classes only used by MapboxOverlay, and MapboxOverlay always sets up map.__deck before adding these layers, __deck is guaranteed to exist when the map is set. Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Remove deck getter, access map.__deck directly Since __deck is guaranteed on MapWithDeck, we can access it directly through the map rather than through a getter. Co-Authored-By: Claude Opus 4.5 <[email protected]> * test(mapbox): Remove trivial deck instance assertions These assertions just verified getDeckInstance set map.__deck, which is obvious and doesn't test any MapboxLayer behavior. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(mapbox): Restore original test structure for external Deck The first test should create Deck without gl option to test the case where deck has its own gl context. This matches the original test behavior where deck.props.gl !== map's gl. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(mapbox): Initialize map.__deck in tests before using MapboxLayer The resolveLayers test now calls getDeckInstance to set up map.__deck before resolving layers, since MapboxLayer.onAdd expects it to be set. Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Consolidate gl context handling in getDeckInstance - Remove `gl` parameter from getDeckInstance - now obtained internally from map.painter.context.gl - Remove always-true `if (deck.props.gl === gl)` condition since deck is always created without gl and getDeckInstance sets it - Move WebGL1 compatibility warning from MapboxOverlay to getDeckInstance - Simplify MapboxOverlay._onAddInterleaved - just creates Deck with user props - Update tests to not pass gl when creating Deck instances Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(mapbox): Pass gl at Deck construction time, not via setProps The gl prop must be set when constructing Deck, not via setProps after construction. Move gl retrieval and WebGL1 compatibility warning back to mapbox-overlay.ts where the Deck is constructed, and remove redundant gl handling from getDeckInstance. Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Remove redundant parameters merge in getDeckInstance Parameters are already merged when creating Deck in mapbox-overlay.ts, so the merge in getDeckInstance is redundant. Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Remove unused deck prop from MapboxLayerProps The deck prop was vestigial from the old code where it was passed to getDeckInstance. Now MapboxLayer accesses the deck via map.__deck, so the prop is no longer needed. Co-Authored-By: Claude Opus 4.5 <[email protected]> * chore(mapbox): Remove unsupported mapbox-layers-react test app Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(mapbox): Remove deck prop from MapboxLayer in resolve-layers Missed this usage when removing the deck prop from MapboxLayerProps. Co-Authored-By: Claude Opus 4.5 <[email protected]> * chore(mapbox): Remove unsupported mapbox-layers test apps Remove mapbox-layers test app directory and update index.html links. The mapbox-layers-react app was already deleted in a previous commit. Co-Authored-By: Claude Opus 4.5 <[email protected]> * docs(mapbox): Update MapboxLayer doc to reflect internal-only status MapboxLayer is no longer part of the public API. Updated the doc to add a deprecation notice and migration guide pointing to MapboxOverlay. Co-Authored-By: Claude Opus 4.5 <[email protected]> * docs(mapbox): Remove MapboxLayer doc and update references MapboxLayer is internal-only and should not be documented as a public API. Removed the doc file and updated references in whats-new.md and test README. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix(mapbox): Update tests to create Deck with default parameters Tests now create Deck with default parameters merged, matching how MapboxOverlay._onAddInterleaved creates the Deck. This is needed because getDeckInstance no longer merges default parameters. Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Remove mapboxLayers tracking from UserData Instead of tracking MapboxLayer instances in a Set, use map.getLayer() to check if a deck layer has a corresponding MapboxLayer on the map. This simplifies the code since resolve-layers already creates MapboxLayer instances with the same ID as the deck layer. Ref: #9944 Co-Authored-By: Claude Opus 4.5 <[email protected]> * refactor(mapbox): Add null guard and consistent signature to MapboxLayer - Add gl parameter to onAdd for consistency with CustomLayerInterface - Add null guard in render() for safety, matching MapboxLayerGroup Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
Followup of #9939
Background
#9939 introduced experimental "batched" rendering mode for
MapboxOverlay, but it's broken as we missed that we have second render hook that now renders everything second time.Change List
MapboxOverlayininterlavedmode when_renderLayersInGroupsis setNote
Eliminates duplicate rendering in interleaved/batched mode by refining external render behavior.
afterRenderto only redraw when non-Mapbox views exist and replaces the layer filter to excludeMAPBOX_VIEW_IDrather than checkingmapboxLayersmapboxLayers-based non-Mapbox layer detection to avoid rendering Mapbox layers twiceWritten by Cursor Bugbot for commit 2f4c199. This will update automatically on new commits. Configure here.