Fix shared-atlas garble: track page changes per renderer, not consume-once

TextureAtlas.beginFrame() returned a consume-once boolean, so after a page
merge only the FIRST renderer to draw rebuilt its vertex model. Terminals
sharing the atlas via CharAtlasCache kept stale texture-page indexes/coords
and drew garbled glyphs on their next partial render (vscode#322756).

Replace the flag with a monotonic TextureAtlas.pagesVersion, bumped on page
merges and overflow-page creation. Each GlyphRenderer tracks its own
last-seen value in beginFrame(), so every terminal sharing the atlas rebuilds
its model independently. setAtlas() resets the tracker to force a rebuild
when the atlas instance changes.

WebglSharedAtlasGarble now passes (was diff ~130/255 vs tolerance 14); full
webgl addon suite 74/74 green.
This commit is contained in:
Anthony Kim
2026-07-04 12:54:34 -07:00
parent 3113eaefaa
commit 32fdce592b
3 changed files with 38 additions and 14 deletions
+14 -10
View File
@@ -132,12 +132,16 @@ export class TextureAtlas implements ITextureAtlas {
}
}
private _requestClearModel = false;
public beginFrame(): boolean {
const result = this._requestClearModel;
this._requestClearModel = false;
return result;
}
/**
* Incremented whenever the structure of the pages array changes in a way that
* invalidates texture page indexes/coordinates cached outside the atlas (page
* merges, overflow page creation). Renderers compare this against their own
* last-seen value to decide when to rebuild their models; the atlas is shared
* across terminals, so a consume-once flag would only repair the first
* renderer to draw (see the shared-atlas garble in vscode#322756).
*/
private _pagesVersion = 0;
public get pagesVersion(): number { return this._pagesVersion; }
public clearTexture(): void {
if (this._pages[0].currentRow.x === 0 && this._pages[0].currentRow.y === 0) {
@@ -206,8 +210,8 @@ export class TextureAtlas implements ITextureAtlas {
// Add the new merged page to the end
this.pages.push(mergedPage);
// Request the model to be cleared to refresh all texture pages.
this._requestClearModel = true;
// Invalidate models so all texture pages are refreshed.
this._pagesVersion++;
this._onAddTextureAtlasCanvas.fire(mergedPage.canvas);
}
@@ -818,8 +822,8 @@ export class TextureAtlas implements ITextureAtlas {
this._overflowSizePage = new AtlasPage(this._document, this._config.deviceMaxTextureSize);
this.pages.push(this._overflowSizePage);
// Request the model to be cleared to refresh all texture pages.
this._requestClearModel = true;
// Invalidate models so all texture pages are refreshed.
this._pagesVersion++;
this._onAddTextureAtlasCanvas.fire(this._overflowSizePage.canvas);
}
activePage = this._overflowSizePage;