Signatur
Beschreibung
Die Erweiterung WEBGL_lose_context gehört zur WebGL API. Mit ihr lässt sich der Verlust eines WebGLRenderingContext bzw. WebGL2RenderingContext nachbilden und der Kontext danach wiederherstellen. Ein echter Kontextverlust passiert z. B. nach einem Absturz des GPU-Treibers, beim Wechsel der Grafikkarte, bei zu hohem Speicherverbrauch oder wenn der Browser zu viele aktive WebGL-Kontexte hat. Robuste Anwendungen müssen auf diesen Fall reagieren. Mit der Erweiterung kann man das gezielt testen.
Das Erweiterungsobjekt bekommt man über gl.getExtension('WEBGL_lose_context'). Es hat zwei Methoden:
loseContext()– simuliert den Verlust des Kontexts. Auf dem Canvas wird das Ereigniswebglcontextlostausgelöst.restoreContext()– simuliert die Wiederherstellung. Danach wirdwebglcontextrestoredausgelöst.
Die Methoden haben keine Parameter und geben undefined zurück. Nach einem Verlust sind alle WebGL-Objekte ungültig: Texturen, Puffer, Shader, Programme und Framebuffer. Im Handler für webglcontextrestored muss die Anwendung sie komplett neu anlegen. Damit eine Wiederherstellung überhaupt möglich ist, muss im Handler für webglcontextlost die Methode event.preventDefault() aufgerufen werden.
In der Praxis wird loseContext() auch genutzt, um GPU-Ressourcen sofort freizugeben, wenn ein Canvas nicht mehr gebraucht wird, z. B. beim Abbauen einer Komponente in einer Single-Page-App. Darauf verlassen sich unter anderem Three.js (renderer.forceContextLoss()) und andere Bibliotheken.
Rückgabewert
gl.getExtension('WEBGL_lose_context') gibt das Erweiterungsobjekt zurück. Wenn die Erweiterung nicht unterstützt wird oder der Kontext bereits verloren ist, gibt der Aufruf null zurück.Beispiele
Erweiterung abrufen und Kontextverlust simulieren
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
const ext = gl.getExtension('WEBGL_lose_context');
canvas.addEventListener('webglcontextlost', (event) => {
event.preventDefault(); // Voraussetzung für die Wiederherstellung
console.log('Kontext verloren:', gl.isContextLost());
});
canvas.addEventListener('webglcontextrestored', () => {
console.log('Kontext wiederhergestellt:', !gl.isContextLost());
});
ext.loseContext();
// Nach einer Sekunde wiederherstellen
setTimeout(() => ext.restoreContext(), 1000);
Ressourcen nach Wiederherstellung neu anlegen
const canvas = document.querySelector('canvas');
const gl = canvas.getContext('webgl');
let buffer = null;
const initResources = () => {
buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 1, -1, -1, 1, -1]), gl.STATIC_DRAW);
console.log('Ressourcen initialisiert');
};
let frameId = 0;
const render = () => {
gl.clearColor(0.1, 0.2, 0.3, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
frameId = requestAnimationFrame(render);
};
canvas.addEventListener('webglcontextlost', (e) => {
e.preventDefault();
cancelAnimationFrame(frameId); // Render-Schleife anhalten
buffer = null; // alte Objekte sind ungültig
});
canvas.addEventListener('webglcontextrestored', () => {
initResources();
render();
});
initResources();
render();
// Testweise Verlust auslösen
const ext = gl.getExtension('WEBGL_lose_context');
ext?.loseContext();
setTimeout(() => ext?.restoreContext(), 500);
GPU-Speicher beim Aufräumen sofort freigeben
const destroyWebGLCanvas = (canvas) => {
const gl = canvas.getContext('webgl2') ?? canvas.getContext('webgl');
gl?.getExtension('WEBGL_lose_context')?.loseContext();
canvas.width = canvas.height = 0; // Zeichenpuffer verkleinern
canvas.remove();
console.log('Canvas entfernt, Kontext freigegeben');
};
destroyWebGLCanvas(document.querySelector('#viewer'));
// Wichtig · Fallstricke
- preventDefault() ist Pflicht: Wird im Handler für
webglcontextlostnichtevent.preventDefault()aufgerufen, stellt der Browser den Kontext nicht wieder her.restoreContext()löst dann den FehlerINVALID_OPERATIONaus. - Falsche Reihenfolge:
restoreContext()ohne vorherigen Verlust erzeugt ebenfallsINVALID_OPERATION. Ein zweiter Aufruf vonloseContext()auf einem bereits verlorenen Kontext hat keine Wirkung. - Erweiterung früh abrufen: Das Erweiterungsobjekt sollte man vor dem Verlust holen und aufbewahren. Nach dem Verlust gibt
getExtension()nullzurück. - Ereignisse kommen asynchron:
webglcontextrestoredwird nicht synchron ausgelöst. Neu anlegen sollte man Ressourcen deshalb nur im Ereignis-Handler. - Alle Objekte ungültig: Nach dem Verlust gibt jeder WebGL-Aufruf still Standardwerte zurück (z. B.
nulloder0). Mitgl.isContextLost()lässt sich der Zustand prüfen. - Kompatibilität: Die Erweiterung wird von allen modernen Browsern für WebGL 1 und WebGL 2 unterstützt. Trotzdem sollte man den Rückgabewert auf
nullprüfen, z. B. mit optionaler Verkettung?..