From 84ab6a49c4cf717027251a4c118552a7ed0472ea Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 28 Oct 2022 18:56:49 -0700 Subject: [PATCH] Bring texturePage partially to atlas --- addons/xterm-addon-webgl/src/GlyphRenderer.ts | 54 +++++++++++-------- src/browser/renderer/shared/TextureAtlas.ts | 29 +++++++--- src/browser/renderer/shared/Types.d.ts | 6 +++ 3 files changed, 61 insertions(+), 28 deletions(-) diff --git a/addons/xterm-addon-webgl/src/GlyphRenderer.ts b/addons/xterm-addon-webgl/src/GlyphRenderer.ts index a6c2f74c..58df22dd 100644 --- a/addons/xterm-addon-webgl/src/GlyphRenderer.ts +++ b/addons/xterm-addon-webgl/src/GlyphRenderer.ts @@ -85,18 +85,17 @@ let $leftCellPadding = 0; let $clippedPixels = 0; export class GlyphRenderer extends Disposable { + private readonly _program: WebGLProgram; + private readonly _vertexArrayObject: IWebGLVertexArrayObject; + private readonly _projectionLocation: WebGLUniformLocation; + private readonly _resolutionLocation: WebGLUniformLocation; + private readonly _textureLocation: WebGLUniformLocation; + private readonly _atlasTexture: WebGLTexture; + private readonly _atlasTexture1: WebGLTexture; + private readonly _attributesBuffer: WebGLBuffer; + private _atlas: ITextureAtlas | undefined; - - private _program: WebGLProgram; - private _vertexArrayObject: IWebGLVertexArrayObject; - private _projectionLocation: WebGLUniformLocation; - private _resolutionLocation: WebGLUniformLocation; - private _textureLocation: WebGLUniformLocation; - private readonly _nullTexture: WebGLTexture; - private _atlasTexture: WebGLTexture; - private _attributesBuffer: WebGLBuffer; private _activeBuffer: number = 0; - private _vertices: IVertices = { count: 0, attributes: new Float32Array(0), @@ -172,16 +171,17 @@ export class GlyphRenderer extends Disposable { this.register(toDisposable(() => gl.deleteTexture(this._atlasTexture))); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture); + 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, 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); - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); - this._nullTexture = throwIfFalsy(gl.createTexture()); + this._atlasTexture1 = throwIfFalsy(gl.createTexture()); + this.register(toDisposable(() => gl.deleteTexture(this._atlasTexture1))); gl.activeTexture(gl.TEXTURE0 + 1); - gl.bindTexture(gl.TEXTURE_2D, this._nullTexture); - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255])); + gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture1); 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, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255])); // Allow drawing of transparent texture gl.enable(gl.BLEND); @@ -234,7 +234,7 @@ export class GlyphRenderer extends Disposable { array[$i + 2] = ($glyph.size.x - $clippedPixels) / this._dimensions.device.canvas.width; array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height; // a_texpage - array[$i + 4] = 0; + array[$i + 4] = $glyph.texturePage; // a_texcoord array[$i + 5] = $glyph.texturePositionClipSpace.x + $clippedPixels / this._atlas.cacheCanvas.width; array[$i + 6] = $glyph.texturePositionClipSpace.y; @@ -249,7 +249,7 @@ export class GlyphRenderer extends Disposable { array[$i + 2] = $glyph.size.x / this._dimensions.device.canvas.width; array[$i + 3] = $glyph.size.y / this._dimensions.device.canvas.height; // a_texpage - array[$i + 4] = 0; + array[$i + 4] = $glyph.texturePage; // a_texcoord array[$i + 5] = $glyph.texturePositionClipSpace.x; array[$i + 6] = $glyph.texturePositionClipSpace.y; @@ -333,16 +333,18 @@ export class GlyphRenderer extends Disposable { // TODO: Make nicer const layerTextureUnits = new Int32Array([0, 1]); gl.uniform1iv(this._textureLocation, layerTextureUnits); - gl.uniform1i(this._textureLocation, 0); gl.activeTexture(gl.TEXTURE0 + 0); gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.cacheCanvas); - // TODO: Why is mipmap here? + gl.generateMipmap(gl.TEXTURE_2D); + + // TODO: Check if the particular texture page changed + gl.activeTexture(gl.TEXTURE0 + 1); + gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture1); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this._atlas.cacheCanvas1!); gl.generateMipmap(gl.TEXTURE_2D); } - gl.activeTexture(gl.TEXTURE0 + 1); - gl.bindTexture(gl.TEXTURE_2D, this._nullTexture); // Set uniforms gl.uniformMatrix4fv(this._projectionLocation, false, PROJECTION_MATRIX); @@ -356,9 +358,19 @@ export class GlyphRenderer extends Disposable { const gl = this._gl; this._atlas = atlas; + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture); + 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.cacheCanvas); gl.generateMipmap(gl.TEXTURE_2D); + + gl.activeTexture(gl.TEXTURE0 + 1); + gl.bindTexture(gl.TEXTURE_2D, this._atlasTexture1); + 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.cacheCanvas1!); + gl.generateMipmap(gl.TEXTURE_2D); } public setDimensions(dimensions: IRenderDimensions): void { diff --git a/src/browser/renderer/shared/TextureAtlas.ts b/src/browser/renderer/shared/TextureAtlas.ts index 4edebf5c..25ecf3cb 100644 --- a/src/browser/renderer/shared/TextureAtlas.ts +++ b/src/browser/renderer/shared/TextureAtlas.ts @@ -30,9 +30,10 @@ const TEXTURE_CAPACITY = Math.floor(TEXTURE_HEIGHT * 0.8); * A shared object which is used to draw nothing for a particular cell. */ const NULL_RASTERIZED_GLYPH: IRasterizedGlyph = { - offset: { x: 0, y: 0 }, + texturePage: 0, texturePosition: { x: 0, y: 0 }, texturePositionClipSpace: { x: 0, y: 0 }, + offset: { x: 0, y: 0 }, size: { x: 0, y: 0 }, sizeClipSpace: { x: 0, y: 0 } }; @@ -56,7 +57,9 @@ export class TextureAtlas implements ITextureAtlas { // The texture that the atlas is drawn to public cacheCanvas: HTMLCanvasElement; + public cacheCanvas1: HTMLCanvasElement | undefined; private _cacheCtx: CanvasRenderingContext2D; + private _cacheCtx1: CanvasRenderingContext2D | undefined; private _tmpCanvas: HTMLCanvasElement; // A temporary context that glyphs are drawn to before being transfered to the atlas. @@ -89,23 +92,34 @@ export class TextureAtlas implements ITextureAtlas { private readonly _config: ICharAtlasConfig, private readonly _unicodeService: IUnicodeService ) { - this.cacheCanvas = document.createElement('canvas'); - this.cacheCanvas.width = TEXTURE_WIDTH; - this.cacheCanvas.height = TEXTURE_HEIGHT; + this.cacheCanvas = this._createCanvas(TEXTURE_WIDTH, TEXTURE_HEIGHT); // The canvas needs alpha because we use clearColor to convert the background color to alpha. // It might also contain some characters with transparent backgrounds if allowTransparency is // set. this._cacheCtx = throwIfFalsy(this.cacheCanvas.getContext('2d', { alpha: true })); - this._tmpCanvas = document.createElement('canvas'); - this._tmpCanvas.width = this._config.deviceCellWidth * 4 + TMP_CANVAS_GLYPH_PADDING * 2; - this._tmpCanvas.height = this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 2; + this.cacheCanvas1 = this._createCanvas(TEXTURE_WIDTH, TEXTURE_HEIGHT); + this._cacheCtx1 = throwIfFalsy(this.cacheCanvas1.getContext('2d', { alpha: true })); + this._cacheCtx1.fillStyle = 'rgb(255, 255, 0)'; + this._cacheCtx1.fillRect(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT); + + this._tmpCanvas = this._createCanvas( + this._config.deviceCellWidth * 4 + TMP_CANVAS_GLYPH_PADDING * 2, + this._config.deviceCellHeight + TMP_CANVAS_GLYPH_PADDING * 2 + ); this._tmpCtx = throwIfFalsy(this._tmpCanvas.getContext('2d', { alpha: this._config.allowTransparency, willReadFrequently: true })); } + private _createCanvas(width: number, height: number): HTMLCanvasElement { + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + return canvas; + } + public dispose(): void { if (this.cacheCanvas.parentElement) { this.cacheCanvas.parentElement.removeChild(this.cacheCanvas); @@ -759,6 +773,7 @@ export class TextureAtlas implements ITextureAtlas { } } return { + texturePage: 1, texturePosition: { x: 0, y: 0 }, texturePositionClipSpace: { x: 0, y: 0 }, size: { diff --git a/src/browser/renderer/shared/Types.d.ts b/src/browser/renderer/shared/Types.d.ts index 433d48a2..b5220e2c 100644 --- a/src/browser/renderer/shared/Types.d.ts +++ b/src/browser/renderer/shared/Types.d.ts @@ -88,6 +88,8 @@ export interface IRenderer extends IDisposable { export interface ITextureAtlas extends IDisposable { readonly cacheCanvas: HTMLCanvasElement; + readonly cacheCanvas1: HTMLCanvasElement | undefined; + hasCanvasChanged: boolean; /** @@ -120,6 +122,10 @@ export interface IRasterizedGlyph { * in pixels. */ offset: IVector; + /** + * The index of the texture page that the glyph is on. + */ + texturePage: number; /** * the x and y position of the glyph in the texture in pixels. */