Skip to content

Commit

Permalink
[GLJS-1120] Fix a blank map issue after WebGL context loss (internal-…
Browse files Browse the repository at this point in the history
…2150)
  • Loading branch information
underoot committed Jan 30, 2025
1 parent e562bd8 commit 4a82ff7
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 9 deletions.
37 changes: 31 additions & 6 deletions 3d-style/render/model_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ class ModelManager extends Evented {
[id: string]: ReferencedModel;
};
};
modelUris: {
[scope: string]: {
[id: string]: string;
}
};
numModelsLoading: {
[scope: string]: number;
};
Expand All @@ -30,6 +35,7 @@ class ModelManager extends Evented {
super();
this.requestManager = requestManager;
this.models = {'': {}};
this.modelUris = {'': {}};
this.numModelsLoading = {};
}

Expand All @@ -53,7 +59,11 @@ class ModelManager extends Evented {

load(modelUris: {
[key: string]: string;
}, scope: string) {
}, scope: string, options: {
keepNumReferences?: boolean
} = {
keepNumReferences: false
}) {
if (!this.models[scope]) this.models[scope] = {};

const modelIds = Object.keys(modelUris);
Expand All @@ -70,7 +80,9 @@ class ModelManager extends Evented {
// @ts-expect-error - TS2339 - Property 'value' does not exist on type 'PromiseSettledResult<any>'.
const {status, value} = results[i];
if (status === 'fulfilled' && value) {
this.models[scope][modelIds[i]] = {model: value, numReferences : 1};
const previousModel = this.models[scope][modelIds[i]];
const numReferences = options.keepNumReferences && previousModel ? previousModel.numReferences : 1;
this.models[scope][modelIds[i]] = {model: value, numReferences};
}
}
this.numModelsLoading[scope] -= modelIds.length;
Expand Down Expand Up @@ -99,35 +111,47 @@ class ModelManager extends Evented {

addModel(id: string, url: string, scope: string) {
if (!this.models[scope]) this.models[scope] = {};
if (!this.modelUris[scope]) this.modelUris[scope] = {};

// update num references if the model exists
if (this.hasModel(id, scope)) {
this.models[scope][id].numReferences++;
}
this.load({[id]: this.requestManager.normalizeModelURL(url)}, scope);

this.modelUris[scope][id] = this.requestManager.normalizeModelURL(url);

this.load({[id]: this.modelUris[scope][id]}, scope);
}

addModels(models: ModelsSpecification, scope: string) {
if (!this.models[scope]) this.models[scope] = {};
if (!this.modelUris[scope]) this.modelUris[scope] = {};

const modelUris: Record<string, any> = {};
const modelUris: Record<string, any> = this.modelUris[scope];
for (const modelId in models) {
// Add a void object so we mark this model as requested
// @ts-expect-error - TS2739 - Type '{}' is missing the following properties from type 'ReferencedModel': model, numReferences
this.models[scope][modelId] = {};
modelUris[modelId] = this.requestManager.normalizeModelURL(models[modelId]);
}
this.load(modelUris, scope);
this.load(modelUris, scope, {keepNumReferences: true});
}

reloadModels(scope: string) {
this.load(this.modelUris[scope], scope);
}

addModelsFromBucket(modelUris: Array<string>, scope: string) {
if (!this.models[scope]) this.models[scope] = {};
if (!this.modelUris[scope]) this.modelUris[scope] = {};

const modelsRequests: Record<string, any> = {};
for (const modelUri of modelUris) {
if (this.hasModel(modelUri, scope)) {
this.models[scope][modelUri].numReferences++;
} else {
modelsRequests[modelUri] = this.requestManager.normalizeModelURL(modelUri);
this.modelUris[scope][modelUri] = this.requestManager.normalizeModelURL(modelUri);
modelsRequests[modelUri] = this.modelUris[scope][modelUri];
}
}
this.load(modelsRequests, scope);
Expand All @@ -139,6 +163,7 @@ class ModelManager extends Evented {
if (this.models[scope][id].numReferences === 0) {
const model = this.models[scope][id].model;
delete this.models[scope][id];
delete this.modelUris[scope][id];
model.destroy();
}
}
Expand Down
14 changes: 13 additions & 1 deletion 3d-style/source/tiled_3d_model_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import loadTileJSON from '../../src/source/load_tilejson';
import TileBounds from '../../src/source/tile_bounds';
import {extend} from '../../src/util/util';
import {postTurnstileEvent} from '../../src/util/mapbox';
import {makeFQID} from '../../src/util/fqid';

// Import Tiled3dModelBucket as a module with side effects to ensure
// it's registered as a serializable class on the main thread
Expand Down Expand Up @@ -49,7 +50,6 @@ class Tiled3DModelSource extends Evented<SourceEvents> implements ISource {
map: Map;

onRemove: undefined;
reload: undefined;
abortTile: undefined;
unloadTile: undefined;
prepare: undefined;
Expand Down Expand Up @@ -82,6 +82,18 @@ class Tiled3DModelSource extends Evented<SourceEvents> implements ISource {
this.load();
}

reload() {
this.cancelTileJSONRequest();
const fqid = makeFQID(this.id, this.scope);
this.load(() => this.map.style.clearSource(fqid));
}

cancelTileJSONRequest() {
if (!this._tileJSONRequest) return;
this._tileJSONRequest.cancel();
this._tileJSONRequest = null;
}

load(callback?: Callback<undefined>) {
this._loaded = false;
this.fire(new Event('dataloading', {dataType: 'source'}));
Expand Down
8 changes: 7 additions & 1 deletion src/source/geojson_source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {extend} from '../util/util';
import EXTENT from '../style-spec/data/extent';
import {ResourceType} from '../util/ajax';
import browser from '../util/browser';
import {makeFQID} from '../util/fqid';

import type {ISource, SourceEvents} from './source';
import type {Map as MapboxMap} from '../ui/map';
Expand Down Expand Up @@ -94,7 +95,6 @@ class GeoJSONSource extends Evented<SourceEvents> implements ISource {
_pendingLoad: Cancelable | null | undefined;
_partialReload: boolean;

reload: undefined;
hasTile: undefined;
prepare: undefined;
afterUpdate: undefined;
Expand Down Expand Up @@ -429,6 +429,12 @@ class GeoJSONSource extends Evented<SourceEvents> implements ISource {
return this._loaded;
}

reload() {
const fqid = makeFQID(this.id, this.scope);
this.map.style.clearSource(fqid);
this._updateWorkerData();
}

loadTile(tile: Tile, callback: Callback<undefined>) {
const message = !tile.actor ? 'loadTile' : 'reloadTile';
tile.actor = this.actor;
Expand Down
7 changes: 7 additions & 0 deletions src/style/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3727,6 +3727,13 @@ class Style extends Evented<MapEvents> {
}
}

reloadModels() {
this.modelManager.reloadModels('');
this.forEachFragmentStyle((style) => {
style.modelManager.reloadModels(style.scope);
});
}

updateSources(transform: Transform) {
let lightDirection: vec3 | null | undefined;
if (this.directionalLight) {
Expand Down
5 changes: 4 additions & 1 deletion src/ui/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4163,7 +4163,10 @@ export class Map extends Camera {

_contextRestored(event: any) {
this._setupPainter();
this.resize();
this.painter.resize(Math.ceil(this._containerWidth), Math.ceil(this._containerHeight));
this._updateTerrain();
this.style.reloadModels();
this.style.clearSources();
this._update();
this.fire(new Event('webglcontextrestored', {originalEvent: event}));
}
Expand Down
12 changes: 12 additions & 0 deletions test/integration/lib/operation-handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ export const operationHandlers = {

waitForRender(map, () => map.loaded(), doneCb);
},
forceContextRestart(map, params, doneCb) {
const canvas = map.getCanvas();
const ext = map.painter.context.gl.getExtension('WEBGL_lose_context');
canvas.addEventListener('webglcontextlost', (e) => {
e.preventDefault();
setTimeout(() => {
ext.restoreContext();
doneCb();
});
});
ext.loseContext();
},
waitFrameReady(map, params, doneCb) {
let timeIterationInterval = 0;
if (params.length) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
148 changes: 148 additions & 0 deletions test/integration/render-tests/context-restore/style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{
"version": 8,
"metadata": {
"test": {
"width": 128,
"height": 128,
"allowed": 0.003,
"operations": [
["wait"],
["forceContextRestart"],
["wait"]
]
}
},
"sprite": "local://sprites/sprite",
"glyphs": "local://glyphs/{fontstack}/{range}.pbf",
"models": {
"maple1": "local://models/maple1-lod2.glb",
"maple2": "local://models/maple2-lod2.glb",
"oak1": "local://models/oak1-lod2.glb",
"oak2": "local://models/oak2-lod2.glb"
},
"sources": {
"mapbox": {
"type": "vector",
"maxzoom": 15,
"tiles": [
"local://tiles/{z}-{x}-{y}.vector.pbf"
]
},
"trees": {
"type": "vector",
"maxzoom": 15,
"tiles": [
"local://tiles/trees/{z}-{x}-{y}.pbf"
]
},
"symbols": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
-122.40258704262851,
37.784333172276225
],
"type": "Point"
}
}
]
}
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "lightgray"
}
},
{
"id": "land",
"type": "fill",
"source": "mapbox",
"source-layer": "water",
"paint": {
"fill-color": "lightblue"
}
},
{
"id": "road",
"type": "line",
"source": "mapbox",
"source-layer": "road",
"paint": {
"line-color": "lightyellow",
"line-width": 10,
"line-emissive-strength": 1
}
},
{
"id": "tree-layer",
"type": "model",
"source": "trees",
"source-layer": "trees",
"layout" : {
"model-id":
["match", ["%", ["id"], 4],
0, "maple1",
1, "maple2",
3, "oak1",
"oak2"]
},
"paint": {
"model-rotation": ["match", ["%", ["id"], 4],
0, ["literal", [0.0, 0.0, 0.0]],
1, ["literal", [0.0, 0.0, 50.0]],
2, ["literal", [10.0, 0.0, 120.0]],
["literal", [0.0, -3.0, -60]]],
"model-scale": ["match", ["%", ["id"], 3],
0, ["literal", [3.0, 3.25, 3.0]],
1, ["literal", [1.8, 1.9, 1.8]],
["literal", [5.2, 5.1, 5.1]]],
"model-color": [
"case",
["boolean", ["feature-state", "hover"], false],
"blue",
["match", ["%", ["id"], 6],
0, "orange",
1, "gray",
2, "white",
3, "pink",
4, "yellow",
"green"]
],
"model-color-mix-intensity": 0.2,
"model-cutoff-fade-range": 0.2
}
},
{
"id": "geometry",
"type": "symbol",
"source": "symbols",
"layout": {
"icon-image": "rocket-12",
"text-field": "Mapbox",
"text-font": [
"Open Sans Semibold",
"Arial Unicode MS Bold"
],
"text-allow-overlap": true,
"text-ignore-placement": true,
"text-offset": [0, 1]
}
}
],
"zoom": 15.1,
"bearing": 264,
"center": [
-122.4027,
37.7845
]
}

0 comments on commit 4a82ff7

Please sign in to comment.