From 7d1efd1741c0469546e90953b1aa97cc542f990c Mon Sep 17 00:00:00 2001 From: Jonathan Lurie Date: Wed, 2 Oct 2024 15:20:38 +0200 Subject: [PATCH 1/4] Add WebGL context loss warning message --- CHANGELOG.md | 4 ++++ src/Map.ts | 10 +++++++++- src/style/style_template.css | 2 +- src/tools.ts | 27 ++++++++++++++++++++++++++- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f187768..0470b37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # MapTiler SDK Changelog +## NEXT +### New Features +- Shows a warning message in the map container if WebGL context is lost + ## 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) diff --git a/src/Map.ts b/src/Map.ts index de99c3f..25ce6b5 100644 --- a/src/Map.ts +++ b/src/Map.ts @@ -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"; @@ -555,6 +555,14 @@ 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); + }); + }); } /** diff --git a/src/style/style_template.css b/src/style/style_template.css index 7433943..600b79b 100644 --- a/src/style/style_template.css +++ b/src/style/style_template.css @@ -145,7 +145,7 @@ line-height: 14px; } -.no-webgl-support-div { +.webgl-warning-div { position: absolute; top: 0; left: 0; diff --git a/src/tools.ts b/src/tools.ts index c5a9d82..35b0192 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -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, please refresh the page to continue."; + + 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); } From 1d0ccd57c6912fda1130adb0ea8fd8bfa13f10ce Mon Sep 17 00:00:00 2001 From: Jonathan Lurie Date: Thu, 3 Oct 2024 12:13:08 +0200 Subject: [PATCH 2/4] adding GL context loss event --- CHANGELOG.md | 1 + readme.md | 21 +++++++++++++++++++++ src/Map.ts | 1 + src/tools.ts | 4 ++-- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0470b37..e58f4b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## NEXT ### New Features - Shows a warning message in the map container if WebGL context is lost +- The event `"webglContextLost"` is now exposed ## 2.3.0 ### Bug Fixes diff --git a/readme.md b/readme.md index 27758cc..d40e48d 100644 --- a/readme.md +++ b/readme.md @@ -565,6 +565,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 leverage 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 response to 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 the best course of action to hide a technical failure to the end user. + +Here is how to respond to a WebGL 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. diff --git a/src/Map.ts b/src/Map.ts index 25ce6b5..a84b2ff 100644 --- a/src/Map.ts +++ b/src/Map.ts @@ -561,6 +561,7 @@ export class Map extends maplibregl.Map { this.getCanvas().addEventListener("webglcontextlost", (e) => { console.warn(e); displayWebGLContextLostWarning(options.container); + this.fire("webglContextLost", { error: e }); }); }); } diff --git a/src/tools.ts b/src/tools.ts index 35b0192..fac3ee4 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -204,7 +204,7 @@ export function displayNoWebGlWarning(container: HTMLElement | string) { * 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, please refresh the page to continue."; + const webglError = "The WebGL context was lost."; let actualContainer: HTMLElement | null = null; @@ -222,5 +222,5 @@ export function displayWebGLContextLostWarning(container: HTMLElement | string) errorMessageDiv.innerHTML = webglError; errorMessageDiv.classList.add("webgl-warning-div"); actualContainer.appendChild(errorMessageDiv); - throw new Error(webglError); + // throw new Error(webglError); } From 48047764a9e8b5ba0ca19c352b63e7bb3716fa58 Mon Sep 17 00:00:00 2001 From: Jonathan Lurie Date: Thu, 3 Oct 2024 13:24:15 +0200 Subject: [PATCH 3/4] readme typos --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index d40e48d..3e2b8dc 100644 --- a/readme.md +++ b/readme.md @@ -566,9 +566,9 @@ 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 leverage 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 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 response to 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 the best course of action to hide a technical failure to the end user. +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 most appropriate scenario can be implemented at application level. Here is how to respond to a WebGL loss with a simple page refresh: ```ts From 835fdebd098896f7578ea2fdaf78f4750de4ea97 Mon Sep 17 00:00:00 2001 From: Jonathan Lurie Date: Thu, 3 Oct 2024 13:26:00 +0200 Subject: [PATCH 4/4] readme typos --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 3e2b8dc..1484f7d 100644 --- a/readme.md +++ b/readme.md @@ -568,9 +568,9 @@ We believe that the *promise* approach is better because it does not nest scopes ### 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 most appropriate scenario can be implemented at application level. +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 loss with a simple page refresh: +Here is how to respond to a WebGL context loss with a simple page refresh: ```ts // Init the map