From efba2c9040c1ce0490f65c7bc3d474ea46fdba42 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 28 Oct 2022 19:30:42 -0700 Subject: [PATCH] Get pages working, add to demo --- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 48 +++++++++---------- addons/xterm-addon-webgl/src/WebglAddon.ts | 5 +- addons/xterm-addon-webgl/src/WebglRenderer.ts | 6 ++- .../typings/xterm-addon-webgl.d.ts | 3 ++ demo/client.ts | 18 ++++--- src/browser/renderer/shared/TextureAtlas.ts | 9 +++- src/browser/renderer/shared/Types.d.ts | 2 + 7 files changed, 57 insertions(+), 34 deletions(-) diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index 823a0db1..54afb7a7 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -62,7 +62,7 @@ precision lowp float; in vec2 v_texcoord; flat in int v_texpage; -uniform sampler2D u_texture[2]; +uniform sampler2D u_texture[8]; out vec4 outColor; @@ -71,6 +71,18 @@ void main() { outColor = texture(u_texture[0], v_texcoord); } else if (v_texpage == 1) { outColor = texture(u_texture[1], v_texcoord); + } else if (v_texpage == 2) { + outColor = texture(u_texture[2], v_texcoord); + } else if (v_texpage == 3) { + outColor = texture(u_texture[3], v_texcoord); + } else if (v_texpage == 4) { + outColor = texture(u_texture[4], v_texcoord); + } else if (v_texpage == 5) { + outColor = texture(u_texture[5], v_texcoord); + } else if (v_texpage == 6) { + outColor = texture(u_texture[6], v_texcoord); + } else if (v_texpage == 7) { + outColor = texture(u_texture[7], v_texcoord); } }`; @@ -327,23 +339,17 @@ export class GlyphRenderer extends Disposable { if (this._atlas.hasCanvasChanged) { this._atlas.hasCanvasChanged = false; // TODO: Make nicer - const layerTextureUnits = new Int32Array([0, 1]); + const layerTextureUnits = new Int32Array([0, 1, 2, 3, 4, 5, 6, 7]); gl.uniform1iv(this._textureLocation, layerTextureUnits); - gl.activeTexture(gl.TEXTURE0 + 0); - gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[0]); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.pages[0].canvas); - gl.generateMipmap(gl.TEXTURE_2D); - - if (this._atlas.pages.length > 1) { - // TODO: Check if the particular texture page changed - gl.activeTexture(gl.TEXTURE0 + 1); - gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[1]); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.pages[1].canvas); + // TODO: Only upload the texture(s) that changed + for (let i = 0; i < this._atlas.pages.length; i++) { + 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); gl.generateMipmap(gl.TEXTURE_2D); } } - // Set uniforms gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); gl.uniform2f(this._resolutionLocation, gl.canvas.width, gl.canvas.height); @@ -356,19 +362,13 @@ export class GlyphRenderer extends Disposable { const gl = this._gl; this._atlas = atlas; - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[0]); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[0].canvas); - gl.generateMipmap(gl.TEXTURE_2D); - - if (atlas.pages.length > 1) { - gl.activeTexture(gl.TEXTURE0 + 1); - gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[1]); + // TODO: Share code + for (let i = 0; i < this._atlas.pages.length; i++) { + gl.activeTexture(gl.TEXTURE0 + i); + gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i]); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, atlas.pages[1].canvas); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.pages[i].canvas); gl.generateMipmap(gl.TEXTURE_2D); } } diff --git a/addons/xterm-addon-webgl/src/WebglAddon.ts b/addons/xterm-addon-webgl/src/WebglAddon.ts index 4f4c9dcc..5262da8e 100644 --- a/addons/xterm-addon-webgl/src/WebglAddon.ts +++ b/addons/xterm-addon-webgl/src/WebglAddon.ts @@ -17,8 +17,10 @@ export class WebglAddon extends Disposable implements ITerminalAddon { private _terminal?: Terminal; private _renderer?: WebglRenderer; - private readonly _onChangeTextureAtlas = this.register(new EventEmitter()); + private readonly _onChangeTextureAtlas = this.register(new EventEmitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; + private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter()); + public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; private readonly _onContextLoss = this.register(new EventEmitter()); public readonly onContextLoss = this._onContextLoss.event; @@ -64,6 +66,7 @@ export class WebglAddon extends Disposable implements ITerminalAddon { )); this.register(forwardEvent(this._renderer.onContextLoss, this._onContextLoss)); this.register(forwardEvent(this._renderer.onChangeTextureAtlas, this._onChangeTextureAtlas)); + this.register(forwardEvent(this._renderer.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas)); renderService.setRenderer(this._renderer); this.register(toDisposable(() => { diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index 35542bcf..636be672 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -14,7 +14,7 @@ import { ITerminal } from 'browser/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { CellData } from 'common/buffer/CellData'; import { Content, NULL_CELL_CHAR, NULL_CELL_CODE } from 'common/buffer/Constants'; -import { EventEmitter } from 'common/EventEmitter'; +import { EventEmitter, forwardEvent } from 'common/EventEmitter'; import { Disposable, toDisposable } from 'common/Lifecycle'; import { ICoreService, IDecorationService, IOptionsService } from 'common/services/Services'; import { CharData, IBufferLine, ICellData } from 'common/Types'; @@ -49,6 +49,8 @@ export class WebglRenderer extends Disposable implements IRenderer { private readonly _onChangeTextureAtlas = this.register(new EventEmitter()); public readonly onChangeTextureAtlas = this._onChangeTextureAtlas.event; + private readonly _onAddTextureAtlasCanvas = this.register(new EventEmitter()); + public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; private readonly _onRequestRedraw = this.register(new EventEmitter()); public readonly onRequestRedraw = this._onRequestRedraw.event; private readonly _onContextLoss = this.register(new EventEmitter()); @@ -262,6 +264,8 @@ export class WebglRenderer extends Disposable implements IRenderer { ); if (this._charAtlas !== atlas) { this._onChangeTextureAtlas.fire(atlas.pages[0].canvas); + // TODO: Dispose this when there's a new atlas + forwardEvent(atlas.onAddTextureAtlasCanvas, this._onAddTextureAtlasCanvas); } this._charAtlas = atlas; this._charAtlas.warmUp(); diff --git a/addons/xterm-addon-webgl/typings/xterm-addon-webgl.d.ts b/addons/xterm-addon-webgl/typings/xterm-addon-webgl.d.ts index 6865b6db..a6afd2ac 100644 --- a/addons/xterm-addon-webgl/typings/xterm-addon-webgl.d.ts +++ b/addons/xterm-addon-webgl/typings/xterm-addon-webgl.d.ts @@ -22,6 +22,9 @@ declare module 'xterm-addon-webgl' { */ public readonly onChangeTextureAtlas: IEvent; + // TODO: Doc + public readonly onAddTextureAtlasCanvas: IEvent; + constructor(preserveDrawingBuffer?: boolean); /** diff --git a/demo/client.ts b/demo/client.ts index 5b561a8c..3829b6dd 100644 --- a/demo/client.ts +++ b/demo/client.ts @@ -273,8 +273,9 @@ function createTerminal(): void { typedTerm.loadAddon(addons.webgl.instance); setTimeout(() => { if (addons.webgl.instance !== undefined) { - addTextureAtlas(addons.webgl.instance.textureAtlas); - addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e)); + setTextureAtlas(addons.webgl.instance.textureAtlas); + addons.webgl.instance.onChangeTextureAtlas(e => setTextureAtlas(e)); + addons.webgl.instance.onAddTextureAtlasCanvas(e => appendTextureAtlas(e)); } }, 0); @@ -551,13 +552,13 @@ function initAddons(term: TerminalType): void { term.loadAddon(addon.instance); if (name === 'webgl') { setTimeout(() => { - addTextureAtlas(addons.webgl.instance.textureAtlas); - addons.webgl.instance.onChangeTextureAtlas(e => addTextureAtlas(e)); + setTextureAtlas(addons.webgl.instance.textureAtlas); + addons.webgl.instance.onChangeTextureAtlas(e => setTextureAtlas(e)); }, 0); } else if (name === 'canvas') { setTimeout(() => { - addTextureAtlas(addons.canvas.instance.textureAtlas); - addons.canvas.instance.onChangeTextureAtlas(e => addTextureAtlas(e)); + setTextureAtlas(addons.canvas.instance.textureAtlas); + addons.canvas.instance.onChangeTextureAtlas(e => setTextureAtlas(e)); }, 0); } else if (name === 'unicode11') { term.unicode.activeVersion = '11'; @@ -648,9 +649,12 @@ function htmlSerializeButtonHandler(): void { document.getElementById('htmlserialize-output-result').innerText = 'Copied to clipboard'; } -function addTextureAtlas(e: HTMLCanvasElement): void { +function setTextureAtlas(e: HTMLCanvasElement): void { document.querySelector('#texture-atlas').replaceChildren(e); } +function appendTextureAtlas(e: HTMLCanvasElement): void { + document.querySelector('#texture-atlas').appendChild(e); +} function writeCustomGlyphHandler(): void { term.write('\n\r'); diff --git a/src/browser/renderer/shared/TextureAtlas.ts b/src/browser/renderer/shared/TextureAtlas.ts index e2d5d617..5b7f7eb2 100644 --- a/src/browser/renderer/shared/TextureAtlas.ts +++ b/src/browser/renderer/shared/TextureAtlas.ts @@ -14,6 +14,7 @@ import { IUnicodeService } from 'common/services/Services'; import { FourKeyMap } from 'common/MultiKeyMap'; import { IdleTaskQueue } from 'common/TaskQueue'; import { IBoundingBox, ICharAtlasConfig, IRasterizedGlyph, ITextureAtlas } from 'browser/renderer/shared/Types'; +import { EventEmitter } from 'common/EventEmitter'; // For debugging purposes, it can be useful to set this to a really tiny value. const TEXTURE_WIDTH = 512; @@ -67,6 +68,10 @@ export class TextureAtlas implements ITextureAtlas { private _workBoundingBox: IBoundingBox = { top: 0, left: 0, bottom: 0, right: 0 }; private _workAttributeData: AttributeData = new AttributeData(); + // TODO: Register + private readonly _onAddTextureAtlasCanvas = new EventEmitter(); + public readonly onAddTextureAtlasCanvas = this._onAddTextureAtlasCanvas.event; + constructor( private readonly _document: Document, private readonly _config: ICharAtlasConfig, @@ -114,7 +119,9 @@ export class TextureAtlas implements ITextureAtlas { if (this._pages[this._pages.length - 1].currentRow.y > TEXTURE_CAPACITY) { // TODO: Support drawing to multiple pages at once console.log(`Add page #${this._pages.length + 1}`); - this._pages.push(new AtlasPage(this._document)); + const newPage = new AtlasPage(this._document); + this._pages.push(newPage); + this._onAddTextureAtlasCanvas.fire(newPage.canvas); return true; } return false; diff --git a/src/browser/renderer/shared/Types.d.ts b/src/browser/renderer/shared/Types.d.ts index 2eaac493..a622438b 100644 --- a/src/browser/renderer/shared/Types.d.ts +++ b/src/browser/renderer/shared/Types.d.ts @@ -91,6 +91,8 @@ export interface ITextureAtlas extends IDisposable { hasCanvasChanged: boolean; + onAddTextureAtlasCanvas: IEvent; + /** * Warm up the texture atlas, adding common glyphs to avoid slowing early frame. */