Skia与Impeller的对比测试

上一篇文章,我们了解了Impeller在渲染方面的优势以及整个渲染流程,接下来我将通过实际实验来测试下Skia与Impeller在渲染性能上的优劣。

我编写了一段自定义Canvas动画绘制的代码,在屏幕上绘制一个圆形,并在随机位置产生气泡,以模拟复杂UI动画场景。效果如下图所示:

该动画运行一段时间以收集足够帧数据。我们分别在ImpellerSkia 渲染器下运行此应用,并记录性能数据。

我使用的设备是Pixel 8 pro,得到的渲染数据如下: 使用skia的渲染表现:

使用Impeller的渲染表现:

同时我下载了devtool的相关数据,并编写了python脚本,对数据进行了分析,得到了如下表格:

指标 Impeller 渲染 Skia 渲染
UI线程帧构建平均耗时 (ms) 2.10 2.07
UI线程帧构建 95th 百分位 耗时 (ms) 3.45 3.48
GPU线程光栅绘制平均耗时 (ms) 2.81 4.05
GPU线程光栅绘制 95th 百分位 耗时 (ms) 4.00 5.55
每帧总耗时平均值 (ms) 6.57 7.71
每帧总耗时 95th 百分位 (ms) 8.74 10.24
帧在8.33ms内完成渲染比例 (%) (120Hz帧率要求) 91.6% 67.1%
帧耗时超过16.67ms比例 (%) (低于60fps的掉帧) 0.29% 0.09%

可以看到,Impeller 相比 Skia 在 GPU 渲染阶段有明显优势:平均每帧 GPU 绘制耗时降低约30%(2.81ms vs 4.05ms),95th百分位耗时降低约1.5ms。这意味着大部分帧Impeller的GPU线程负担更小,帧渲染更快。而UI线程帧建构时间两者基本相当,平均约2ms,说明更换渲染器对框架层的布局建树开销几乎无影响。

在总帧时间上,Impeller 模式平均每帧约6.57ms,Skia约7.71ms;结合120Hz刷新率8.33ms的预算,Impeller 大多数帧都能赶在下一次VSync前完成。如上表所示,在Impeller下有高达91.6%的帧耗时不超过8.33ms,可以实现接近120fps的流畅度,而 Skia 下这一比例只有约67.1%。换言之,Skia 渲染有约三分之一帧未能在120Hz间隔内完成(这些帧通常耗时落在8~12ms之间),需要等到下一个刷新周期才显示出来,等效帧率跌至60--120fps之间。Impeller 因为减少了 GPU 卡顿,在相同动画下绝大部分帧都达到了120fps要求。两者在超过16.67ms(低于60fps的明显掉帧)情况都很少,测试中Skia仅有~0.09%帧、Impeller约0.29%帧超过16.67ms,不过Impeller的几次超时是集中在首次创建管线的少数帧,上文平均数已平滑了这类一次性开销。

上述统计和图表反映出 Impeller 的总体性能优势:更低的平均帧渲染时间和更少的帧延迟抖动。接下来我们深入各类性能事件,逐项分析差异来源。

  1. UI线程帧建构(UI::Frame)

    首先是 UI 线程的帧构建阶段。UI线程主要执行框架的 build/layout 等操作,性能取决于UI结构和代码逻辑,本身不受渲染后端影响。因此切换 Impeller 不会影响 Dart 层UI布局构建效率,这一点从数据中得到验证。值得注意的是,我在 Skia 模式下捕获到一帧异常的 UI 构建耗时(约39.8ms),远高于正常水平,导致该帧总耗时达47ms。这可能由于偶发的垃圾回收或极端情况(与渲染器关系不大),Impeller 跑相同场景并未发生类似的异常。这提醒我们在分析时应关注整体趋势而非个别异常帧。总的来说,两种引擎的 UI 线程开销可以视为持平。

    不过,UI线程也可能受到 GPU 线程速度的间接影响:如果 GPU 无法及时处理上一帧,UI线程在 produce 新帧时可能阻塞等待(即 PipelineProduce 延迟)。这一情况常发生于连续多帧超时导致 pipeline 队列积压。在我收集到的数据中,Skia 有一些帧表现出 UI构建完成很快但总帧耗时接近两倍刷新间隔的情况,这表示UI曾等待 GPU 空闲。而 Impeller 由于 GPU 更快,极少出现此等待。后文Pipeline部分详述。

2. GPU线程光栅绘制(Rasterizer::Draw)

GPU线程的表现是 Impeller 相对 Skia 差异最大的部分。Rasterizer::Draw 事件表示 GPU 线程将 UI线程提交的layer树光栅化为屏幕图像的过程。在 Skia 中,Rasterizer 绘制每帧平均耗时约4.05ms,中位数比 Skia 快了约1.4ms。这种大幅提升说明 Impeller 针对 GPU 渲染执行做了优化。一方面,Impeller 的渲染实现利用现代图形API更高效,例如可能通过批处理减少驱动开销、利用并发等手段,让同样的绘制工作耗时更短。另一方面,没有插入运行时的 shader 编译步骤,也避免了 GPU线程的突然卡顿。

值得强调的是,Skia 渲染中 GPU 耗时出现了一些明显的波动和尖峰,例如我们观察到 Skia 有多帧Rasterizer耗时达到912ms,远超平常。这往往对应着Shader JIT 编译过程。

不过Impeller 虽然没有运行时 shader 编译,但并非完全没有一次性开销。在我们的Impeller追踪数据中,出现了 PipelineVK::Create 等事件------这是 Impeller 为特定渲染操作创建GPU管线状态的过程(对应第一次使用某类渲染管线,例如第一次绘制某种组合的Shader/Blend模式)。这些事件一般也只在首次发生,Impeller会将结果缓存供后续使用。大多数 Pipeline 创建非常快,未对帧率造成影响,但我观察到有两次管线创建使得当帧 GPU 耗时激增:一次耗时约17ms(帧28),另一次约35ms(帧178),分别导致这两帧总时间达到20ms和40ms,出现明显卡顿。推测第一次发生在动画初始时,Impeller需要创建一批基础管线;第二次可能是动画过程中引入了新的绘制效果触发新的管线创建。这些 一次性的长耗时 构成了Impeller帧耗时分布的"长尾",也是上文统计中Impeller极少数帧超过16ms的原因。不过,这与Skia频繁的小幅卡顿有本质不同------Impeller的管线创建只在首次出现,属于可预见且可避免的开销(例如可在应用初始化或闲时预先触发这些绘制来完成管线创建),而 Skia 的 JIT shader 编译则难以完全预测,可能在应用运行过程中的任何新场景触发。

3. PipelineProduce 阶段与帧同步

PipelineProduce 是 Flutter 框架将 UI线程构建的场景提交给 GPU线程处理的过程。如果 GPU线程滞后于UI线程,那么UI在这个阶段可能被阻塞等待。通过分析 SceneDisplayLag 事件和帧间隔,我们可以了解掉帧与管线阻塞情况。我的统计显示,在相近时长的采样中,Skia 模式触发了 29 次 SceneDisplayLag(帧未及时显示的延迟事件),而 Impeller 仅有 12 次,Skia 出现帧滞后的频率约为 Impeller 的2.4倍。这与之前帧在8ms内完成的比例吻合:Skia 更频繁地错过目标 vsync,导致 pipeline 队列堆积,从而出现UI等待 GPU的情况。具体表现如前文提到的Skia某帧UI耗时极低但总时长接近两个帧间隔------意味着UI构建完成后等待了将近一个帧时间,直到GPU处理完前一帧才能继续。这就是一次 PipelineProduce 延迟导致的整帧延后。Impeller 因更少错过vsync,UI很少需要等待,PipelineProduce 几乎都是即时完成,保障了帧流水线的畅通。

4. 着色器编译与GPU事件

Shader 编译事件在两种引擎表现出明显差异:在 Skia Timeline 中我们发现了多处 Shader 相关编译记录(例如 Compiler::convertProgram 等),总计约14次,与我们的帧耗时尖峰一一对应。而 Impeller Timeline 中没有任何 runtime ShaderCompile 事件,验证了其预编译设计的效果。取而代之,Impeller 有几十次 PipelineVK::Create 事件,如上节所述,表示管线创建。值得一提,Impeller 这样设计实际上是将需要编译的 Shader 集合限定在一个较小范围,并在引擎构建时就全部编译好。Impeller默认支持Flutter核心绘制效果的预编译,但如果应用使用自定义Shader或某些特殊绘制(比如滤镜),Impeller 可能暂不支持或仍需要特殊处理。因此在开启 Impeller 后,应确认应用的绘制效果都正常且性能受益。如果有使用 Scene.toImage、旧版API等情况,需要关注Impeller支持度。

GPU 内存与并发: 从本次测试的性能数据来看,两种引擎在GPU占用和并发调度上的区别不易直接量化。但根据官方文档,Impeller 利用现代GPU API的优势,在需要时可以跨多线程并行执行GPU相关任务,并对资源生命周期进行更精细的控制。这在极端复杂场景下可能进一步提高吞吐。本测试的Canvas动画尚不足以体现Impeller在并发上的潜力,但其"可预测性能"的目标使它更适合那些需要稳定高帧率的场景,例如富动画界面、游戏等。

综合上述分析,Flutter Impeller 渲染引擎在我自定义Canvas动画测试中表现出优于 Skia 的渲染性能。Impeller 有效地消除了由于运行时着色器编译导致的帧率抖动,使帧时间更加稳定。对于动画复杂、图形效果丰富的应用,启用 Impeller 可以显著减少卡顿,带来更顺滑的用户体验。

相关推荐
梦想改变生活6 小时前
《Flutter篇第一章》基于GetX 和 Binding、Dio 实现的 Flutter UI 架构
flutter·ui·架构
耳東陈10 小时前
[重磅发布] Flutter Chen Generator 必备脚本工具
flutter
亿刀11 小时前
【学习VPN之路】NET技术
android·flutter
浅蓝色11 小时前
Flutter平台判断问题,并适配鸿蒙
flutter
JarvanMo12 小时前
使用 Flutter Lints 提升你的 Flutter 代码:更整洁、更安全、更快速的应用
flutter
mobsmobs1 天前
Flutter开发环境搭建与工具链
android·flutter·ios·android studio·xcode
Bryce李小白1 天前
Flutter 主流 UI 框架总结归纳
flutter·ui
谕酱1 天前
flutter_ume停更了,那我就自己写一个flutter_mana
flutter·dart
顾林海1 天前
Dart 异步编程之 Future 详解
flutter·面试·dart