From 4a50ca81c62528e4171ba8654a08184eb846e4bf Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 28 Aug 2022 09:06:42 -0700 Subject: [PATCH 1/2] Optimize gc for WebglRenderer._updateModel and BufferLine.loadCell RectangleRenderer not done yet --- addons/xterm-addon-webgl/src/WebglRenderer.ts | 42 ++++++++++++------- src/common/buffer/BufferLine.ts | 13 ++++-- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/addons/xterm-addon-webgl/src/WebglRenderer.ts b/addons/xterm-addon-webgl/src/WebglRenderer.ts index ea10b368..81817a80 100644 --- a/addons/xterm-addon-webgl/src/WebglRenderer.ts +++ b/addons/xterm-addon-webgl/src/WebglRenderer.ts @@ -22,7 +22,7 @@ import { EventEmitter } from 'common/EventEmitter'; import { CellData } from 'common/buffer/CellData'; import { addDisposableDomListener } from 'browser/Lifecycle'; import { ICharacterJoinerService, ICoreBrowserService } from 'browser/services/Services'; -import { CharData, ICellData } from 'common/Types'; +import { CharData, IBufferLine, ICellData } from 'common/Types'; import { AttributeData } from 'common/buffer/AttributeData'; import { ICoreService, IDecorationService } from 'common/services/Services'; @@ -314,14 +314,28 @@ export class WebglRenderer extends Disposable implements IRenderer { private _updateModel(start: number, end: number): void { const terminal = this._core; let cell: ICellData = this._workCell; - let lastBg: number = 0; - for (let y = start; y <= end; y++) { - const row = y + terminal.buffer.ydisp; - const line = terminal.buffer.lines.get(row)!; + // Declare variable ahead of time to avoid garbage collection + let lastBg: number; + let y: number; + let row: number; + let line: IBufferLine; + let joinedRanges: [number, number][]; + let isJoined: boolean; + let lastCharX: number; + let range: [number, number]; + let chars: string; + let code: number; + let i: number; + let x: number; + let j: number; + + for (y = start; y <= end; y++) { + row = y + terminal.buffer.ydisp; + line = terminal.buffer.lines.get(row)!; this._model.lineLengths[y] = 0; - const joinedRanges = this._characterJoinerService.getJoinedCharacters(row); - for (let x = 0; x < terminal.cols; x++) { + joinedRanges = this._characterJoinerService.getJoinedCharacters(row); + for (x = 0; x < terminal.cols; x++) { lastBg = this._workColors.bg; line.loadCell(x, cell); @@ -330,15 +344,15 @@ export class WebglRenderer extends Disposable implements IRenderer { } // If true, indicates that the current character(s) to draw were joined. - let isJoined = false; - let lastCharX = x; + isJoined = false; + lastCharX = x; // Process any joined character ranges as needed. Because of how the // ranges are produced, we know that they are valid for the characters // and attributes of our input. if (joinedRanges.length > 0 && x === joinedRanges[0][0]) { isJoined = true; - const range = joinedRanges.shift()!; + range = joinedRanges.shift()!; // We already know the exact start and end column of the joined range, // so we get the string and width representing it directly. @@ -352,9 +366,9 @@ export class WebglRenderer extends Disposable implements IRenderer { lastCharX = range[1] - 1; } - const chars = cell.getChars(); - let code = cell.getCode(); - const i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; + chars = cell.getChars(); + code = cell.getCode(); + i = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; // Load colors/resolve overrides into work colors this._loadColorsForCell(x, row); @@ -390,7 +404,7 @@ export class WebglRenderer extends Disposable implements IRenderer { // Null out non-first cells for (x++; x < lastCharX; x++) { - const j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; + j = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; this._glyphRenderer.updateCell(x, y, NULL_CELL_CODE, 0, 0, 0, NULL_CELL_CHAR, 0); this._model.cells[j] = NULL_CELL_CODE; this._model.cells[j + RENDER_MODEL_BG_OFFSET] = this._workColors.bg; diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts index 6d2a442f..eed87e6b 100644 --- a/src/common/buffer/BufferLine.ts +++ b/src/common/buffer/BufferLine.ts @@ -37,6 +37,11 @@ const enum Cell { export const DEFAULT_ATTR_DATA = Object.freeze(new AttributeData()); +/** Work variables to avoid garbage collection. */ +const w: { startIndex: number } = { + startIndex: 0 +}; + /** * Typed array based bufferline implementation. * @@ -168,10 +173,10 @@ export class BufferLine implements IBufferLine { * to GC as it significantly reduced the amount of new objects/references needed. */ public loadCell(index: number, cell: ICellData): ICellData { - const startIndex = index * CELL_SIZE; - cell.content = this._data[startIndex + Cell.CONTENT]; - cell.fg = this._data[startIndex + Cell.FG]; - cell.bg = this._data[startIndex + Cell.BG]; + w.startIndex = index * CELL_SIZE; + cell.content = this._data[w.startIndex + Cell.CONTENT]; + cell.fg = this._data[w.startIndex + Cell.FG]; + cell.bg = this._data[w.startIndex + Cell.BG]; if (cell.content & Content.IS_COMBINED_MASK) { cell.combinedData = this._combined[index]; } From 3b4ecd0709baeac86404a5609b3ff7c9f4ff0963 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 28 Aug 2022 09:20:24 -0700 Subject: [PATCH 2/2] Optimize gc in rectangle renderer --- .../src/RectangleRenderer.ts | 79 ++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/addons/xterm-addon-webgl/src/RectangleRenderer.ts b/addons/xterm-addon-webgl/src/RectangleRenderer.ts index f16fdec0..acaea56a 100644 --- a/addons/xterm-addon-webgl/src/RectangleRenderer.ts +++ b/addons/xterm-addon-webgl/src/RectangleRenderer.ts @@ -59,6 +59,18 @@ const BYTES_PER_RECTANGLE = INDICES_PER_RECTANGLE * Float32Array.BYTES_PER_ELEME const INITIAL_BUFFER_RECTANGLE_CAPACITY = 20 * INDICES_PER_RECTANGLE; +/** Work variables to avoid garbage collection. */ +const w: { rgba: number, isDefault: boolean, x1: number, y1: number, r: number, g: number, b: number, a: number } = { + rgba: 0, + isDefault: false, + x1: 0, + y1: 0, + r: 0, + g: 0, + b: 0, + a: 0 +}; + export class RectangleRenderer extends Disposable { private _program: WebGLProgram; @@ -174,22 +186,34 @@ export class RectangleRenderer extends Disposable { const terminal = this._terminal; const vertices = this._vertices; + // Declare variable ahead of time to avoid garbage collection let rectangleCount = 1; + let y: number; + let x: number; + let currentStartX: number; + let currentBg: number; + let currentFg: number; + let currentInverse: boolean; + let modelIndex: number; + let bg: number; + let fg: number; + let inverse: boolean; + let offset: number; - for (let y = 0; y < terminal.rows; y++) { - let currentStartX = -1; - let currentBg = 0; - let currentFg = 0; - let currentInverse = false; - for (let x = 0; x < terminal.cols; x++) { - const modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; - const bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET]; - const fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET]; - const inverse = !!(fg & FgFlags.INVERSE); + for (y = 0; y < terminal.rows; y++) { + currentStartX = -1; + currentBg = 0; + currentFg = 0; + currentInverse = false; + for (x = 0; x < terminal.cols; x++) { + modelIndex = ((y * terminal.cols) + x) * RENDER_MODEL_INDICIES_PER_CELL; + bg = model.cells[modelIndex + RENDER_MODEL_BG_OFFSET]; + fg = model.cells[modelIndex + RENDER_MODEL_FG_OFFSET]; + inverse = !!(fg & FgFlags.INVERSE); if (bg !== currentBg || (fg !== currentFg && (currentInverse || inverse))) { // A rectangle needs to be drawn if going from non-default to another color if (currentBg !== 0 || (currentInverse && currentFg !== 0)) { - const offset = rectangleCount++ * INDICES_PER_RECTANGLE; + offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, x, y); } currentStartX = x; @@ -200,7 +224,7 @@ export class RectangleRenderer extends Disposable { } // Finish rectangle if it's still going if (currentBg !== 0 || (currentInverse && currentFg !== 0)) { - const offset = rectangleCount++ * INDICES_PER_RECTANGLE; + offset = rectangleCount++ * INDICES_PER_RECTANGLE; this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, terminal.cols, y); } } @@ -208,48 +232,47 @@ export class RectangleRenderer extends Disposable { } private _updateRectangle(vertices: IVertices, offset: number, fg: number, bg: number, startX: number, endX: number, y: number): void { - let rgba: number | undefined; - let isDefault = false; + w.isDefault = false; if (fg & FgFlags.INVERSE) { switch (fg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: - rgba = this._colors.ansi[fg & Attributes.PCOLOR_MASK].rgba; + w.rgba = this._colors.ansi[fg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: - rgba = (fg & Attributes.RGB_MASK) << 8; + w.rgba = (fg & Attributes.RGB_MASK) << 8; break; case Attributes.CM_DEFAULT: default: - rgba = this._colors.foreground.rgba; + w.rgba = this._colors.foreground.rgba; } } else { switch (bg & Attributes.CM_MASK) { case Attributes.CM_P16: case Attributes.CM_P256: - rgba = this._colors.ansi[bg & Attributes.PCOLOR_MASK].rgba; + w.rgba = this._colors.ansi[bg & Attributes.PCOLOR_MASK].rgba; break; case Attributes.CM_RGB: - rgba = (bg & Attributes.RGB_MASK) << 8; + w.rgba = (bg & Attributes.RGB_MASK) << 8; break; case Attributes.CM_DEFAULT: default: - rgba = this._colors.background.rgba; - isDefault = true; + w.rgba = this._colors.background.rgba; + w.isDefault = true; } } if (vertices.attributes.length < offset + 4) { vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE); } - const x1 = startX * this._dimensions.scaledCellWidth; - const y1 = y * this._dimensions.scaledCellHeight; - const r = ((rgba >> 24) & 0xFF) / 255; - const g = ((rgba >> 16) & 0xFF) / 255; - const b = ((rgba >> 8 ) & 0xFF) / 255; - const a = (!isDefault && bg & BgFlags.DIM) ? DIM_OPACITY : 1; + w.x1 = startX * this._dimensions.scaledCellWidth; + w.y1 = y * this._dimensions.scaledCellHeight; + w.r = ((w.rgba >> 24) & 0xFF) / 255; + w.g = ((w.rgba >> 16) & 0xFF) / 255; + w.b = ((w.rgba >> 8 ) & 0xFF) / 255; + w.a = (!w.isDefault && bg & BgFlags.DIM) ? DIM_OPACITY : 1; - this._addRectangle(vertices.attributes, offset, x1, y1, (endX - startX) * this._dimensions.scaledCellWidth, this._dimensions.scaledCellHeight, r, g, b, a); + this._addRectangle(vertices.attributes, offset, w.x1, w.y1, (endX - startX) * this._dimensions.scaledCellWidth, this._dimensions.scaledCellHeight, w.r, w.g, w.b, w.a); } private _addRectangle(array: Float32Array, offset: number, x1: number, y1: number, width: number, height: number, r: number, g: number, b: number, a: number): void {