Terminal renderer pool¶
Terminal tabs stay mounted so PTYs and development servers continue to run when a tab is hidden. A renderer pool keeps this persistent-tab model within a bounded GPU and memory budget.
Slot lifecycle¶
The pool in src/modules/terminal/lib/rendererPool.ts has a maximum of nine renderer slots, aligned with the pane and tab resource budget. A slot owns an xterm terminal, fit and search addons, serialization support and an optional WebGL addon.
Slots are acquired when a leaf becomes visible. Hidden idle leaves can release their WebGL slot while retaining their terminal buffer. Hidden busy leaves keep their live grid parked with rendering paused.
Acquisition order¶
When a leaf becomes visible, the pool looks for:
- a slot already bound to that leaf;
- a retained slot for that leaf;
- a clean idle slot;
- an evictable idle slot when the pool is full.
Eviction serializes a retained buffer only when it is safe to do so.
DormantRing¶
Leaves without a renderer slot keep receiving PTY bytes in a bounded DormantRing. The ring is capped at 1 MiB and drops the oldest blocks on overflow. Draining resumes at a line boundary so a partial escape sequence is not replayed from the middle.
Never serialize a busy leaf¶
A leaf that is running a foreground command, an agent, an alternate-screen TUI or a block-shell operation must never be serialized and evicted. Replaying incremental TUI repaints over a stale snapshot can wipe the visible application state. Busy leaves are parked instead.
WebGL lifecycle¶
WebGL addons are created for visible slots and can be reaped after a grace period when slots are parked. The terminal falls back to the DOM renderer when WebGL is unavailable and recovers from context loss.
Invariants¶
- The pool never grows without bound.
- Busy leaves are never serialized or evicted.
- Idle hidden leaves keep their buffer even when the renderer is released.
- The DormantRing has a fixed memory cap.
- A retained leaf avoids a full terminal reset and snapshot replay.