You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
WebGL: clearTextureAtlas() on one Terminal corrupts other Terminals sharing the atlas (clearTexture doesn't request a model clear) #6014
When several Terminal instances share one WebGL glyph atlas (which they do automatically whenever their render config matches — same font/theme/DPR — via CharAtlasCache.acquireTextureAtlas), calling WebglAddon.clearTextureAtlas() on one terminal renders the other terminals' text garbled or blank until something forces a full refresh on them (e.g. a resize).
Root cause: TextureAtlas.clearTexture() mutates the shared atlas but does not set _requestClearModel, so the sibling renderers are never told to rebuild their model.
Mechanism (verified against master)
CharAtlasCache.acquireTextureAtlas() returns the sameTextureAtlas instance to every terminal whose config is equal — the atlas is shared, only the per-terminal GlyphRenderer is not.
WebglRenderer.clearTextureAtlas() does:
this._charAtlas?.clearTexture();// mutates the SHARED atlasthis._clearModel(true);// rebuilds only THIS renderer's modelthis._requestRedrawViewport();// redraws only THIS renderer
TextureAtlas.clearTexture() clears every page and the glyph caches, but does not set _requestClearModel = true.
For a sibling terminal that didn't change, beginFrame() returns false (the flag was never set), so it only updates changed rows. Its unchanged rows keep their old per-cell vertex/texture coordinates, which now point into an emptied/repositioned atlas → garbled or blank glyphs.
The asymmetry is the tell: the page-merge / overflow-page paths in TextureAtlas (_createNewPage, the two this._requestClearModel = true assignments) deliberately set the flag because they mutate the shared atlas and every sharing renderer must rebuild. clearTexture() mutates the shared atlas the same way but skips that signal.
Reproduction
Create two Terminals with identical options and load WebglAddon on each (so they share one atlas).
Write content to both (CJK + box-drawing makes the corruption obvious).
Call clearTextureAtlas() on terminal A only.
Terminal B's unchanged rows render garbled/blank; a resize (full refresh) on B repairs it.
Suggested fix
Have TextureAtlas.clearTexture() set this._requestClearModel = true (mirroring _createNewPage), so every renderer sharing the atlas rebuilds its model on the next frame. Alternatively, WebglRenderer.clearTextureAtlas() could propagate the model-clear to all terminals sharing the atlas.
Environment
@xterm/addon-webgl (observed on 0.19.0; mechanism confirmed still present on master)
Reproduced in a multi-terminal Electron app where panes share a render config.
Some extra data on this, plus a trigger I don't think has been reported yet.
Trigger: display switch, not just OS resume. On macOS (Apple Silicon, built-in Retina XDR, VS Code 1.123.0) the integrated terminal garbles or goes blank after moving the window between displays or switching away to another app and back. It's intermittent. The tell is that a newly opened terminal is garbled too — it re-acquires the same cached atlas via configEquals — and only closing the entire terminal panel fixes it, which drops the last owner so CharAtlasCache disposes the atlas and a fresh one gets built. Selecting the text makes it legible again because the selection forces those cells to redraw.
The diagnosis in this issue no longer matches master._requestClearModel was removed when #6042 landed (2026-07-13); it's now pageLayoutVersion. WebglRenderer.clearTextureAtlas() also does clear the caller's model already:
So the caller is fine. The actual gap is that TextureAtlas.clearTexture() wipes every page and both cache maps but never bumps _pageLayoutVersion — the invalidation channel #6042 added for exactly this class of bug. Page merges (_createNewPage) and evictions both bump it; clearTexture() was missed. Sibling owners therefore never see the change in GlyphRenderer.beginFrame() and keep vertex data pointing into the rows that were just wiped. Once the clearing terminal rasterizes new glyphs into those freed rows, the siblings draw whatever landed there — which is why the corruption looks like other text rather than blank cells.
This also explains the VS Code report specifically: terminalNativeContribution.ts calls forceRedraw() → Terminal.clearTextureAtlas() on onDidResumeOS, and xtermTerminal.ts exposes the same on other paths.
Repro, as a test. I extended WebglSharedAtlasGarble.test.ts (the harness from #6042) with a second terminal case. It fails on master — terminal B's header row renders terminal A's colored flood glyphs, max channel diff ~128 against a limit of 14 — and passes with a one-line bump of _pageLayoutVersion in clearTexture(). Full addon-webgl suite (60 tests) and unit tests (2403) stay green.
Note for anyone landing here from a stable install: the fix for this (#6055, merged 2026-07-27) has not shipped in a stable release. npm latest for @xterm/addon-webgl is still 0.19.0, published 2025-12-22, so the atlas fixes exist only in 0.20.0-beta.*.
Upgrading is also not a cheap addon bump — 0.20.0-beta.298 hard-peer-deps @xterm/xterm ^6.1.0-beta.301, so you move the core terminal off 6.0.0 stable too.
Userland mitigation on 0.19.0, if you cannot take the beta: stop calling clearTextureAtlas() yourself. The atlas is shared across every terminal with equal font/theme config (CharAtlasCache, ownedBy: Terminal[]), so an explicit clear from one terminal has cross-pane blast radius. In my case (Electron embedder, several visible terminals) two call sites were doing it — one on pane activation, one after loading the WebGL addon — which produced a ~1s glyph rebuild on the focused pane plus garbled cells in untouched siblings. Removing both fixed the explicit-clear half of this on stable. Config changes acquire a fresh atlas from the cache by key anyway, so an explicit clear is rarely doing useful work.
Two honest limits on that workaround:
It only covers clears you trigger. Internal atlas page merges (Fix stale rendering after shared texture atlas page merges #6042) remain exposed on 0.19.0 with no userland fix — if you see this symptom without any clearTextureAtlas() call of your own, that is the case you are in, and the beta is the only route.
If the garbling comes with Too many active WebGL contexts. Oldest context will be lost. in the console, it is not this issue — that is WebglAddon.dispose() does not release the underlying WebGL2 context #6068 (dispose never releases the GL context), which the atlas fixes do not address and which needs its own separate workaround. The two look nearly identical: correct cell geometry, wrong glyph contents, on a terminal you did not touch. I hit both simultaneously on 0.19.0 and each needed its own fix; there is a triage table in #6068 for telling them apart.
Activity
heudev commentedon Jul 16, 2026
Some extra data on this, plus a trigger I don't think has been reported yet.
Trigger: display switch, not just OS resume. On macOS (Apple Silicon, built-in Retina XDR, VS Code 1.123.0) the integrated terminal garbles or goes blank after moving the window between displays or switching away to another app and back. It's intermittent. The tell is that a newly opened terminal is garbled too — it re-acquires the same cached atlas via
configEquals— and only closing the entire terminal panel fixes it, which drops the last owner soCharAtlasCachedisposes the atlas and a fresh one gets built. Selecting the text makes it legible again because the selection forces those cells to redraw.The diagnosis in this issue no longer matches master.
_requestClearModelwas removed when #6042 landed (2026-07-13); it's nowpageLayoutVersion.WebglRenderer.clearTextureAtlas()also does clear the caller's model already:So the caller is fine. The actual gap is that
TextureAtlas.clearTexture()wipes every page and both cache maps but never bumps_pageLayoutVersion— the invalidation channel #6042 added for exactly this class of bug. Page merges (_createNewPage) and evictions both bump it;clearTexture()was missed. Sibling owners therefore never see the change inGlyphRenderer.beginFrame()and keep vertex data pointing into the rows that were just wiped. Once the clearing terminal rasterizes new glyphs into those freed rows, the siblings draw whatever landed there — which is why the corruption looks like other text rather than blank cells.This also explains the VS Code report specifically:
terminalNativeContribution.tscallsforceRedraw()→Terminal.clearTextureAtlas()ononDidResumeOS, andxtermTerminal.tsexposes the same on other paths.Repro, as a test. I extended
WebglSharedAtlasGarble.test.ts(the harness from #6042) with a second terminal case. It fails on master — terminal B's header row renders terminal A's colored flood glyphs, max channel diff ~128 against a limit of 14 — and passes with a one-line bump of_pageLayoutVersioninclearTexture(). Fulladdon-webglsuite (60 tests) and unit tests (2403) stay green.PR: #6055
Note that #6018 sets
this._requestClearModel = true, a field that no longer exists post-#6042, so it can't build against master as-is.openwong2kim commentedon Jul 27, 2026
🫡
0xFlo commentedon Aug 22, 2026
Note for anyone landing here from a stable install: the fix for this (#6055, merged 2026-07-27) has not shipped in a stable release. npm
latestfor@xterm/addon-webglis still 0.19.0, published 2025-12-22, so the atlas fixes exist only in0.20.0-beta.*.Upgrading is also not a cheap addon bump —
0.20.0-beta.298hard-peer-deps@xterm/xterm ^6.1.0-beta.301, so you move the core terminal off 6.0.0 stable too.Userland mitigation on 0.19.0, if you cannot take the beta: stop calling
clearTextureAtlas()yourself. The atlas is shared across every terminal with equal font/theme config (CharAtlasCache,ownedBy: Terminal[]), so an explicit clear from one terminal has cross-pane blast radius. In my case (Electron embedder, several visible terminals) two call sites were doing it — one on pane activation, one after loading the WebGL addon — which produced a ~1s glyph rebuild on the focused pane plus garbled cells in untouched siblings. Removing both fixed the explicit-clear half of this on stable. Config changes acquire a fresh atlas from the cache by key anyway, so an explicit clear is rarely doing useful work.Two honest limits on that workaround:
clearTextureAtlas()call of your own, that is the case you are in, and the beta is the only route.Too many active WebGL contexts. Oldest context will be lost.in the console, it is not this issue — that is WebglAddon.dispose() does not release the underlying WebGL2 context #6068 (dispose never releases the GL context), which the atlas fixes do not address and which needs its own separate workaround. The two look nearly identical: correct cell geometry, wrong glyph contents, on a terminal you did not touch. I hit both simultaneously on 0.19.0 and each needed its own fix; there is a triage table in #6068 for telling them apart.