Skip to content

Commit 151a5d8

Browse files
authored
Merge pull request #4075 from kenrussell/context-restoration
Handle WebGL context loss/restore events in WebGL renderer.
2 parents ef759a6 + f309aff commit 151a5d8

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

addons/xterm-addon-webgl/src/WebglRenderer.ts

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,14 @@ export class WebglRenderer extends Disposable implements IRenderer {
4646

4747
private _canvas: HTMLCanvasElement;
4848
private _gl: IWebGL2RenderingContext;
49-
private _rectangleRenderer: RectangleRenderer;
50-
private _glyphRenderer: GlyphRenderer;
49+
private _rectangleRenderer!: RectangleRenderer;
50+
private _glyphRenderer!: GlyphRenderer;
5151

5252
public dimensions: IRenderDimensions;
5353

5454
private _core: ITerminal;
5555
private _isAttached: boolean;
56+
private _contextRestorationTimeout: number | undefined;
5657

5758
private _onChangeTextureAtlas = new EventEmitter<HTMLCanvasElement>();
5859
public get onChangeTextureAtlas(): IEvent<HTMLCanvasElement> { return this._onChangeTextureAtlas.event; }
@@ -108,16 +109,34 @@ export class WebglRenderer extends Disposable implements IRenderer {
108109
throw new Error('WebGL2 not supported ' + this._gl);
109110
}
110111

111-
this.register(addDisposableDomListener(this._canvas, 'webglcontextlost', (e) => { this._onContextLoss.fire(e); }));
112+
this.register(addDisposableDomListener(this._canvas, 'webglcontextlost', (e) => {
113+
console.log('webglcontextlost event received');
114+
// Prevent the default behavior in order to enable WebGL context restoration.
115+
e.preventDefault();
116+
// Wait a few seconds to see if the 'webglcontextrestored' event is fired.
117+
// If not, dispatch the onContextLoss notification to observers.
118+
this._contextRestorationTimeout = setTimeout(() => {
119+
this._contextRestorationTimeout = undefined;
120+
console.warn('webgl context not restored; firing onContextLoss');
121+
this._onContextLoss.fire(e);
122+
}, 3000 /* ms */);
123+
}));
124+
this.register(addDisposableDomListener(this._canvas, 'webglcontextrestored', (e) => {
125+
console.warn('webglcontextrestored event received');
126+
clearTimeout(this._contextRestorationTimeout);
127+
this._contextRestorationTimeout = undefined;
128+
// The texture atlas and glyph renderer must be fully reinitialized
129+
// because their contents have been lost.
130+
removeTerminalFromCache(this._terminal);
131+
this._initializeWebGLState();
132+
this._requestRedrawViewport();
133+
}));
134+
112135
this.register(observeDevicePixelDimensions(this._canvas, (w, h) => this._setCanvasDevicePixelDimensions(w, h)));
113136

114137
this._core.screenElement!.appendChild(this._canvas);
115138

116-
this._rectangleRenderer = this.register(new RectangleRenderer(this._terminal, this._colors, this._gl, this.dimensions));
117-
this._glyphRenderer = this.register(new GlyphRenderer(this._terminal, this._colors, this._gl, this.dimensions));
118-
119-
// Update dimensions and acquire char atlas
120-
this.onCharSizeChanged();
139+
this._initializeWebGLState();
121140

122141
this._isAttached = document.body.contains(this._core.screenElement!);
123142
}
@@ -235,6 +254,21 @@ export class WebglRenderer extends Disposable implements IRenderer {
235254
this._refreshCharAtlas();
236255
}
237256

257+
/**
258+
* Initializes members dependent on WebGL context state.
259+
*/
260+
private _initializeWebGLState(): void {
261+
// Dispose any previous rectangle and glyph renderers before creating new ones.
262+
this._rectangleRenderer?.dispose();
263+
this._glyphRenderer?.dispose();
264+
265+
this._rectangleRenderer = new RectangleRenderer(this._terminal, this._colors, this._gl, this.dimensions);
266+
this._glyphRenderer = new GlyphRenderer(this._terminal, this._colors, this._gl, this.dimensions);
267+
268+
// Update dimensions and acquire char atlas
269+
this.onCharSizeChanged();
270+
}
271+
238272
/**
239273
* Refreshes the char atlas, aquiring a new one if necessary.
240274
* @param terminal The terminal.

demo/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ function htmlSerializeButtonHandler(): void {
606606
}
607607

608608
function addTextureAtlas(e: HTMLCanvasElement) {
609-
document.querySelector('#texture-atlas').appendChild(e);
609+
document.querySelector('#texture-atlas').replaceChildren(e);
610610
}
611611

612612
function writeCustomGlyphHandler() {

0 commit comments

Comments
 (0)