尝试以扩展 VS Code 中的代理模式!

集成终端性能改进

2017年10月3日 Daniel Imms, @Tyriar

集成终端的渲染引擎已针对即将发布的 Visual Studio Code 1.17 版本进行了彻底重写,旨在提升性能。在此版本中,我们从基于 DOM 的渲染系统转向使用 HTML canvas 元素。

DOM 渲染

这有些令人惊讶,但在一个为显示静态文档而设计的系统中,渲染交互式终端是可能的。然而,随着时间的推移,我们发现 DOM 提供的某些功能需要被覆盖才能解决几个问题

选择:在覆盖终端使用场景时,与 DOM 的选择系统进行了大量对抗。由于我们总是只渲染 DOM 可见的内容,因此如果不重新实现选择功能,就无法选择多页内容。滚动还会导致选择被取消。为了解决这些问题,我们添加了自定义选择逻辑。

字符未对齐:由于许多等宽字体对某些 Unicode 字符并非严格等宽,这可能导致像下面图片右侧所示的情况

Characters to the right of the terminal could become misaligned when Unicode was used

一个解决方法是将所有 Unicode 字符包裹在固定宽度的 span 中,但这会增加渲染一帧所需的时间。

过度的垃圾回收:由于渲染终端所需的元素数量,垃圾回收器需要频繁清理内存,这通常会显著延长渲染时间。为了尝试解决此问题,我们设置了一个对象池,允许 DOM 元素被回收利用。

性能:无论我们如何努力解决这些问题,性能总是会受到布局引擎施加的硬性限制。

绕过布局引擎

在某些情况下,仅合成元素和进行布局就可能比一帧(16.6ms)花费更长时间,这对于我们希望在终端中保持流畅的每秒 60 帧(FPS)是不可接受的。解决方案是采用一种新的基于 canvas 的渲染引擎。

<canvas> HTML 元素允许使用 JavaScript API 绘制图形和文本。

渲染层

多个被称为“渲染层”的 canvas 元素用于简化终端不同部分的渲染。

当前的层,按顺序,是

  1. 文本:背景色和前景色文本,此层是不透明的。
  2. 选择:使用鼠标进行选择。
  3. 链接:悬停在链接上时显示的下划线。
  4. 光标:终端的光标。

将这些部分分离成各自的小组件,极大地简化了绘制过程。

仅绘制已更改的内容

新渲染器的一个重要部分是它只绘制已更改的内容。为此,它维护一个精简的内部模型,其中包含单元格绘制状态的最小信息量。然后,该状态用于在执行更昂贵的绘制操作之前,快速检查单元格何时需要更改。对于文本层,此模型包括对字符、文本样式、前景色和背景色的引用。

这与之前的渲染引擎形成对比,后者即使在没有内容更改的情况下,也会将整行从 DOM 中移除、重建并重新添加。

Only individual character changes are now drawn to the screen

上图中绿色的矩形表示被重绘的区域。

纹理图集

纹理图集用于进一步提升渲染时间。在后台有一个 ImageBitmap,它包含了默认背景色上最常见样式的所有 ASCII 字符。

在绘制这些样式的文本时,使用纹理图集而不是常规调用 CanvasRenderingContext2D.fillText。由于 ImageBitmap 与 GPU 共同定位,绘制速度得到了显著提升。

Behind the scenes an image is maintained containing the most common characters

强制跳帧

由于 DOM 渲染的速度,有必要跳过额外的帧以确保解析器有足够的 CPU 时间。虽然这使得命令运行速度比以前更快,但大量数据流经终端会导致帧率降至 10 FPS 以下。

有了新的渲染器,此限制已被解除,您现在可以在终端中享受高达 60 FPS 的帧率。

60 frames per second is now possible in the terminal

结果

我们的基准测试表明,集成终端现在根据情况,渲染速度大约是以前的 5 到 45 倍。即使您没有注意到响应速度和帧率的提升,更快的渲染也意味着更少的电池使用!我们希望您喜欢这些性能改进,它们将在几天内随 VS Code 1.17 版本推出,并且现在已可在 Insiders 版本中测试。

您还可以查看添加此功能的 原始 xterm.js 拉取请求,以了解更多详细信息。

编码愉快!

Daniel Imms,VS Code 团队成员 @Tyriar