perf(console): wrap only new console lines via a per-line cache

Each batch of new output re-wrapped all N visible lines glyph by glyph,
allocating a fresh segment vector per line; a line evicted at the
10,000-line cap shifted every index, so nothing could be kept. And on
every frame, idle or not, seeding the scanline shading summed the wrap
rows of every line above the view.

- The model numbers its lines and the view keeps one wrap per line,
  aligned by that number, so an eviction drops a prefix instead of
  forcing a rebuild. New output wraps only the new lines; the layout is
  then a prefix sum over the N visible lines, with no measuring and no
  per-line allocation.
- The wraps are kept while the wrap width, line height, font size, font
  (by ImFont::FontId) and rasterizer density are exactly unchanged. A
  line-spacing change only re-sums heights; filters and fold toggles
  reuse the cached wraps. A resize drag still re-wraps every visible
  line per frame, as before, but reuses their storage.
- The scanline seed is read from the prefix sum: O(1) per frame.

Heights, offsets and segments are bit-identical to the old rebuild. A
font reload at an unchanged size (the colour-emoji toggle) now re-wraps
the console at once; before, it waited for the next line of output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 09:50:03 -05:00
co-authored by Claude Opus 5.5
parent c2c2a8325b
commit ee0a486676
7 changed files with 541 additions and 61 deletions
+8 -4
View File
@@ -195,11 +195,15 @@ private:
// resulting visible-set generation + wrap width + line height/zoom.
std::uint64_t vis_key_ = ~0ull; // key of the last computeVisibleLines
std::uint64_t vis_generation_ = 0; // bumped whenever visible_indices_ is rebuilt
std::uint64_t layout_key_ = ~0ull; // key of the last BuildConsoleLayout
std::uint64_t layout_key_ = ~0ull; // key of the last layout assembly
// Wrap layout for the visible lines (segments + per-line heights + cumulative Y),
// recomputed each frame by the pure BuildConsoleLayout (console_text_layout.h) and
// consumed by the renderer + hit-testing.
// Per-raw-line wrap cache (index == model_ index after sync). layout_.segments point INTO it, so it is only
// mutated by sync() immediately followed by AssembleConsoleLayout (renderOutput), or by reset() together
// with clearing layout_ (clear()). Touch it nowhere else.
ConsoleWrapCache wrap_cache_;
// Wrap layout for the visible lines (segments + per-line heights + cumulative Y), re-assembled from
// wrap_cache_ when the visible set / geometry changes, and consumed by the renderer + hit-testing.
mutable ConsoleLayout layout_;
// Commands popup (command explorer) — indexes into the active executor's commandReference()