尝试以扩展 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.6 毫秒),如果我们要保持终端每秒 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 版本发布,目前已可在 Insider 构建版中进行测试。

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

编码愉快!

Daniel Imms,VS Code 团队成员 @Tyriar