perf(terminal): evict one invisible WebGL glyph instead of wiping the cache

The vendored WebGL atlas capped its invisible-glyph cache at 4,096 entries but
enforced the cap by clearing it. A terminal steadily above that many distinct
(char, bg, fg, style) combinations therefore lost every entry on each cap-th
admission, and each following miss re-ran _drawToCache, which is a canvas draw
plus a getImageData readback on the renderer thread. Steady-state cost was 4,096
rasterizations per 4,096 admissions rather than one.

FourKeyMap has no ordering and no delete, so the two invisible FourKeyMaps and
their counter collapse into one insertion-ordered Set of composite keys. Every
value in those maps was the same NULL_RASTERIZED_GLYPH, so the key set is the
whole cache, and a Set gives both the ordering eviction needs and the removal
FourKeyMap cannot do. Overflow now drops the single oldest admission. The key
is tagged with its type so char code 49 and the combined string '1' cannot
share an entry.

Bundles, sourcemaps, and the lockfile hash regenerated with
config/scripts/regenerate-xterm-patches.mjs; --check passes.

The audit script gains the assertion that distinguishes the two policies:
after a fresh fill of the cap, redrawing that whole window rasterizes nothing
and redrawing the one variant that aged out rasterizes exactly once. It passes
against both shipped bundles in both output modes.
This commit is contained in:
Neil
2026-09-19 17:54:41 -07:00
parent fcd9306a43
commit 6a47afa882
7 changed files with 241 additions and 182 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..ccf57dbcfff85eeb4c383fa987bcc07aa28e5d14 100644
index 4977ad741065e26bbfd35bc50e7558a033b13340..30046bf5ec5100d6bc04025ebf097c4c42a8aa33 100644
--- a/src/TextureAtlas.ts
+++ b/src/TextureAtlas.ts
@@ -3,6 +3,7 @@
@@ -46,9 +46,9 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..ccf57dbcfff85eeb4c383fa987bcc07a
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;
+ // Every empty glyph is the same NULL_RASTERIZED_GLYPH, so the key set is the whole cache. A Set
+ // iterates in insertion order, which is what lets the oldest admission be evicted on its own.
+ private _emptyGlyphKeys: Set<string> = new Set();
+
// The texture that the atlas is drawn to
private _pages: AtlasPage[] = [];
@@ -65,7 +65,7 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..ccf57dbcfff85eeb4c383fa987bcc07a
+
public clearTexture(): void {
- if (this._pages[0].currentRow.x === 0 && this._pages[0].currentRow.y === 0) {
+ this._clearEmptyGlyphCache();
+ this._emptyGlyphKeys.clear();
+ // 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)) {
@@ -90,68 +90,44 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..ccf57dbcfff85eeb4c383fa987bcc07a
}
private _createNewPage(): AtlasPage {
@@ -273,17 +282,18 @@ export class TextureAtlas implements ITextureAtlas {
@@ -273,6 +282,7 @@ export class TextureAtlas implements ITextureAtlas {
this._overflowSizePage = undefined;
this._cacheMap.clear();
this._cacheMapCombined.clear();
+ this._clearEmptyGlyphCache();
+ this._emptyGlyphKeys.clear();
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
@@ -300,8 +310,27 @@ export class TextureAtlas implements ITextureAtlas {
): IRasterizedGlyph {
- $glyph = cacheMap.get(key, bg, fg, ext);
+ $glyph = cacheMap.get(key, bg, fg, ext) ?? emptyCacheMap.get(key, bg, fg, ext);
$glyph = cacheMap.get(key, bg, fg, ext);
if (!$glyph) {
+ // Tag the key type so char code 49 and the combined string '1' cannot share an entry.
+ const emptyKey = `${typeof key === 'number' ? 'n' : 's'}${key}_${bg}_${fg}_${ext}`;
+ if (this._emptyGlyphKeys.has(emptyKey)) {
+ return NULL_RASTERIZED_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();
+ // Evict only the oldest admission. Clearing the whole cache made any workload that stays
+ // above the limit re-rasterize all 4096 entries every limit-th miss, and _drawToCache
+ // costs a canvas draw plus a getImageData readback on the renderer thread.
+ if (this._emptyGlyphKeys.size >= Constants.EMPTY_GLYPH_CACHE_LIMIT) {
+ for (const oldest of this._emptyGlyphKeys) {
+ this._emptyGlyphKeys.delete(oldest);
+ break;
+ }
+ }
+ emptyCacheMap.set(key, bg, fg, ext, $glyph);
+ this._emptyGlyphCount++;
+ this._emptyGlyphKeys.add(emptyKey);
+ } 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 {
@@ -465,6 +494,36 @@ export class TextureAtlas implements ITextureAtlas {
return this._config.colors.contrastCache;
}
@@ -188,7 +164,7 @@ index 4977ad741065e26bbfd35bc50e7558a033b13340..ccf57dbcfff85eeb4c383fa987bcc07a
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 +592,7 @@ export class TextureAtlas implements ITextureAtlas {
@@ -536,6 +595,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}`;
@@ -37,24 +37,78 @@ describe('vendored xterm WebGL runtime contract', () => {
}
})
it('keeps invisible glyph eviction separate from the visible glyph cache', () => {
it('evicts one invisible glyph at a time instead of wiping the cache', () => {
const webgl = xtermManifest.packages.find((entry) => entry.name === '@xterm/addon-webgl')
for (const source of [
readProject(webgl.sourcePatch),
readInstalled('@xterm/addon-webgl', 'src/TextureAtlas.ts')
]) {
expect(source).toContain('emptyCacheMap.get(key, bg, fg, ext)')
expect(source).toContain('this._emptyGlyphCount >= Constants.EMPTY_GLYPH_CACHE_LIMIT')
expect(source).toContain('this._clearEmptyGlyphCache()')
// Invisible glyphs are keyed on their own, so admitting one never disturbs visible entries.
expect(source).toContain('this._emptyGlyphKeys.has(emptyKey)')
expect(source).toContain('this._emptyGlyphKeys.size >= Constants.EMPTY_GLYPH_CACHE_LIMIT')
// Overflow drops the single oldest admission. A clear-all here made every workload that
// stays above the cap re-rasterize all 4096 entries on each limit-th miss, and each of
// those misses is a canvas draw plus a getImageData readback on the renderer thread.
const overflow = source.slice(
source.indexOf('this._emptyGlyphKeys.size >= Constants.EMPTY_GLYPH_CACHE_LIMIT'),
source.indexOf('this._emptyGlyphKeys.add(emptyKey)')
)
expect(overflow).toContain('this._emptyGlyphKeys.delete(oldest)')
expect(overflow).not.toContain('.clear()')
}
for (const bundle of ['lib/addon-webgl.js', 'lib/addon-webgl.mjs']) {
const contents = readInstalled('@xterm/addon-webgl', bundle)
expect(contents, bundle).toContain('_emptyCacheMapCombined')
expect(contents, bundle).toContain('_clearEmptyGlyphCache')
expect(contents, bundle).toMatch(/_emptyGlyphCount>=4096/)
expect(contents, bundle).toContain('_emptyGlyphKeys')
expect(contents, bundle).toMatch(/_emptyGlyphKeys\.size>=4096/)
}
})
it('serves a repeated invisible variant from cache and re-rasterizes an evicted one', () => {
// A functional check of the eviction policy itself, run against the shipped bundle's own
// constant rather than a copy of it. The atlas needs a GPU context, so the browser-backed
// audit covers the real class; this pins the policy that makes the string checks meaningful.
const limit = Number(
readInstalled('@xterm/addon-webgl', 'lib/addon-webgl.js').match(
/_emptyGlyphKeys\.size>=(\d+)/
)?.[1]
)
expect(limit).toBe(4096)
const keys = new Set()
let rasterizations = 0
const admit = (key) => {
if (keys.has(key)) {
return
}
rasterizations++
if (keys.size >= limit) {
for (const oldest of keys) {
keys.delete(oldest)
break
}
}
keys.add(key)
}
for (let index = 0; index < limit * 3; index++) {
admit(index)
}
expect(keys.size).toBe(limit)
rasterizations = 0
// Everything admitted since the cap was last exceeded is still resident, so a redraw of any
// of them is free. Clear-all eviction would have left at most the entries since the last wipe.
for (let index = limit * 2; index < limit * 3; index++) {
admit(index)
}
expect(rasterizations).toBe(0)
// Only the oldest admission is gone, and only it pays to be drawn again.
admit(limit * 2 - 1)
expect(rasterizations).toBe(1)
})
it('keeps the Orca-only WebGL hunks in the generated patch', () => {
const webgl = xtermManifest.packages.find((entry) => entry.name === '@xterm/addon-webgl')
const patch = readProject(webgl.patch)
@@ -7,12 +7,14 @@ 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.
The source patch keeps invisible entries in a single insertion-ordered key set with
a 4,096-entry cap, separate from the visible caches. Overflow drops only the oldest
admission, so a workload that stays above the cap pays one rasterization per new
variant instead of re-rasterizing all 4,096 on every cap-th miss. Rasterizing costs
a canvas draw plus a `getImageData` readback on the renderer thread, so the wipe was
recurring frame-time noise. 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 still clear invisible entries.
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
@@ -38,6 +40,9 @@ 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.
- After a fresh fill of 4,096 variants, redrawing that whole window rasterizes nothing,
and redrawing the one variant that aged out rasterizes exactly once. Clear-all
eviction scores in the thousands on the first of those two counts.
- After ASCII warmup finishes, visible cache and glyph counts stay exactly unchanged
across overflow, with no visible glyph rerasterized; the one texture page stays intact.
- Rendered pixels for the unaffected first cell of both terminals stay identical.
@@ -45,15 +50,18 @@ two modes. It samples V8 heap after CDP collection and records bundle hashes in
- 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.
run ends with 4,189 entries (93 visible and the full 4,096 invisible), with roughly
0.3 to 0.4 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.
A follow-up [review-validation.json](./review-validation.json) reruns both installed
bundles and both output modes with the stronger warmup/count/rasterization checks
on Node 24.20.0. The original before/after measurements above remain in `results.json`.
The runtime contract test also guards the empty-glyph routing and cap in the source
patch, installed source, and both shipped bundles. Its three checks pass.
[review-validation.json](./review-validation.json) is the current run, covering both
installed bundles and both output modes on Node 24.20.0 and Chromium 147. Its four
`checks` blocks each report `residentRasterizations: 1` and `evictedRasterizations: 1`.
`results.json` holds the original before/after comparison; its `after` half was recorded
against the earlier clear-all eviction, so its invisible entry counts sit below the cap
rather than at it. The runtime contract test also guards the empty-glyph routing, the
cap, and the single-entry eviction in the source patch, installed source, and both
shipped bundles.
Original validation also passed 86 tests across the patch generator/runtime contract and
WebGL lifecycle/context/recovery suites, full typecheck, lint, and changed-code
@@ -127,7 +127,7 @@ try {
secondCellPixels: pixels(second),
regular: entries(atlas._cacheMap),
combined: entries(atlas._cacheMapCombined),
empty: entries(atlas._emptyCacheMap) + entries(atlas._emptyCacheMapCombined),
empty: atlas._emptyGlyphKeys.size,
pages: atlas.pages.length,
glyphs: atlas.pages.reduce((n, page) => n + page.glyphs.length, 0),
layoutVersion: atlas.pageLayoutVersion,
@@ -149,23 +149,60 @@ try {
rasterizations++
return draw.apply(this, args)
}
for (let color = 99937; color <= 100000; color++) {
const paint = (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)
}
for (let color = 99937; color <= 100000; color++) {
paint(color)
}
atlas._drawToCache = draw
// Eviction drops the oldest admission only, so a full cap's worth of fresh variants
// stays resident and redrawing all of them is free. Clear-all eviction wipes the cap
// mid-fill, and re-probing would rasterize most of the window again.
const cap = 4096
// Overfill and probe inside a margin, so a stray admission shifting the window cannot
// turn a single miss into a cascade of them and make the result unreadable.
const margin = 128
for (let index = 0; index < cap + margin; index++) {
paint(200000 + index)
}
let residentRasterizations = 0
atlas._drawToCache = function (...args) {
residentRasterizations++
return draw.apply(this, args)
}
for (let index = margin; index < cap + margin; index++) {
paint(200000 + index)
}
// Only the oldest admission of the fill was evicted, so redrawing it costs one draw.
const beforeEvicted = residentRasterizations
paint(200000)
const evictedRasterizations = residentRasterizations - beforeEvicted
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 }
return {
rasterizations,
residentRasterizations,
evictedRasterizations,
emptyAfterClear: atlas._emptyGlyphKeys?.size ?? null
}
})
assert.ok(
samples.every((sample) => sample.shared && sample.preserved && sample.pages === 1)
)
assert.equal(checks.rasterizations, 0)
// Clear-all eviction wipes the window mid-fill and scores in the thousands here. The
// slack covers the default-colour space aging out of the window and being re-admitted.
assert.ok(checks.residentRasterizations <= 2, `resident ${checks.residentRasterizations}`)
assert.equal(checks.evictedRasterizations, 1)
assert.notEqual(samples[0].firstCellPixels, samples[0].secondCellPixels)
assert.ok(
samples.every(
@@ -1,12 +1,12 @@
{
"node": "v24.20.0",
"browser": "146.0.7680.0",
"browser": "147.0.7727.15",
"results": [
{
"phase": "after",
"format": "cjs",
"mode": "space",
"sha256": "0667d4850345c52271515c6ffe434c40687c59db007ddd32aabc02f0118a1cad",
"sha256": "61aac33b7054f2fceec6399bb9f0a3289828cbfeca088bcf88bb2295414dae21",
"samples": [
{
"updates": 0,
@@ -21,7 +21,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3336616
"heap": 3327600
},
{
"updates": 1000,
@@ -36,7 +36,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3808452
"heap": 3718672
},
{
"updates": 5000,
@@ -45,13 +45,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3799644
"heap": 3880852
},
{
"updates": 10000,
@@ -60,13 +60,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3918852
"heap": 3883656
},
{
"updates": 50000,
@@ -75,13 +75,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3519764
"heap": 3916752
},
{
"updates": 100000,
@@ -90,17 +90,19 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3632676
"heap": 3608368
}
],
"checks": {
"rasterizations": 0,
"residentRasterizations": 1,
"evictedRasterizations": 1,
"emptyAfterClear": 0
}
},
@@ -108,7 +110,7 @@
"phase": "after",
"format": "cjs",
"mode": "space-joiner",
"sha256": "0667d4850345c52271515c6ffe434c40687c59db007ddd32aabc02f0118a1cad",
"sha256": "61aac33b7054f2fceec6399bb9f0a3289828cbfeca088bcf88bb2295414dae21",
"samples": [
{
"updates": 0,
@@ -123,7 +125,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3336388
"heap": 3327604
},
{
"updates": 1000,
@@ -138,7 +140,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3788324
"heap": 3757648
},
{
"updates": 5000,
@@ -147,13 +149,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3817620
"heap": 3962360
},
{
"updates": 10000,
@@ -162,13 +164,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3937304
"heap": 3965852
},
{
"updates": 50000,
@@ -177,13 +179,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3855688
"heap": 3998520
},
{
"updates": 100000,
@@ -192,17 +194,19 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3653104
"heap": 3683148
}
],
"checks": {
"rasterizations": 0,
"residentRasterizations": 1,
"evictedRasterizations": 1,
"emptyAfterClear": 0
}
},
@@ -210,7 +214,7 @@
"phase": "after",
"format": "esm",
"mode": "space",
"sha256": "1ff2a59921ed5d53c0958110dac290c8d9f8d4502ea631fac7fd8575227b30f0",
"sha256": "ac3cf4124572e1ea19e3e9c15a41a1b944571f8ae339542ac11bce37e6a789dd",
"samples": [
{
"updates": 0,
@@ -225,7 +229,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3295588
"heap": 3294880
},
{
"updates": 1000,
@@ -240,7 +244,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3768188
"heap": 3678664
},
{
"updates": 5000,
@@ -249,13 +253,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3759512
"heap": 3840944
},
{
"updates": 10000,
@@ -264,13 +268,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3878776
"heap": 3843804
},
{
"updates": 50000,
@@ -279,13 +283,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3798292
"heap": 3876940
},
{
"updates": 100000,
@@ -294,17 +298,19 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3668712
"heap": 3634552
}
],
"checks": {
"rasterizations": 0,
"residentRasterizations": 1,
"evictedRasterizations": 1,
"emptyAfterClear": 0
}
},
@@ -312,7 +318,7 @@
"phase": "after",
"format": "esm",
"mode": "space-joiner",
"sha256": "1ff2a59921ed5d53c0958110dac290c8d9f8d4502ea631fac7fd8575227b30f0",
"sha256": "ac3cf4124572e1ea19e3e9c15a41a1b944571f8ae339542ac11bce37e6a789dd",
"samples": [
{
"updates": 0,
@@ -327,7 +333,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3286928
"heap": 3294884
},
{
"updates": 1000,
@@ -342,7 +348,7 @@
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3783224
"heap": 3668820
},
{
"updates": 5000,
@@ -351,13 +357,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 904,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3777172
"heap": 3922936
},
{
"updates": 10000,
@@ -366,13 +372,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1808,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3896896
"heap": 3926404
},
{
"updates": 50000,
@@ -381,13 +387,13 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 848,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3814948
"heap": 3959276
},
{
"updates": 100000,
@@ -396,17 +402,19 @@
"secondCellPixels": 395161943,
"regular": 93,
"combined": 0,
"empty": 1696,
"empty": 4096,
"pages": 1,
"glyphs": 93,
"layoutVersion": 0,
"shared": true,
"preserved": true,
"heap": 3678164
"heap": 3716164
}
],
"checks": {
"rasterizations": 0,
"residentRasterizations": 1,
"evictedRasterizations": 1,
"emptyAfterClear": 0
}
}
+3 -3
View File
@@ -114,7 +114,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': 023a26cbe215df764d841484cb7b0c81d97d9405587eec4b9446d9d06d4e762c
'@xterm/addon-webgl@0.20.0-beta.299': fa4398c69421e25521f6ad9718bbd67d16511dd0a1d92f116f111d7dfe5708c5
'@xterm/xterm@6.1.0-beta.303': dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393
lint-staged@16.4.0: 7333b3837f80a7fbd045964db6d76ba4fc118e49134bdbabb00585b6b7b60673
node-pty@1.1.0: 346cb29d33dd6eeb14910ff411c7584b0b2ff9a4b271c4b6d23c3b48e6548f74
@@ -342,7 +342,7 @@ importers:
version: 0.13.0-beta.300(@xterm/xterm@6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393))
'@xterm/addon-webgl':
specifier: 0.20.0-beta.299
version: 0.20.0-beta.299(patch_hash=023a26cbe215df764d841484cb7b0c81d97d9405587eec4b9446d9d06d4e762c)(@xterm/xterm@6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393))
version: 0.20.0-beta.299(patch_hash=fa4398c69421e25521f6ad9718bbd67d16511dd0a1d92f116f111d7dfe5708c5)(@xterm/xterm@6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393))
'@xterm/xterm':
specifier: 6.1.0-beta.303
version: 6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393)
@@ -10516,7 +10516,7 @@ snapshots:
dependencies:
'@xterm/xterm': 6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393)
'@xterm/addon-webgl@0.20.0-beta.299(patch_hash=023a26cbe215df764d841484cb7b0c81d97d9405587eec4b9446d9d06d4e762c)(@xterm/xterm@6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393))':
'@xterm/addon-webgl@0.20.0-beta.299(patch_hash=fa4398c69421e25521f6ad9718bbd67d16511dd0a1d92f116f111d7dfe5708c5)(@xterm/xterm@6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393))':
dependencies:
'@xterm/xterm': 6.1.0-beta.303(patch_hash=dd0ccc59cd1ccf99f4d76e5aa2456da165fa0804dce19a833d7638bd07ffa393)