Chrome新一代光栅化后端Skia Graphite技术解析

本文详细介绍了Chrome团队推出的新一代光栅化后端Skia Graphite,该技术采用现代图形API和多线程架构,在Motionmark 1.3测试中性能提升15%,显著改善了页面交互响应速度和滚动流畅度。

介绍Skia Graphite:Chrome未来的光栅化后端

今天的"The Fast and the Curious"帖子涵盖了Skia新光栅化后端Graphite在Apple Silicon Macs版Chrome中的发布。Graphite帮助Chrome在Motionmark 1.3测试中获得卓越分数,并且是解锁Chrome图形未来大量改进的关键。

Chrome中Skia的简史

在Chrome中,Skia用于将来自Blink和浏览器UI的绘制命令渲染到屏幕上的像素,这个过程称为光栅化。Skia从一开始就为Chrome图形提供动力。随着网络的发展和变得更加复杂,Skia最终遇到了性能问题,这导致Chrome和Skia投资于一个名为Ganesh的GPU加速光栅化后端。

多年来,Ganesh发展成为一个稳固的高性能光栅化后端,GPU光栅化在Chrome的所有平台上基于GL启动(在Windows上通过ANGLE在D3D9/11上)。然而,Ganesh始终有一个以GL为中心的设计,有太多专门的代码路径,团队在尝试以原则性方式利用现代图形API实现优化时遇到了瓶颈。

这为团队重新思考GPU光栅化奠定了基础,以新的光栅化后端Graphite的形式从头开始。Graphite从一开始就被设计为原则性的,具有更少且更易理解的代码路径。这种前瞻性设计有助于利用现代图形API(如Metal、Vulkan和D3D12)和基于计算的光栅化路径等范例,并且默认是多线程的。

结果

在Chrome中使用Graphite,我们在Macbook Pro M3上将Motionmark 1.3分数提高了近15%。同时,我们改进了实际指标,如INP(交互到下一次绘制时间)、LCP(最大内容绘制时间)、图形流畅度(掉帧百分比)、GPU进程malloc内存使用等。这一切都意味着交互更加流畅,滚动时的卡顿减少,等待网站显示的时间更少。

Graphite和Ganesh的区别

现代图形API

Ganesh最初是在OpenGL ES上实现的,它对多线程或计算着色器等GPU功能的支持很少。从那时起,现代图形API(如Vulkan、Metal和D3D12)已经发展到利用多线程并暴露新的GPU功能。它们允许应用程序对何时以及如何执行和调度昂贵的工作(如分配GPU资源)有更多的控制,同时有效利用CPU和GPU。

虽然我们能够调整Ganesh以支持现代图形API,但它积累了足够的技术债务,以至于很难充分利用现代图形API的多线程和GPU计算能力。

对于Chrome中的Graphite,我们选择使用Chrome的WebGPU实现Dawn作为平台原生图形API(如Metal、Vulkan和D3D)的抽象层。Dawn为现代图形API中常见的能力提供了基线,并通过利用Dawn成熟且经过良好测试的原生后端而不是为Graphite从头开始实现它们,帮助我们减少长期维护负担。

2D深度测试

GPU渲染管线的核心部分是深度测试,它可以通过从前到后绘制不透明对象,然后从后到前绘制半透明对象来减少或消除过度绘制。在图形中,“过度绘制"指的是多次不必要地渲染相同像素,这可能对性能和电池寿命产生负面影响,尤其是在移动设备上。

Ganesh从未利用图形卡的深度测试能力,这 admittedly 是为渲染3D内容而不是加速2D图形而设计的。Ganesh由于在绘制不透明和半透明对象时依赖严格遵守画家顺序而遭受过度绘制。

Graphite扩展了Skia的GPU渲染以利用深度测试,为每个"绘制"分配一个z值,定义其画家顺序索引。虽然透明效果和图像仍然必须从后到前绘制,但前景中的不透明对象现在可以自动消除过度绘制。这意味着不透明绘制可以重新排序以最小化昂贵的GPU状态更改,同时依赖深度缓冲区产生正确输出。

深度测试还用于在Graphite中实现裁剪,通过将裁剪形状视为仅深度绘制,而不是像在Ganesh中那样维护裁剪堆栈。除了减少算法复杂性外,这种方法的一个显著好处是渲染"绘制"所需的着色器程序不依赖于裁剪堆栈的状态。

多线程

Chromium是一个复杂的多进程应用程序,渲染进程向共享的GPU进程发出命令,该进程负责实际显示网页、标签页甚至浏览器UI中的所有内容。GPU进程主线程是所有渲染工作的主要驱动者,也是所有GPU命令发出的地方。

由于Ganesh和OpenGL的单线程性质,只有有限的工作集可以移动到其他线程,这使得主线程容易过载,导致增加的卡顿和延迟,最终损害用户体验。

相比之下,Graphite的API设计用于利用现代图形API的多线程能力。Graphite的新核心API围绕独立的记录器(Recorders)构建,这些记录器可以在多个线程上产生记录(Recordings),且它们之间几乎不需要同步。尽管记录被提交到主线程上的GPU,但在产生记录时,更昂贵的工作被移动到其他线程,保持GPU主线程空闲。

性能悬崖和管线编译

当Ganesh最初实现时,图形卡的可编程能力相当有限,尤其是分支非常昂贵。为了解决这个问题,Ganesh有许多专门的着色器管线来处理常见情况。这些专门化很难预测,并且取决于与每个单独绘制相关的大量因素,导致基本上相同的页面内容产生大量不同的管线。由于这些管线必须各自编译,它不适用于现代网络内容,这些内容可能在任何时刻触发新管线的效果和动画,导致明显的卡顿。

Graphite的设计理念是尽可能合并渲染管线的数量,同时仍保持性能。这减少了必须编译的管线数量,并使Chrome能够确保它们在启动时编译,这样它们就不会中断活动浏览。Ganesh的专门化方法还导致了令人惊讶的性能悬崖。例如,虽然它可以处理简单情况,但真实页面内容通常是复杂的混合。通过合并管线,复杂内容可以像简单内容一样有效地渲染。

未来计划

多线程光栅化

目前,Graphite使用两个记录器集成到Chromium中:一个在主线程上处理网页内容瓦片和Canvas2D,另一个用于合成。未来,这种模型将开启许多令人兴奋的可能性,以进一步改进Chrome的性能。光栅化可以跨多个线程分叉,而不是用每个渲染器进程的任务饱和主GPU线程。

减少简单内容的GPU内存

Graphite记录还可以通过某些动态更改(如平移)重新发布到GPU。这可以用于加速滚动,同时消除重新发布渲染命令的不必要工作。这使我们能够自动减少缓存网页内容作为瓦片所需的GPU内存量。如果内容足够简单,绘制缓存图像和绘制其内容之间的性能差异可能值得跳过为其分配瓦片,而只是每帧重新渲染它。

GPU计算路径光栅化

在2D图形渲染领域,基于GPU计算的路径光栅ization非常流行,最近有像Pathfinder和vello这样的实现。我们希望在Skia中实现这些想法,可能使用混合方法。目前,Graphite在可能的情况下依赖MSAA,但在许多情况下我们不能这样做,因为旧集成GPU上的性能较差或非平铺GPU上的高内存开销,我们必须回退到使用图集进行缓存的CPU路径光栅化。基于GPU计算的路径光栅化将使我们能够改进MSAA的视觉质量(通常限于每像素4个样本)和CPU光栅化的性能。

这些是Chrome图形团队计划追求的未来方向,我们很高兴看到我们能推动到什么程度。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计