fix(terminal): bound WebGL atlas pages and release removed page textures (#39715)

Cap merged atlas pages at FORCED_MAX_TEXTURE_SIZE instead of the device
MAX_TEXTURE_SIZE (16384 on Apple Silicon), stop the link render layer
from acquiring the shared atlas with a mismatched 2048 texture size, and
reset GL texture slots of removed atlas pages to the 1x1 placeholder.
Mirrors xtermjs/xterm.js#6187, #6156 and #6306 in the addon-webgl source
patch and regenerates the runtime patch and lock hash.

Co-authored-by: FlamingoPg <1106310035@qq.com>
This commit is contained in:
Neil
2026-11-07 21:30:43 -08:00
committed by GitHub
co-authored by FlamingoPg
parent efeee83b2c
commit 6a68dac421
3 changed files with 223 additions and 31 deletions
File diff suppressed because one or more lines are too long
@@ -32,7 +32,7 @@ index 49fdf5b1bb4d6c1f602e36470248a10a690a3479..384f4db6f22d0691f66938d42333d1b0
if (code !== NULL_CELL_CODE && cell.extended.underlineStyle === UnderlineStyle.DOTTED) {
const lineWidth = Math.max(1, Math.floor(this._optionService.rawOptions.fontSize * this._coreBrowserService.dpr / 15));
diff --git a/src/GlyphRenderer.ts b/src/GlyphRenderer.ts
index 742f0879ff4f04509e4a07c8efdf0d5743fe8ee5..885a206a394fff783737a412c0b75bf935dd0eca 100644
index 742f0879ff4f04509e4a07c8efdf0d5743fe8ee5..21347443255cba3011e534248557239931ab7d47 100644
--- a/src/GlyphRenderer.ts
+++ b/src/GlyphRenderer.ts
@@ -61,6 +61,8 @@ function createFragmentShaderSource(maxFragmentShaderTextureUnits: number): stri
@@ -53,6 +53,30 @@ index 742f0879ff4f04509e4a07c8efdf0d5743fe8ee5..885a206a394fff783737a412c0b75bf9
}`);
}
@@ -102,6 +104,7 @@ export class GlyphRenderer extends Disposable {
private _atlas: ITextureAtlas | undefined;
private _lastSeenPageLayoutVersion: number = -1;
private _pageOverflowWarned: boolean = false;
+ private _uploadedTextureCount: number = 0;
private _activeBuffer: number = 0;
private readonly _vertices: IVertices = {
count: 0,
@@ -387,6 +390,15 @@ export class GlyphRenderer extends Disposable {
this._bindAtlasPageTexture(gl, this._atlas, i);
}
}
+ // Merging or evicting pages shrinks the page list, release the textures of pages that no
+ // longer exist as they would otherwise keep their full size in GPU memory until reused.
+ for (let i = pageCount; i < this._uploadedTextureCount; i++) {
+ gl.activeTexture(gl.TEXTURE0 + i);
+ gl.bindTexture(gl.TEXTURE_2D, this._atlasTextures[i].texture);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
+ this._atlasTextures[i].version = -1;
+ }
+ this._uploadedTextureCount = pageCount;
// Draw the viewport
gl.drawElementsInstanced(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_BYTE, 0, bufferLength / Constants.INDICES_PER_CELL);
diff --git a/src/RectangleRenderer.ts b/src/RectangleRenderer.ts
index eebfda3b3df9b4c8cefcc6a64692053ed5eaeefa..75e34767bd1e12802b72371fddb5fc9028814e11 100644
--- a/src/RectangleRenderer.ts
@@ -78,7 +102,7 @@ index eebfda3b3df9b4c8cefcc6a64692053ed5eaeefa..75e34767bd1e12802b72371fddb5fc90
this._updateRectangle(vertices, offset, currentFg, currentBg, currentStartX, terminal.cols, y);
}
diff --git a/src/TextureAtlas.ts b/src/TextureAtlas.ts
index eff0d0e1c36906b1f14b85fb8db7907bf685c8a7..b4fcff8c6be49b1868a17bcabfa3bb54689fb8b1 100644
index eff0d0e1c36906b1f14b85fb8db7907bf685c8a7..1fb0b7a971e33e1dbd2dcf9841645ca464617af2 100644
--- a/src/TextureAtlas.ts
+++ b/src/TextureAtlas.ts
@@ -3,6 +3,7 @@
@@ -156,6 +180,15 @@ index eff0d0e1c36906b1f14b85fb8db7907bf685c8a7..b4fcff8c6be49b1868a17bcabfa3bb54
}
private _createNewPage(): AtlasPage {
@@ -164,7 +172,7 @@ export class TextureAtlas implements ITextureAtlas {
// Find the set of the largest 4 images, below the maximum size, with the highest
// percentages used
const pagesBySize = this._pages.filter(e => {
- return e.canvas.width * 2 <= (TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE);
+ return e.canvas.width * 2 <= Math.min(TextureAtlas.maxTextureSize || Constants.FORCED_MAX_TEXTURE_SIZE, Constants.FORCED_MAX_TEXTURE_SIZE);
}).sort((a, b) => {
if (b.canvas.width !== a.canvas.width) {
return b.canvas.width - a.canvas.width;
@@ -468,6 +476,36 @@ export class TextureAtlas implements ITextureAtlas {
return this._config.colors.contrastCache;
}
@@ -261,15 +294,78 @@ index 2d0f5a3a8ee7fc0765a0179cee0102edb9376e4b..92f1c7241e887cd907d21d91aa26c0de
this._glyphRenderer.value.render(this._model);
if (!this._cursorBlinkStateManager.value || this._cursorBlinkStateManager.value.isCursorVisible) {
diff --git a/src/renderLayer/BaseRenderLayer.ts b/src/renderLayer/BaseRenderLayer.ts
index 5b11615328957b6f5d41a174284c11e061910d91..1654e5ae39baf65a7243cbe7b1395c28824b269e 100644
index 5b11615328957b6f5d41a174284c11e061910d91..00f299659106716258ad7b02c2bfdddcc53e91fe 100644
--- a/src/renderLayer/BaseRenderLayer.ts
+++ b/src/renderLayer/BaseRenderLayer.ts
@@ -97,7 +97,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
}
@@ -3,8 +3,6 @@
* @license MIT
*/
this._charAtlas = acquireTextureAtlas(terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr, 2048);
- this._charAtlas.warmUp();
+ this._charAtlas.warmUp(terminal.element);
-import { ReadonlyColorSet } from 'browser/Types';
-import { acquireTextureAtlas } from '../CharAtlasCache';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
import { Disposable, toDisposable } from 'common/Lifecycle';
@@ -14,7 +12,6 @@ import { Terminal } from '@xterm/xterm';
import { IRenderLayer } from './Types';
import { throwIfFalsy } from 'browser/renderer/shared/RendererUtils';
import { TEXT_BASELINE } from '../Constants';
-import type { ITextureAtlas } from '../Types';
export abstract class BaseRenderLayer extends Disposable implements IRenderLayer {
private _canvas: HTMLCanvasElement;
@@ -26,8 +23,6 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
private _deviceCharLeft: number = 0;
private _deviceCharTop: number = 0;
- protected _charAtlas: ITextureAtlas | undefined;
-
constructor(
terminal: Terminal,
private _container: HTMLElement,
@@ -44,8 +39,7 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
this._canvas.style.zIndex = zIndex.toString();
this._initCanvas();
this._container.appendChild(this._canvas);
- this._register(this._themeService.onChangeColors(e => {
- this._refreshCharAtlas(terminal, e);
+ this._register(this._themeService.onChangeColors(() => {
this.reset(terminal);
}));
this._register(toDisposable(() => {
@@ -81,25 +75,10 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
this._initCanvas();
this._container.replaceChild(this._canvas, oldCanvas);
- // Regenerate char atlas and force a full redraw
- this._refreshCharAtlas(terminal, this._themeService.colors);
+ // Force a full redraw
this.handleGridChanged(terminal, 0, terminal.rows - 1);
}
- /**
- * Refreshes the char atlas, aquiring a new one if necessary.
- * @param terminal The terminal.
- * @param colorSet The color set to use for the char atlas.
- */
- private _refreshCharAtlas(terminal: Terminal, colorSet: ReadonlyColorSet): void {
- if (this._deviceCharWidth <= 0 && this._deviceCharHeight <= 0) {
- return;
- }
-
- this._charAtlas = acquireTextureAtlas(terminal, this._optionsService.rawOptions, colorSet, this._deviceCellWidth, this._deviceCellHeight, this._deviceCharWidth, this._deviceCharHeight, this._coreBrowserService.dpr, 2048);
- this._charAtlas.warmUp();
- }
-
public resize(terminal: Terminal, dim: IRenderDimensions): void {
this._deviceCellWidth = dim.device.cell.width;
this._deviceCellHeight = dim.device.cell.height;
@@ -116,8 +95,6 @@ export abstract class BaseRenderLayer extends Disposable implements IRenderLayer
if (!this._alpha) {
this._clearAll();
}
-
- this._refreshCharAtlas(terminal, this._themeService.colors);
}
public abstract reset(terminal: Terminal): void;
+3 -3
View File
@@ -205,7 +205,7 @@ patchedDependencies:
'@xterm/addon-ligatures@0.11.0-beta.330': 47405b9994b5acf1b4e90b49250358c1ca03649854d59560e7732b72fe336920
'@xterm/addon-search@0.17.0-beta.330': eee5338dd2621ece46e79c61ec06766cd7fadaf79ffdb24e2a8ab68e97ef31f0
'@xterm/addon-serialize@0.15.0-beta.330': 17ca66ab9c7f52fc2fee94053b3b587c1210da468f3ddc7c4f5dba50b521c93e
'@xterm/addon-webgl@0.20.0-beta.329': a12343c037a1a504d80da0a0bbdbbdc7ce3440a088021a95da1dd6f62b98d110
'@xterm/addon-webgl@0.20.0-beta.329': 0d63e956bc68b3f56942e2ed9325e2bf2b1dab76ab403a0690b94232b5ef4aab
'@xterm/xterm@6.1.0-beta.336': 4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c
glob@13.0.6: 163edae2cab27bf7a8cf791c9a0ee368b596bfef898e490b3b77e7e44a8f0223
i18next-cli@1.74.2: 2ee32eef742615cd133e1a6940f027deb3723a8430bb7953bba3a6769b0aac47
@@ -463,7 +463,7 @@ importers:
version: 0.13.0-beta.330(@xterm/xterm@6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c))
'@xterm/addon-webgl':
specifier: 0.20.0-beta.329
version: 0.20.0-beta.329(patch_hash=a12343c037a1a504d80da0a0bbdbbdc7ce3440a088021a95da1dd6f62b98d110)(@xterm/xterm@6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c))
version: 0.20.0-beta.329(patch_hash=0d63e956bc68b3f56942e2ed9325e2bf2b1dab76ab403a0690b94232b5ef4aab)(@xterm/xterm@6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c))
'@xterm/xterm':
specifier: 6.1.0-beta.336
version: 6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c)
@@ -11386,7 +11386,7 @@ snapshots:
dependencies:
'@xterm/xterm': 6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c)
'@xterm/addon-webgl@0.20.0-beta.329(patch_hash=a12343c037a1a504d80da0a0bbdbbdc7ce3440a088021a95da1dd6f62b98d110)(@xterm/xterm@6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c))':
'@xterm/addon-webgl@0.20.0-beta.329(patch_hash=0d63e956bc68b3f56942e2ed9325e2bf2b1dab76ab403a0690b94232b5ef4aab)(@xterm/xterm@6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c))':
dependencies:
'@xterm/xterm': 6.1.0-beta.336(patch_hash=4b7fbfe7a7f626843ab88535cc6e2181872ea7c107876b15406a295182bc3c2c)