diff --git a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts index 1db5c81d..b9b74db2 100644 --- a/addons/xterm-addon-canvas/src/BaseRenderLayer.ts +++ b/addons/xterm-addon-canvas/src/BaseRenderLayer.ts @@ -368,9 +368,9 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer this._ctx.save(); this._clipRow(y); // Draw the image, use the bitmap if it's available - if (this._charAtlas.hasCanvasChanged) { + if (this._charAtlas.pages[0].hasCanvasChanged) { this._bitmapGenerator?.refresh(); - this._charAtlas.hasCanvasChanged = false; + this._charAtlas.pages[0].hasCanvasChanged = false; } this._ctx.drawImage( this._bitmapGenerator?.bitmap || this._charAtlas!.pages[0].canvas, diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index 54afb7a7..7bab8968 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -335,14 +335,14 @@ export class GlyphRenderer extends Disposable { gl.bindBuffer(gl.ARRAY_BUFFER, this._attributesBuffer); gl.bufferData(gl.ARRAY_BUFFER, activeBuffer.subarray(0, bufferLength), gl.STREAM_DRAW); - // Bind the texture atlas if it's changed - if (this._atlas.hasCanvasChanged) { - this._atlas.hasCanvasChanged = false; - // TODO: Make nicer - const layerTextureUnits = new Int32Array([0, 1, 2, 3, 4, 5, 6, 7]); - gl.uniform1iv(this._textureLocation, layerTextureUnits); - // TODO: Only upload the texture(s) that changed - for (let i = 0; i < this._atlas.pages.length; i++) { + // TODO: Only do this once + const layerTextureUnits = new Int32Array([0, 1, 2, 3, 4, 5, 6, 7]); + gl.uniform1iv(this._textureLocation, layerTextureUnits); + + // Bind the atlas page texture if they have changed + for (let i = 0; i < this._atlas.pages.length; i++) { + if (this._atlas.pages[i].hasCanvasChanged) { + this._atlas.pages[i].hasCanvasChanged = false; gl.activeTexture(gl.TEXTURE0 + i); gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i]); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.pages[i].canvas); diff --git a/src/browser/renderer/shared/TextureAtlas.ts b/src/browser/renderer/shared/TextureAtlas.ts index ff5e6db1..018c8f08 100644 --- a/src/browser/renderer/shared/TextureAtlas.ts +++ b/src/browser/renderer/shared/TextureAtlas.ts @@ -47,14 +47,12 @@ export class TextureAtlas implements ITextureAtlas { // The texture that the atlas is drawn to private _pages: AtlasPage[] = []; - public get pages(): { canvas: HTMLCanvasElement }[] { return this._pages; } + public get pages(): { canvas: HTMLCanvasElement, hasCanvasChanged: boolean }[] { return this._pages; } private _tmpCanvas: HTMLCanvasElement; // A temporary context that glyphs are drawn to before being transfered to the atlas. private _tmpCtx: CanvasRenderingContext2D; - public hasCanvasChanged = false; - private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 }; private _workAttributeData: AttributeData = new AttributeData(); @@ -130,7 +128,6 @@ export class TextureAtlas implements ITextureAtlas { this._cacheMap.clear(); this._cacheMapCombined.clear(); this._didWarmUp = false; - this.hasCanvasChanged = true; } public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number): IRasterizedGlyph { @@ -317,8 +314,6 @@ export class TextureAtlas implements ITextureAtlas { // Uncomment for debugging // console.log(`draw to cache "${chars}"`, bg, fg, ext); - this.hasCanvasChanged = true; - // Allow 1 cell width per character, with a minimum of 2 (CJK), plus some padding. This is used // to draw the glyph to the canvas as well as to restrict the bounding box search to ensure // giant ligatures (eg. =====>) don't impact overall performance. @@ -589,6 +584,8 @@ export class TextureAtlas implements ITextureAtlas { } const page = this._pages[this._pages.length - 1]; + page.hasCanvasChanged = true; + const rasterizedGlyph = this._findGlyphBoundingBox(imageData, this._workBoundingBox, allowedWidth, restrictedPowerlineGlyph, customGlyph, padding, page.canvas.width, page.canvas.height); // Find the best atlas row to use @@ -761,6 +758,12 @@ class AtlasPage { public readonly canvas: HTMLCanvasElement; public readonly ctx: CanvasRenderingContext2D; + /** + * Whether the canvas of the atlas page has changed, this is only set to true by the atlas, the + * user of the boolean is required to reset its value to false. + */ + public hasCanvasChanged = false; + // Texture atlas current positioning data. The texture packing strategy used is to fill from // left-to-right and top-to-bottom. When the glyph being written is less than half of the current // row's height, the following happens: @@ -796,6 +799,7 @@ class AtlasPage { this.currentRow.y = 0; this.currentRow.height = 0; this.fixedRows.length = 0; + this.hasCanvasChanged = true; } } diff --git a/src/browser/renderer/shared/Types.d.ts b/src/browser/renderer/shared/Types.d.ts index a622438b..0948aec7 100644 --- a/src/browser/renderer/shared/Types.d.ts +++ b/src/browser/renderer/shared/Types.d.ts @@ -87,9 +87,7 @@ export interface IRenderer extends IDisposable { } export interface ITextureAtlas extends IDisposable { - readonly pages: { canvas: HTMLCanvasElement }[]; - - hasCanvasChanged: boolean; + readonly pages: { canvas: HTMLCanvasElement, hasCanvasChanged: boolean }[]; onAddTextureAtlasCanvas: IEvent;