介绍Skia Graphite:Chrome未来的光栅化后端
2025年7月8日,星期二
今天的"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图形提供动力。随着Web的发展和变得更加复杂,Skia最终遇到了性能问题,这导致Chrome和Skia投资于一个名为Ganesh的GPU加速光栅化后端。
多年来,Ganesh发展成为一个稳固的高性能光栅化后端,并且GPU光栅ization在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围绕独立的Recorder展开,这些Recorder可以在多个线程上产生Recording,且它们之间需要最少的同步。尽管Recording被提交到主线程上的GPU,但更昂贵的工作在产生Recording时被移动到其他线程,保持GPU主线程空闲。
性能悬崖和管道编译
当Ganesh最初实现时,图形卡的可编程能力非常有限,尤其是分支非常昂贵。为了解决这个问题,Ganesh有许多专门的着色器管道来处理常见情况。这些专门化很难预测,并且依赖于与每个单独绘制相关的大量因素,导致 essentially 相同页面内容的不同管道爆炸式增长。由于这些管道必须各自编译,它不适用于现代Web内容,这些内容可能在任何时刻触发具有效果和动画的新管道,导致明显的卡顿。
Graphite的设计理念是尽可能合并渲染管道的数量,同时仍保持性能。这减少了必须编译的管道数量,并使Chrome能够确保它们在启动时编译,这样它们就不会中断主动浏览。Ganesh的专门化方法还导致了令人惊讶的性能悬崖。例如,虽然它可以处理简单情况,但真实页面内容通常是复杂的混合。通过合并管道,复杂内容可以像简单内容一样有效地渲染。
未来计划
多线程光栅化
目前,Graphite使用两个Recorder集成到Chromium中:一个在主线程上处理Web内容 tiles 和 Canvas2D,另一个用于合成。将来,这种模型将开启许多令人兴奋的可能性,以进一步提高Chrome的性能。而不是用来自每个渲染器进程的任务饱和主GPU线程,光栅化可以分叉到多个线程。
当前:
未来:
减少简单内容的GPU内存
Graphite recording 还可以通过某些动态更改(如平移)重新发布到GPU。这可以用于加速滚动,同时消除重新发布渲染命令的不必要工作。这使我们能够自动减少缓存Web内容所需的GPU内存量。如果内容足够简单,绘制缓存图像和绘制其内容之间的性能差异可能值得跳过为其分配 tile 并仅每帧重新渲染它。
GPU计算路径光栅化
在2D图形渲染领域,基于GPU计算的路径光栅化非常流行,最近有像Pathfinder和vello这样的实现。我们希望在Skia中实现这些想法,可能使用混合方法。目前,Graphite在可能的情况下依赖MSAA,但在许多情况下我们不能这样做,因为旧集成GPU上的性能较差或非平铺GPU上的高内存开销,我们必须回退到使用 atlas 进行缓存的CPU路径光栅化。基于GPU计算的路径光栅化将使我们能够改进MSAA的视觉质量(通常限于每像素4个样本)和CPU光栅化的性能。
这些是Chrome图形团队计划追求的未来方向,我们很高兴看到我们能推动到什么程度。
发布者:Michael Ludwig & Sunny Sachanandani