From 3bcb57545e3d2db44555f9128f4fefb66555fd98 Mon Sep 17 00:00:00 2001 From: Victor Sych Date: Thu, 21 May 2026 04:59:37 +0200 Subject: [PATCH] Re-upload atlas textures after page merges --- addons/addon-webgl/src/GlyphRenderer.ts | 4 ++++ addons/addon-webgl/src/RectangleRenderer.ts | 5 +++-- addons/addon-webgl/src/WebglRenderer.ts | 17 ++++++++--------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/addons/addon-webgl/src/GlyphRenderer.ts b/addons/addon-webgl/src/GlyphRenderer.ts index b412f2d89..46b8efeb9 100644 --- a/addons/addon-webgl/src/GlyphRenderer.ts +++ b/addons/addon-webgl/src/GlyphRenderer.ts @@ -373,6 +373,10 @@ export class GlyphRenderer extends Disposable { public setAtlas(atlas: ITextureAtlas): void { this._atlas = atlas; + this.invalidateAtlasTextures(); + } + + public invalidateAtlasTextures(): void { for (const glTexture of this._atlasTextures) { glTexture.version = -1; } diff --git a/addons/addon-webgl/src/RectangleRenderer.ts b/addons/addon-webgl/src/RectangleRenderer.ts index 4ad8d66ff..81e129e5c 100644 --- a/addons/addon-webgl/src/RectangleRenderer.ts +++ b/addons/addon-webgl/src/RectangleRenderer.ts @@ -336,8 +336,9 @@ export class RectangleRenderer extends Disposable { } } - if (vertices.attributes.length < offset + 4) { - vertices.attributes = expandFloat32Array(vertices.attributes, this._terminal.rows * this._terminal.cols * INDICES_PER_RECTANGLE); + if (vertices.attributes.length < offset + INDICES_PER_RECTANGLE) { + // +1 for the viewport-clear rectangle at offset 0. + vertices.attributes = expandFloat32Array(vertices.attributes, (this._terminal.rows * this._terminal.cols + 1) * INDICES_PER_RECTANGLE); } $x1 = startX * this._dimensions.device.cell.width; $y1 = y * this._dimensions.device.cell.height; diff --git a/addons/addon-webgl/src/WebglRenderer.ts b/addons/addon-webgl/src/WebglRenderer.ts index 578955fc1..500308bd1 100644 --- a/addons/addon-webgl/src/WebglRenderer.ts +++ b/addons/addon-webgl/src/WebglRenderer.ts @@ -29,13 +29,7 @@ import { combinedDisposable, Disposable, MutableDisposable, toDisposable } from import { createRenderDimensions } from 'browser/renderer/shared/RendererUtils'; const enum Constants { - /** - * Upper bound on how many times `renderRows` re-runs `_updateModel` after detecting a - * mid-update atlas page merge. A single retry should always suffice once every glyph - * needed by the frame is cached; the cap exists purely as a safety net against an - * unexpected runaway, not as a tuned value. - */ - MERGE_RETRY_LIMIT = 3 + MERGE_RETRY_LIMIT = 32 } export class WebglRenderer extends Disposable implements IRenderer { @@ -385,13 +379,18 @@ export class WebglRenderer extends Disposable implements IRenderer { this._updateModel(start, end); } - // A mid-update atlas page merge shifts cached glyphs to new texture pages, leaving - // stale texturePage values in the vertex buffer. Re-run a full update if that happened. + // A mid-update atlas page merge invalidates vertex data and may not bump the host + // page's version, so re-run the update and force a full texture rebind. + let merged = false; let mergeRetries = 0; while (this._charAtlas && this._glyphRenderer.value.beginFrame() && mergeRetries++ < Constants.MERGE_RETRY_LIMIT) { + merged = true; this._clearModel(true); this._updateModel(0, this._terminal.rows - 1); } + if (merged) { + this._glyphRenderer.value.invalidateAtlasTextures(); + } // Render this._rectangleRenderer.value.renderBackgrounds();