Skip to content

Commit

Permalink
RD-298: Add WebGL context loss warning message (#120)
Browse files Browse the repository at this point in the history
* Add WebGL context loss warning message

* adding GL context loss event

* readme typos

* readme typos
  • Loading branch information
jonathanlurie authored Oct 9, 2024
1 parent 5d3f7a7 commit 35aa952
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 5 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

## NEXT
### New Features

- Shows a warning message in the map container if WebGL context is lost
- The event `"webglContextLost"` is now exposed
- The `Map` class instances now have a `.setTerrainAnimationDuration(d: number)` method
- The `Map` class instances now have events related to terrain animation `"terrainAnimationStart"` and `"terrainAnimationStop"`
- expose the function `getWebGLSupportError()` to detect WebGL compatibility


## 2.3.0
### Bug Fixes
- Updating from MapLibre v4.4.1 to v4.7.0. See Maplibre changelogs for [v4.5.0](https://github.com/maplibre/maplibre-gl-js/blob/main/CHANGELOG.md#450), [v4.5.1](https://github.com/maplibre/maplibre-gl-js/blob/main/CHANGELOG.md#451), [v4.5.2](https://github.com/maplibre/maplibre-gl-js/blob/main/CHANGELOG.md#452), and [v4.6.0](https://github.com/maplibre/maplibre-gl-js/blob/main/CHANGELOG.md#460)
Expand Down
21 changes: 21 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,27 @@ We believe that the *promise* approach is better because it does not nest scopes

> 📣 *__Note:__* Generally speaking, *promises* are not a go to replacement for all event+callback and are suitable only for events that are called only once in the lifecycle of a Map instance. This is the reason why we have decided to provide a *promise* equivalent only for the `load`, `ready` and `loadWithTerrain` events but not for events that may be called multiple time such as interaction events.
### The `webglContextLost` event
The maps is rendered with WebGL, that leverages the GPU to provide high-performance graphics. In some cases, the host machine, operating system or the graphics driver, can decide that continuing to run such high performance graphics is unsustainable, and will abort the process. This is called a "WebGL context loss". Such situation happens when the ressources are running low or when multiple browser tabs are competing to access graphics memory.

The best course of action in such situation varies from an app to another. Sometimes a page refresh is the best thing to do, in other cases, instantiating a new Map dynmicaly at application level is more appropriate because it hides a technical failure to the end user. The event `webglContextLost` is exposed so that the most appropriate scenario can be implemented at application level.

Here is how to respond to a WebGL context loss with a simple page refresh:
```ts

// Init the map
const map = new maptilersdk.Map({
container: "map-container",
hash: true,
})

// Refresh the page if context is lost.
// Since `hash` is true, the location will be the same as before
map.on("webglContextLost", (e) => {
location.reload();
})
```

# Color Ramps
A color ramp is a color gradient defined in a specific interval, for instance in [0, 1], and for any value within this interval will retrieve a color. They are defined by at least a color at each bound and usually additional colors within the range.

Expand Down
11 changes: 10 additions & 1 deletion src/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type { ReferenceMapStyle, MapStyleVariant } from "@maptiler/client";
import { config, MAPTILER_SESSION_ID, type SdkConfig } from "./config";
import { defaults } from "./defaults";
import { MaptilerLogoControl } from "./MaptilerLogoControl";
import { combineTransformRequest, displayNoWebGlWarning } from "./tools";
import { combineTransformRequest, displayNoWebGlWarning, displayWebGLContextLostWarning } from "./tools";
import { getBrowserLanguage, Language, type LanguageInfo } from "./language";
import { styleToStyle } from "./mapstyle";
import { MaptilerTerrainControl } from "./MaptilerTerrainControl";
Expand Down Expand Up @@ -556,6 +556,15 @@ export class Map extends maplibregl.Map {
if (options.terrain) {
this.enableTerrain(options.terrainExaggeration ?? this.terrainExaggeration);
}

// Display a message if WebGL context is lost
this.once("load", () => {
this.getCanvas().addEventListener("webglcontextlost", (e) => {
console.warn(e);
displayWebGLContextLostWarning(options.container);
this.fire("webglContextLost", { error: e });
});
});
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/style/style_template.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
line-height: 14px;
}

.no-webgl-support-div {
.webgl-warning-div {
position: absolute;
top: 0;
left: 0;
Expand Down
27 changes: 26 additions & 1 deletion src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,32 @@ export function displayNoWebGlWarning(container: HTMLElement | string) {

const errorMessageDiv = document.createElement("div");
errorMessageDiv.innerHTML = webglError;
errorMessageDiv.classList.add("no-webgl-support-div");
errorMessageDiv.classList.add("webgl-warning-div");
actualContainer.appendChild(errorMessageDiv);
throw new Error(webglError);
}

/**
* Display an error message in the Map div if WebGL2 is not supported
*/
export function displayWebGLContextLostWarning(container: HTMLElement | string) {
const webglError = "The WebGL context was lost.";

let actualContainer: HTMLElement | null = null;

if (typeof container === "string") {
actualContainer = document.getElementById(container);
} else if (container instanceof HTMLElement) {
actualContainer = container;
}

if (!actualContainer) {
throw new Error("The Map container must be provided.");
}

const errorMessageDiv = document.createElement("div");
errorMessageDiv.innerHTML = webglError;
errorMessageDiv.classList.add("webgl-warning-div");
actualContainer.appendChild(errorMessageDiv);
// throw new Error(webglError);
}

0 comments on commit 35aa952

Please sign in to comment.