fix(terminal): bound invisible WebGL glyph cache entries

This commit is contained in:
m4air
2026-09-15 22:40:05 -07:00
parent a88e7feebe
commit baa40698fa
6 changed files with 1021 additions and 24 deletions
File diff suppressed because one or more lines are too long
@@ -21,7 +21,7 @@ index 742f0879ff4f04509e4a07c8efdf0d5743fe8ee5..885a206a394fff783737a412c0b75bf9
}
diff --git a/src/TextureAtlas.ts b/src/TextureAtlas.ts
index 4977ad741065e26bbfd35bc50e7558a033b13340..f55805ddc3f266415f7f8e81e5b8c318e7db194c 100644
index 4977ad741065e26bbfd35bc50e7558a033b13340..ccf57dbcfff85eeb4c383fa987bcc07aa28e5d14 100644
--- a/src/TextureAtlas.ts
+++ b/src/TextureAtlas.ts
@@ -3,6 +3,7 @@
@@ -32,7 +32,28 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..f55805ddc3f266415f7f8e81e5b8c318
import { IColorContrastCache } from 'browser/Types';
import { DIM_OPACITY, TEXT_BASELINE } from './Constants';
import { tryDrawCustomGlyph } from './customGlyphs/CustomGlyphRasterizer';
@@ -135,21 +136,23 @@ export class TextureAtlas implements ITextureAtlas {
@@ -42,7 +43,8 @@ const enum Constants {
* is enforced to ensure uploading the texture still finishes in a reasonable amount of time. A
* 4096 squared image takes up 16MB of GPU memory.
*/
- FORCED_MAX_TEXTURE_SIZE = 4096
+ FORCED_MAX_TEXTURE_SIZE = 4096,
+ EMPTY_GLYPH_CACHE_LIMIT = 4096
}
interface ICharAtlasActiveRow {
@@ -60,6 +62,10 @@ export class TextureAtlas implements ITextureAtlas {
private _cacheMap: FourKeyMap<number, number, number, number, IRasterizedGlyph> = new FourKeyMap();
private _cacheMapCombined: FourKeyMap<string, number, number, number, IRasterizedGlyph> = new FourKeyMap();
+ private _emptyCacheMap: FourKeyMap<number, number, number, number, IRasterizedGlyph> = new FourKeyMap();
+ private _emptyCacheMapCombined: FourKeyMap<string, number, number, number, IRasterizedGlyph> = new FourKeyMap();
+ private _emptyGlyphCount = 0;
+
// The texture that the atlas is drawn to
private _pages: AtlasPage[] = [];
public get pages(): { canvas: HTMLCanvasElement, version: number }[] { return this._pages; }
@@ -135,21 +141,24 @@ export class TextureAtlas implements ITextureAtlas {
private _pageLayoutVersion = 0;
public get pageLayoutVersion(): number { return this._pageLayoutVersion; }
@@ -44,6 +65,7 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..f55805ddc3f266415f7f8e81e5b8c318
+
public clearTexture(): void {
- if (this._pages[0].currentRow.x === 0 && this._pages[0].currentRow.y === 0) {
+ this._clearEmptyGlyphCache();
+ // Guard on every page rather than pages[0]: a merged page is never written through
+ // currentRow, so once one lands at index 0 the old check made every later clear a no-op.
+ if (this._pages.every(page => page.glyphs.length === 0 && page.currentRow.x === 0 && page.currentRow.y === 0)) {
@@ -68,7 +90,68 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..f55805ddc3f266415f7f8e81e5b8c318
}
private _createNewPage(): AtlasPage {
@@ -465,6 +468,36 @@ export class TextureAtlas implements ITextureAtlas {
@@ -273,17 +282,18 @@ export class TextureAtlas implements ITextureAtlas {
this._overflowSizePage = undefined;
this._cacheMap.clear();
this._cacheMapCombined.clear();
+ this._clearEmptyGlyphCache();
this._didWarmUp = false;
this._pageLayoutVersion++;
this._logService.debug(`Evicted ${pageCount} WebGL atlas pages in ${(performance.now() - startTime).toFixed(2)}ms`);
}
public getRasterizedGlyphCombinedChar(chars: string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph {
- return this._getFromCacheMap(this._cacheMapCombined, chars, bg, fg, ext, restrictToCellHeight, domContainer);
+ return this._getFromCacheMap(this._cacheMapCombined, this._emptyCacheMapCombined, chars, bg, fg, ext, restrictToCellHeight, domContainer);
}
public getRasterizedGlyph(code: number, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph {
- return this._getFromCacheMap(this._cacheMap, code, bg, fg, ext, restrictToCellHeight, domContainer);
+ return this._getFromCacheMap(this._cacheMap, this._emptyCacheMap, code, bg, fg, ext, restrictToCellHeight, domContainer);
}
/**
@@ -291,6 +301,7 @@ export class TextureAtlas implements ITextureAtlas {
*/
private _getFromCacheMap(
cacheMap: FourKeyMap<string | number, number, number, number, IRasterizedGlyph>,
+ emptyCacheMap: FourKeyMap<string | number, number, number, number, IRasterizedGlyph>,
key: string | number,
bg: number,
fg: number,
@@ -298,14 +309,29 @@ export class TextureAtlas implements ITextureAtlas {
restrictToCellHeight: boolean,
domContainer: HTMLElement | undefined
): IRasterizedGlyph {
- $glyph = cacheMap.get(key, bg, fg, ext);
+ $glyph = cacheMap.get(key, bg, fg, ext) ?? emptyCacheMap.get(key, bg, fg, ext);
if (!$glyph) {
$glyph = this._drawToCache(key, bg, fg, ext, restrictToCellHeight, domContainer);
- cacheMap.set(key, bg, fg, ext, $glyph);
+ if ($glyph === NULL_RASTERIZED_GLYPH) {
+ // Empty glyphs consume no atlas pixels, so page eviction cannot bound their metadata.
+ if (this._emptyGlyphCount >= Constants.EMPTY_GLYPH_CACHE_LIMIT) {
+ this._clearEmptyGlyphCache();
+ }
+ emptyCacheMap.set(key, bg, fg, ext, $glyph);
+ this._emptyGlyphCount++;
+ } else {
+ cacheMap.set(key, bg, fg, ext, $glyph);
+ }
}
return $glyph;
}
+ private _clearEmptyGlyphCache(): void {
+ this._emptyCacheMap.clear();
+ this._emptyCacheMapCombined.clear();
+ this._emptyGlyphCount = 0;
+ }
+
private _getColorFromAnsiIndex(idx: number): IColor {
if (idx >= this._config.colors.ansi.length) {
throw new Error('No color found for idx ' + idx);
@@ -465,6 +491,36 @@ export class TextureAtlas implements ITextureAtlas {
return this._config.colors.contrastCache;
}
@@ -105,7 +188,7 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..f55805ddc3f266415f7f8e81e5b8c318
private _drawToCache(codeOrChars: number | string, bg: number, fg: number, ext: number, restrictToCellHeight: boolean, domContainer: HTMLElement | undefined): IRasterizedGlyph {
const chars = typeof codeOrChars === 'number' ? String.fromCharCode(codeOrChars) : codeOrChars;
@@ -536,6 +569,7 @@ export class TextureAtlas implements ITextureAtlas {
@@ -536,6 +592,7 @@ export class TextureAtlas implements ITextureAtlas {
const fontStyle = italic ? 'italic' : '';
this._tmpCtx.font =
`${fontStyle} ${fontWeight} ${this._config.fontSize * this._config.devicePixelRatio}px ${this._config.fontFamily}`;
@@ -0,0 +1,58 @@
# WebGL invisible-glyph cache retention
The installed xterm WebGL addon caches every character/background/foreground/style
variant. Invisible glyphs return a shared empty glyph and occupy no texture
pixels, but still create cache entries. The texture-page eviction threshold can
therefore never collect a stream of new invisible variants. Changing true-color
foreground values while redrawing a space reproduces the growth; a space followed
by a zero-width joiner exercises the separate combined-character cache.
The source patch keeps invisible entries in separate caches with a shared
4,096-entry cap. Overflow clears only those entries. Visible glyphs, texture pages,
and the atlas layout version stay intact, so a sibling terminal sharing the atlas
does not need to rebuild its model. Explicit atlas clearing and page eviction
also clear invisible entries. A cache miss after eviction rasterizes the invisible
glyph again; recent repeated variants remain cache hits.
Both generated bundles and their source maps were regenerated using Orca's pinned
xterm patch generator, the lockfile hashes were updated, and the dependency was
reinstalled with the frozen lockfile. The generator's final `--check` passes.
## Reproduce
From the repository root, with Playwright's Chromium headless shell installed:
```sh
ORCA_BACKGROUND_LAUNCH=1 node docs/audits/webgl-empty-glyph-retention/reproduce.mjs
```
`ORCA_AUDIT_CHROMIUM` can select another Chromium executable. For a before/after
comparison, set `ORCA_AUDIT_WEBGL_BASELINE` to the previous installed
`lib/addon-webgl.js`. This audit reconstructed that baseline by reversing the new
patch from a dependency copy and applying the previous checked-in patch; no
application source or generated bundle was edited by hand.
The script runs the actual installed CJS and ESM bundles in headless Chromium
with SwiftShader, feeds ordinary terminal output, and renders 100,000 redraws in
two modes. It samples V8 heap after CDP collection and records bundle hashes in
[results.json](./results.json). Two terminals share one atlas. Assertions cover:
- Invisible entries stay at or below 4,096; the baseline accumulates 100,000.
- Visible glyph caches and the one texture page stay intact across overflow.
- Rendered pixels for the unaffected first cell of both terminals stay identical.
- The last 64 repeated variants cause no new rasterization.
- Explicit clear drops invisible metadata even when the atlas has no drawn glyphs.
The baseline ends with 100,093 entries and about 13.6 MB of heap growth. The fixed
run ends with 1,789 entries (93 visible and 1,696 invisible), with roughly 0.3 MB
of heap growth. Exact heap samples vary; the bounded entry count is the invariant.
No Orca window was launched, and no browser window was shown.
Validation also passed 86 tests across the patch generator/runtime contract and
WebGL lifecycle/context/recovery suites, full typecheck, lint, and changed-code
quality. Test commands used `ORCA_BACKGROUND_LAUNCH=1`.
The reported `v1.4.198` ships the same addon version and lacks this cap. This is a
renderer retaining path compatible with app-scope memory growth, but #19831 does
not establish a stream of distinct invisible color variants. It does not explain
#19768's separately measured main PID. No incident attribution is claimed.
@@ -0,0 +1,189 @@
import assert from 'node:assert/strict'
import { createHash } from 'node:crypto'
import { readFile } from 'node:fs/promises'
import { resolve } from 'node:path'
import { chromium } from 'playwright'
if (process.env.ORCA_BACKGROUND_LAUNCH !== '1') {
throw new Error('Run with ORCA_BACKGROUND_LAUNCH=1')
}
const installed = resolve('node_modules/@xterm/addon-webgl/lib/addon-webgl.js')
const current = [
['after', installed],
['after', installed.replace(/\.js$/, '.mjs')]
]
const bundles = process.env.ORCA_AUDIT_WEBGL_BASELINE
? [['before', resolve(process.env.ORCA_AUDIT_WEBGL_BASELINE)], ...current]
: current
const browser = await chromium.launch({
executablePath: process.env.ORCA_AUDIT_CHROMIUM,
headless: true,
args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader']
})
const results = []
try {
for (const [phase, bundle] of bundles) {
const sha256 = createHash('sha256')
.update(await readFile(bundle))
.digest('hex')
for (const mode of ['space', 'space-joiner']) {
const page = await browser.newPage()
try {
await page.setContent('<div id="first"></div><div id="second"></div>')
await page.addStyleTag({ path: resolve('node_modules/@xterm/xterm/css/xterm.css') })
await page.addScriptTag({ path: resolve('node_modules/@xterm/xterm/lib/xterm.js') })
if (bundle.endsWith('.mjs')) {
const source = await readFile(bundle, 'utf8')
await page.evaluate(
async (url) => {
window.WebglAddon = await import(url)
},
`data:text/javascript;base64,${Buffer.from(source).toString('base64')}`
)
} else {
await page.addScriptTag({ path: bundle })
}
await page.evaluate(async (mode) => {
const create = (id, text) => {
const terminal = new Terminal({
cols: 4,
rows: 1,
scrollback: 0,
allowProposedApi: true,
logLevel: 'off'
})
terminal.open(document.getElementById(id))
const addon = new WebglAddon.WebglAddon()
terminal.loadAddon(addon)
terminal._core.writeSync(text)
addon._renderer.renderRows(0, 0)
return { terminal, addon }
}
const first = create('first', 'A')
const second = create('second', 'B')
window.glyphAudit = { first, second, mode, index: 0 }
await new Promise(requestAnimationFrame)
await new Promise(requestIdleCallback)
}, mode)
const cdp = await page.context().newCDPSession(page)
const samples = []
for (const count of [0, 1000, 5000, 10000, 50000, 100000]) {
const state = await page.evaluate((count) => {
const { first, second, mode } = window.glyphAudit
for (let index = window.glyphAudit.index; index < count; index++) {
const color = index + 1
first.terminal._core.writeSync(
`\x1b[1;2H\x1b[38;2;${color >> 16};${(color >> 8) & 255};${color & 255}m ${mode === 'space-joiner' ? '\u200d' : ''}`
)
first.addon._renderer.renderRows(0, 0)
}
first.addon._renderer.renderRows(0, 0)
second.addon._renderer.renderRows(0, 0)
window.glyphAudit.index = count
const atlas = first.addon._renderer._charAtlas
const entries = (map) => {
let result = 0
for (const second of Object.values(map?._data._data ?? {})) {
for (const inner of Object.values(second)) {
for (const fourth of Object.values(inner._data)) {
result += Object.keys(fourth).length
}
}
}
return result
}
const pixels = ({ terminal, addon }) => {
const gl = addon._renderer._gl
const width = Math.floor(gl.drawingBufferWidth / terminal.cols)
const height = gl.drawingBufferHeight
const bytes = new Uint8Array(width * height * 4)
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, bytes)
let hash = 2166136261
for (const byte of bytes) {
hash = Math.imul(hash ^ byte, 16777619)
}
return hash >>> 0
}
return {
updates: count,
firstCellPixels: pixels(first),
secondCellPixels: pixels(second),
regular: entries(atlas._cacheMap),
combined: entries(atlas._cacheMapCombined),
empty: entries(atlas._emptyCacheMap) + entries(atlas._emptyCacheMapCombined),
pages: atlas.pages.length,
glyphs: atlas.pages.reduce((n, page) => n + page.glyphs.length, 0),
layoutVersion: atlas.pageLayoutVersion,
shared: atlas === second.addon._renderer._charAtlas,
preserved:
first.terminal.buffer.active.getLine(0).getCell(0).getChars() === 'A' &&
second.terminal.buffer.active.getLine(0).getCell(0).getChars() === 'B'
}
}, count)
await cdp.send('HeapProfiler.collectGarbage')
samples.push({ ...state, heap: (await cdp.send('Runtime.getHeapUsage')).usedSize })
}
const checks = await page.evaluate(() => {
const { first, mode } = window.glyphAudit
const atlas = first.addon._renderer._charAtlas
let rasterizations = 0
const draw = atlas._drawToCache
atlas._drawToCache = function (...args) {
rasterizations++
return draw.apply(this, args)
}
for (let color = 99937; color <= 100000; color++) {
first.terminal._core.writeSync(
`\x1b[1;2H\x1b[38;2;${color >> 16};${(color >> 8) & 255};${color & 255}m ${mode === 'space-joiner' ? '\u200d' : ''}`
)
first.addon._renderer.renderRows(0, 0)
}
atlas._drawToCache = draw
first.addon.clearTextureAtlas()
// Exercise explicit clear with only invisible glyph metadata and no drawn atlas cells.
atlas.getRasterizedGlyph(32, 0, 0x3000001, 0, false, first.terminal.element)
first.addon.clearTextureAtlas()
return { rasterizations, emptyAfterClear: atlas._emptyGlyphCount ?? null }
})
assert.ok(
samples.every((sample) => sample.shared && sample.preserved && sample.pages === 1)
)
assert.equal(checks.rasterizations, 0)
assert.notEqual(samples[0].firstCellPixels, samples[0].secondCellPixels)
assert.ok(
samples.every(
(sample) =>
sample.firstCellPixels === samples[0].firstCellPixels &&
sample.secondCellPixels === samples[0].secondCellPixels
)
)
if (phase === 'after') {
assert.ok(
samples.every(
(sample) => sample.empty <= 4096 && sample.regular + sample.combined < 200
)
)
assert.equal(checks.emptyAfterClear, 0)
assert.equal(samples[0].layoutVersion, samples.at(-1).layoutVersion)
} else {
assert.ok(samples.at(-1).regular + samples.at(-1).combined >= 100000)
}
results.push({
phase,
format: bundle.endsWith('.mjs') ? 'esm' : 'cjs',
mode,
sha256,
samples,
checks
})
} finally {
await page.close()
}
}
}
console.log(
JSON.stringify({ node: process.version, browser: browser.version(), results }, null, 2)
)
} finally {
await browser.close()
}
@@ -0,0 +1,582 @@
{
"node": "v26.6.0",
"browser": "147.0.7727.15",
"results": [
{
"phase": "before",
"format": "cjs",
"mode": "space",
"sha256": "c4b646075065e9ed5f885880ff80e2edd54f481f982adaf193cc742de17e1a4f",
"samples": [
{
"updates": 0,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 3,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 3,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3224056
},
{
"updates": 1000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 1004,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 4,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3920428
},
{
"updates": 5000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 5093,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 4291832
},
{
"updates": 10000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 10093,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 4912812
},
{
"updates": 50000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 50093,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 10498896
},
{
"updates": 100000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 100093,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 16964756
}
],
"checks": {
"rasterizations": 0,
"emptyAfterClear": null
}
},
{
"phase": "before",
"format": "cjs",
"mode": "space-joiner",
"sha256": "c4b646075065e9ed5f885880ff80e2edd54f481f982adaf193cc742de17e1a4f",
"samples": [
{
"updates": 0,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 3,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 3,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3519884
},
{
"updates": 1000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 1000,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3812016
},
{
"updates": 5000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 5000,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 4305884
},
{
"updates": 10000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 10000,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 4927728
},
{
"updates": 50000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 50000,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 10510028
},
{
"updates": 100000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 100000,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 16974812
}
],
"checks": {
"rasterizations": 0,
"emptyAfterClear": null
}
},
{
"phase": "after",
"format": "cjs",
"mode": "space",
"sha256": "0667d4850345c52271515c6ffe434c40687c59db007ddd32aabc02f0118a1cad",
"samples": [
{
"updates": 0,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3334740
},
{
"updates": 1000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1000,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3799504
},
{
"updates": 5000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3792468
},
{
"updates": 10000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3913748
},
{
"updates": 50000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3833376
},
{
"updates": 100000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3638720
}
],
"checks": {
"rasterizations": 0,
"emptyAfterClear": 0
}
},
{
"phase": "after",
"format": "cjs",
"mode": "space-joiner",
"sha256": "0667d4850345c52271515c6ffe434c40687c59db007ddd32aabc02f0118a1cad",
"samples": [
{
"updates": 0,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 3,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 3,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3451404
},
{
"updates": 1000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1000,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3814924
},
{
"updates": 5000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3809484
},
{
"updates": 10000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3931512
},
{
"updates": 50000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3849588
},
{
"updates": 100000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3654196
}
],
"checks": {
"rasterizations": 0,
"emptyAfterClear": 0
}
},
{
"phase": "after",
"format": "esm",
"mode": "space",
"sha256": "1ff2a59921ed5d53c0958110dac290c8d9f8d4502ea631fac7fd8575227b30f0",
"samples": [
{
"updates": 0,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 3,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 3,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3476632
},
{
"updates": 1000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1000,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3758644
},
{
"updates": 5000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3752072
},
{
"updates": 10000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3873504
},
{
"updates": 50000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3794992
},
{
"updates": 100000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3666356
}
],
"checks": {
"rasterizations": 0,
"emptyAfterClear": 0
}
},
{
"phase": "after",
"format": "esm",
"mode": "space-joiner",
"sha256": "1ff2a59921ed5d53c0958110dac290c8d9f8d4502ea631fac7fd8575227b30f0",
"samples": [
{
"updates": 0,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 0,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3293684
},
{
"updates": 1000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1000,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3736872
},
{
"updates": 5000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3769340
},
{
"updates": 10000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3891352
},
{
"updates": 50000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3809472
},
{
"updates": 100000,
"firstCellPixels": 1033360771,
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3680100
}
],
"checks": {
"rasterizations": 0,
"emptyAfterClear": 0
}
}
]
}
+3 -3
View File
@@ -113,7 +113,7 @@ patchedDependencies:
'@xterm/addon-ligatures@0.11.0-beta.300': 47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920
'@xterm/addon-search@0.17.0-beta.300': eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0
'@xterm/addon-serialize@0.15.0-beta.300': 851eac3d75e6d8c013b9f4c053e61d824b23965cb19ecc28e335e05059f3a294
'@xterm/addon-webgl@0.20.0-beta.299': 94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e
'@xterm/addon-webgl@0.20.0-beta.299': 023a26cbe215df764d841484cb7b0c81d97d9405587eec4b9446d9d06d4e762c
'@xterm/xterm@6.1.0-beta.303': 1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4
lint-staged@16.4.0: 7333b3837f80a7fbd045964db6d76ba4fc118e49134bdbabb00585b6b7b60673
node-pty@1.1.0: 346cb29d33dd6eeb14910ff411c7584b0b2ff9a4b271c4b6d23c3b48e6548f74
@@ -338,7 +338,7 @@ importers:
version: 0.13.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
'@xterm/addon-webgl':
specifier: 0.20.0-beta.299
version: 0.20.0-beta.299(patch_hash=94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
version: 0.20.0-beta.299(patch_hash=023a26cbe215df764d841484cb7b0c81d97d9405587eec4b9446d9d06d4e762c)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))
'@xterm/xterm':
specifier: 6.1.0-beta.303
version: 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
@@ -10459,7 +10459,7 @@ snapshots:
dependencies:
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)
'@xterm/addon-webgl@0.20.0-beta.299(patch_hash=94687e89a0115e6e6aa102837f986debdc029c091527ee5eb4a4e17ceaf9473e)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
'@xterm/addon-webgl@0.20.0-beta.299(patch_hash=023a26cbe215df764d841484cb7b0c81d97d9405587eec4b9446d9d06d4e762c)(@xterm/xterm@6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4))':
dependencies:
'@xterm/xterm': 6.1.0-beta.303(patch_hash=1f36ce689bc50c703ae09aeda0e064f107e18e4a5ecba19e746fa5edc4b02ef4)