Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion modules/google-maps/src/google-maps-overlay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export default class GoogleMapsOverlay {

// Create WebGL overlay for camera data (and GL context if interleaved)
const overlay = new google.maps.WebGLOverlayView();
overlay.onAdd = noop;
overlay.onAdd = interleaved ? this._onAddInterleaved.bind(this) : noop;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If onContextRestored is also invoked on first add, doesn't this call _onContextRestored twice?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. On first add Google Maps does fire onContextRestored separately, so this would indeed double-init. The intent here was to cover the re-add case where the context was never lost and onContextRestored is skipped.

Thinking about the cleanest fix: track an _overlayInitialized flag per overlay instance that _onContextRestored sets (and _onContextLost/_onRemove clears), then defer the fallback via queueMicrotask so Google Maps' own onContextRestored can fire first. If it does, the flag gates out the duplicate. If it doesn't (the re-add case), we fetch the existing context.

Does that approach sound right to you before I push?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am still not convinced. As I commented before:

onRemove does not delete this._deck, only hides it.
Only places where this._deck is set to null is in onContextLost and finalize.
So either you are calling finalize in your own app, after which you should not call setMap again; or Google Maps is invoking onContextLost but not onContextRestored. Neither of these scenarios are for this component to handle.

overlay.onContextRestored = interleaved ? this._onContextRestored.bind(this) : noop;
overlay.onDraw = this._onDrawVector.bind(this);
overlay.onContextLost = interleaved ? this._onContextLost.bind(this) : noop;
Expand Down Expand Up @@ -205,6 +205,23 @@ export default class GoogleMapsOverlay {
this._deck = createDeckInstance(this._map, overlay, this._deck, this.props);
}

_onAddInterleaved() {
// When a WebGLOverlayView is removed and a new one is added to the same map,
// Google Maps may skip onContextRestored because the WebGL context was never
// actually lost. In that case, retrieve the existing GL context from the map's
// canvas and initialize the deck instance here in onAdd.
if (!this._map) {
return;
}
const mapCanvas = this._map.getDiv().querySelector('canvas');
if (mapCanvas) {
const gl = mapCanvas.getContext('webgl2');
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very suspicious... If the underlying context has not been created for any reason, this will return a new WebGL context AND break the base map because getContext is not called with any expected options.

Copy link
Copy Markdown
Contributor Author

@officialasishkumar officialasishkumar Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair concern. HTMLCanvasElement.getContext('webgl2') is supposed to return the existing context if one was already created on that canvas (which Google Maps does before onAdd runs). But the moment the invariant breaks, we'd spuriously create a fresh context without the options Google Maps needs, which is exactly what you're flagging.

Two options I can take:

  1. Drop this path entirely and instead trigger this._overlay.requestRedraw() right after the re-add, letting the normal onContextRestored lifecycle handle it.
  2. Keep the canvas lookup but guard with a check that the canvas already has an active gl context before calling getContext (e.g. inspect canvas attributes or skip if any unexpected state), and only fire once via the _overlayInitialized flag I mentioned on the other thread.

(1) feels safer to me if requestRedraw reliably triggers onContextRestored on a re-add.

if (gl) {
this._onContextRestored({gl});
}
}
}

_updateContainerSize() {
// Update positioning container size and position to match map
if (!this._map) return;
Expand Down