feat(terminal): default renderer to canvas

This commit is contained in:
zhangheng
2026-06-24 17:23:13 +08:00
parent a145a19636
commit 7593561761
2 changed files with 16 additions and 11 deletions

View File

@@ -4,7 +4,7 @@
> 定位提醒:本项目目标不是复刻 xterm.js 这个通用 JS 终端库,而是按 [TERMINAL_HANDOFF.md](TERMINAL_HANDOFF.md) 的方向,用 `Axum + portable-pty`(后端)+ `Leptos + Rust/WASM`(前端)沉淀一套**高性能、可复用的 Rust 终端组件**。因此追平的重点是「让真实终端会话好用且性能好」,而非铺平 xterm.js 的全部 API 面。
>
> **性能目标**:终端默认渲染器已切到 WebGL glyph atlas / per-cell quad,保留 Canvas 2D 与 DOM renderer 作为回退。Canvas 2D 解决了大部分 DOM 节点膨胀、长输出重排和高频输出卡顿问题WebGL 路径现在按字形 atlas 缓存并批量绘制单元格 quad
> **性能目标**:终端默认渲染器已切到 Canvas 2D保留 WebGL glyph atlas / per-cell quad 与 DOM renderer 作为可切换路径。最新基准显示 Canvas 2D 在 10k 行输出、200×80 / 240×100 viewport 下吞吐和 frame time 都明显优于当前 WebGL 实现WebGL 继续作为优化回归目标保留
---
@@ -37,7 +37,7 @@
| 文本选区 + 复制自建模型Ctrl-Shift-C / 选区存在时 Ctrl+C跨滚动 | `component.rs` 鼠标处理 / `core.rs::selection_text` |
| 鼠标上报vim/htop/tmux 鼠标SGR/Default/UTF-8 编码,滚轮) | `component.rs` 鼠标处理 / `encode_mouse_report` |
| 中文 / IME 输入(拼音/日文/韩文,隐藏 textarea + composition 事件) | `component.rs::handle_input` / `handle_composition_*` |
| WebGL glyph atlas 默认渲染器per-cell quad、DPR glyph atlas、背景/underline/cursor backdrop、Canvas/DOM 回退) | `component.rs::Renderer` / `render_webgl` / `GlyphAtlas` / `render_canvas` / `canvas_render_plan` |
| Canvas 2D 默认渲染器 + WebGL glyph atlas 可选路径per-cell quad、DPR glyph atlas、背景/underline/cursor backdrop、DOM 回退) | `component.rs::Renderer` / `render_canvas` / `canvas_render_plan` / `render_webgl` / `GlyphAtlas` |
---
@@ -138,11 +138,11 @@
### P4 — Canvas / WebGL 渲染器(性能目标)✅ 已完成
**现状**WebGL glyph atlas / per-cell quad 已作为默认渲染器Canvas 2D 与 DOM renderer 保留为回退路径。长输出、输入后 live/history 稳定性、IME textarea buffer 坐标、DPR backing store、虚拟滚动 sizer、selection overlay、cursor overlay 均已验证。
**现状**Canvas 2D 已作为默认渲染器;WebGL glyph atlas / per-cell quad 与 DOM renderer 保留为可切换路径。长输出、输入后 live/history 稳定性、IME textarea buffer 坐标、DPR backing store、虚拟滚动 sizer、selection overlay、cursor overlay 均已验证。2026-06-24 benchmark 显示 Canvas 2D 在当前实现下优于 WebGL因此默认策略先以 Canvas 为准。
**已实现**
1. **WebGL glyph atlas 默认渲染器**
- `Renderer::WebGl` 默认,`Renderer::Canvas` / `Renderer::Dom` 作为回退
1. **Canvas 2D 默认渲染器 + WebGL 可选路径**
- `Renderer::Canvas` 默认,`Renderer::WebGl` / `Renderer::Dom` 作为可切换回归路径
- WebGL2 初始化 shader/program/vertex buffer/atlas texture/fallback texture。
- `GlyphAtlas` 按 grapheme + bold/italic + cell width 缓存字形ASCII、CJK wide 字符、组合字符按 `unicode-segmentation` / `unicode-width` 拆成 cell glyph。
- 文本通过 per-cell quad 批量绘制,颜色作为 vertex attribute小字号不再走整屏纹理二次采样。
@@ -245,7 +245,12 @@
- `/rustui/terminal` 页面提供 WebGL / Canvas / DOM runtime 切换。
- 支持 `?renderer=webgl|canvas|dom` 初始化,便于浏览器回归和 benchmark 固定模式。
- `scripts/terminal-bench.mjs --renderer webgl|canvas|dom` 复用同一入口跑对比,并将截图按 renderer 命名。
4. 与 Canvas 2D 做 benchmark 对比,按 viewport size / output rate 自动选择 renderer
4. **与 Canvas 2D 做 benchmark 对比,按 viewport size / output rate 自动选择 renderer**
- 2026-06-24 基线10k 行输出:
- `200x80`WebGL `155.27 rows/sec`frame p95 `283.3ms`Canvas `311.67 rows/sec`frame p95 `116.7ms`
- `240x100`WebGL `92.48 rows/sec`frame p95 `399.9ms`Canvas `157.65 rows/sec`frame p95 `116.8ms`
- 两种 renderer 的 pixel smoke 均通过Canvas 可直接读取 canvas 像素WebGL 走 screenshot smoke。
- 当前策略:默认 CanvasWebGL 继续通过页面切换、`?renderer=webgl``--renderer webgl` 保留优化回归入口。
---

View File

@@ -105,12 +105,12 @@ impl Drop for WebSocketHandle {
// TerminalPanel component
// ---------------------------------------------------------------------------
/// Which renderer draws the terminal grid. WebGL is the default performance
/// path; Canvas and DOM keep stable fallbacks for unsupported browsers.
/// Which renderer draws the terminal grid. Canvas is the default stable
/// performance path; WebGL and DOM stay available for regression comparison.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Renderer {
#[default]
WebGl,
#[default]
Canvas,
Dom,
}
@@ -177,7 +177,7 @@ impl Default for TerminalOptions {
min_cols: MIN_COLS,
min_rows: MIN_ROWS,
scrollback: super::core::DEFAULT_SCROLLBACK,
renderer: Renderer::WebGl,
renderer: Renderer::Canvas,
font: TerminalFontOptions::default(),
theme: TerminalTheme::default(),
cursor: TerminalCursorOptions::default(),
@@ -2883,7 +2883,7 @@ mod tests {
assert!(renderer_uses_canvas_surface(Renderer::WebGl));
assert!(renderer_uses_canvas_surface(Renderer::Canvas));
assert!(!renderer_uses_canvas_surface(Renderer::Dom));
assert_eq!(Renderer::default(), Renderer::WebGl);
assert_eq!(Renderer::default(), Renderer::Canvas);
}
#[test]