diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index e9055a17c4..ba6333aa2e 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -11,6 +11,7 @@ import { NULL_CELL_CODE } from 'common/buffer/Constants'; import { Terminal, IBufferLine } from 'xterm'; import { IColorSet } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; +import { Disposable, toDisposable } from 'common/Lifecycle'; interface IVertices { attributes: Float32Array; @@ -69,7 +70,7 @@ const INDICES_PER_CELL = 10; const BYTES_PER_CELL = INDICES_PER_CELL * Float32Array.BYTES_PER_ELEMENT; const CELL_POSITION_INDICES = 2; -export class GlyphRenderer { +export class GlyphRenderer extends Disposable { private _atlas: WebglCharAtlas | undefined; private _program: WebGLProgram; @@ -96,9 +97,11 @@ export class GlyphRenderer { private _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions ) { + super(); + const gl = this._gl; - const program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource)); - this._program = program; + this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource)); + this.register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations this._projectionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_projection')); @@ -112,6 +115,7 @@ export class GlyphRenderer { // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); const unitQuadVerticesBuffer = gl.createBuffer(); + this.register(toDisposable(() => gl.deleteBuffer(unitQuadVerticesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW); gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); @@ -121,11 +125,13 @@ export class GlyphRenderer { // unitQuadVertuces to allow is to draw 2 triangles from the vertices const unitQuadElementIndices = new Uint8Array([0, 1, 3, 0, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); + this.register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW); // Setup attributes this._attributesBuffer = throwIfFalsy(gl.createBuffer()); + this.register(toDisposable(() => gl.deleteBuffer(this._attributesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.enableVertexAttribArray(VertexAttribLocations.OFFSET); gl.vertexAttribPointer(VertexAttribLocations.OFFSET, 2, gl.FLOAT, false, BYTES_PER_CELL, 0); @@ -145,6 +151,7 @@ export class GlyphRenderer { // Setup empty texture atlas this._atlasTexture = throwIfFalsy(gl.createTexture()); + this.register(toDisposable(() => gl.deleteTexture(this._atlasTexture))); gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255])); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index 420e58d401..16b1b9c0b3 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -11,6 +11,7 @@ import { IColor } from 'common/Types'; import { IColorSet } from 'browser/Types'; import { IRenderDimensions } from 'browser/renderer/Types'; import { RENDER_MODEL_BG_OFFSET, RENDER_MODEL_FG_OFFSET, RENDER_MODEL_INDICIES_PER_CELL } from './RenderModel'; +import { Disposable, toDisposable } from 'common/Lifecycle'; const enum VertexAttribLocations { POSITION = 0, @@ -57,7 +58,7 @@ const BYTES_PER_RECTANGLE = INDICES_PER_RECTANGLE * Float32Array.BYTES_PER_ELEME const INITIAL_BUFFER_RECTANGLE_CAPACITY = 20 * INDICES_PER_RECTANGLE; -export class RectangleRenderer { +export class RectangleRenderer extends Disposable { private _program: WebGLProgram; private _vertexArrayObject: IWebGLVertexArrayObject; @@ -77,9 +78,12 @@ export class RectangleRenderer { private _gl: IWebGL2RenderingContext, private _dimensions: IRenderDimensions ) { + super(); + const gl = this._gl; this._program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource)); + this.register(toDisposable(() => gl.deleteProgram(this._program))); // Uniform locations this._resolutionLocation = throwIfFalsy(gl.getUniformLocation(this._program, 'u_resolution')); @@ -92,6 +96,7 @@ export class RectangleRenderer { // Setup a_unitquad, this defines the 4 vertices of a rectangle const unitQuadVertices = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]); const unitQuadVerticesBuffer = gl.createBuffer(); + this.register(toDisposable(() => gl.deleteBuffer(unitQuadVerticesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, unitQuadVerticesBuffer); gl.bufferData(gl.ARRAY_BUFFER, unitQuadVertices, gl.STATIC_DRAW); gl.enableVertexAttribArray(VertexAttribLocations.UNIT_QUAD); @@ -101,11 +106,13 @@ export class RectangleRenderer { // unitQuadVertuces to allow is to draw 2 triangles from the vertices const unitQuadElementIndices = new Uint8Array([0, 1, 3, 0, 2, 3]); const elementIndicesBuffer = gl.createBuffer(); + this.register(toDisposable(() => gl.deleteBuffer(elementIndicesBuffer))); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementIndicesBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, unitQuadElementIndices, gl.STATIC_DRAW); // Setup attributes this._attributesBuffer = throwIfFalsy(gl.createBuffer()); + this.register(toDisposable(() => gl.deleteBuffer(this._attributesBuffer))); gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.enableVertexAttribArray(VertexAttribLocations.POSITION); gl.vertexAttribPointer(VertexAttribLocations.POSITION, 2, gl.FLOAT, false, BYTES_PER_RECTANGLE, 0); diff --git a/addons/xterm-addon-webgl/src/WebglAddon.ts b/addons/xterm-addon-webgl/src/WebglAddon.ts index 4db072e86b..4ff31ee987 100644 --- a/addons/xterm-addon-webgl/src/WebglAddon.ts +++ b/addons/xterm-addon-webgl/src/WebglAddon.ts @@ -45,6 +45,7 @@ export class WebglAddon implements ITerminalAddon { const renderService: IRenderService = (this._terminal as any)._core._renderService; renderService.setRenderer((this._terminal as any)._core._createRenderer()); renderService.onResize(this._terminal.cols, this._terminal.rows); + this._renderer?.dispose(); this._renderer = undefined; } diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index ef31a95958..72da37d74b 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -98,8 +98,8 @@ export class WebglRenderer extends Disposable implements IRenderer { this._core.screenElement!.appendChild(this._canvas); - this._rectangleRenderer = new RectangleRenderer(this._terminal, this._colors, this._gl, this.dimensions); - this._glyphRenderer = new GlyphRenderer(this._terminal, this._colors, this._gl, this.dimensions); + this._rectangleRenderer = this.register(new RectangleRenderer(this._terminal, this._colors, this._gl, this.dimensions)); + this._glyphRenderer = this.register(new GlyphRenderer(this._terminal, this._colors, this._gl, this.dimensions)); // Update dimensions and acquire char atlas this.onCharSizeChanged(); diff --git a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts index 619ca1a80c..0a5a0065eb 100644 --- a/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts +++ b/addons/xterm-addon-webgl/src/renderLayer/BaseRenderLayer.ts @@ -40,7 +40,7 @@ export abstract class BaseRenderLayer implements IRenderLayer { } public dispose(): void { - this._container.removeChild(this._canvas); + this._canvas.remove(); if (this._charAtlas) { this._charAtlas.dispose(); } diff --git a/src/common/Lifecycle.ts b/src/common/Lifecycle.ts index 56bcfdcd9e..b3a7cc2189 100644 --- a/src/common/Lifecycle.ts +++ b/src/common/Lifecycle.ts @@ -50,6 +50,13 @@ export abstract class Disposable implements IDisposable { } } +/** + * Wrap a function in a disposable. + */ +export function toDisposable(f: () => void): IDisposable { + return { dispose: f }; +} + /** * Dispose of all disposables in an array and set its length to 0. */