Flutter 多窗口重要优化合并,多窗口性能和实用性大幅提升

最近 #179874 这个 PR 终于重新通过 #192128 合并到 master 了,也就是从这个 PR 开始,Flutter 的 Multi-View 渲染,终于可以从“只要来一帧,所有 View 都重新 composite”,变成了 “哪一个 View 真脏了,只 composite 哪一个” 。

这个实现之前最麻烦的地方在于:Texture、Platform View、前后台切换这些变化,实际上不支持天然把 Flutter RenderObject 标成 dirty,所以这个 PR 大量代码其实都是在补齐这套 dirty 信号链。

这个 PR 其实也挺坎坷,原本的 Multi-View 处理比较粗暴,对应之前的 #164320, RendererBinding.drawFrame() 在完成 layout / paint 后,实际上的逻辑基本等价于:

也就是这里根本不管某个 RenderView 有没有变化,比如现在桌面 Multi-Window 有:

但是之前只要 A 请求了一帧,A/B/C 三个 RenderView 都会执行 compositeFrame()。

但是这个问题浪费的可不只是一个 Dart 函数调用场景,RenderView.compositeFrame() 会重新完成 Layer Tree 到 ui.Scene 的构建,然后通过 FlutterView.render(scene) 把 Scene 提交给 Engine。

也就是说,你虽然没有重新 paint B,但还是会把 B 的已有 Layer Tree 再走一遍 scene composition / render submission,而这类问题以前单 View 根本不存在,但是 Multi-View 出来之后,场景就不一样了。

所以这次 PR 的核心改动其实本身页不复杂,比如在 RenderView 里加了:

然后 RendererBinding.drawFrame() 变成:

RenderObject dirty - View dirty - 只提交 dirty View。

所以现在 Flutter 除了 RenderObject ,还需要知道哪个 View “ 脏了” ,PR 给 PipelineOwner 加了一个 onFlushedPaint:

然后当 PipelineOwner.flushPaint() 完成之后 onFlushedPaint?.call(dirtyNodes.isNotEmpty); ,每一个 View 对应的 PipelineOwner 如果这一帧确实 paint 过 RenderObject,就调用:

这个设计很好理解,就是 paint 是 View 是不是需要重新产生 Scene 的最直接证据,所以如果三个窗口里只有窗口 A 有动画:

A 的 PipelineOwner 有 dirty paint node,所以 A composite,而 B/C 没有 dirty node,就连 compositeFrame() 都用不进。

这对于 Flutter 正在推进的桌面 Multi-Window 挺重要的,因为窗口越多,这个优化越有价值,因为原来的成本更接近“总 View 数”,现在开始接近“本帧 Dirty View 数”。

这个在后续的 GoogleBook ,鸿蒙 PC 之类的多窗口场景都挺有意义的,它可以省掉没变化 View 的 scene composition 和 engine render submission,还有后续可能产生的 raster/compositor 工作。

当然,这个问题到这里本来还挺简单的,但是遇到 Texture 场景就变复杂了,因为在 Texture 这种视频场景,会有画面变了,但 Flutter RenderObject 根本没变的情况。

比如 View A 里放了 Texture(textureId: 123) ,视频解码器产生了新的一帧,从 Flutter Framework 角度看的话:

以前其实也没什么问题,因为 Flutter 每一帧反正都会对所有 RenderView 全部 composite ,所以 Texture 的新内容自然就顺便被提交了。

但是现在如果开始跳过“干净 View”,就出现类似:

Texture 123 明明已经产生新视频帧,但 View A 看起来完全不 dirty,所以 Flutter把整个 View A 跳过了。

然后就会看到,视频就画面就冻结了,所以这个 PR 新增了一条新的通知链路, Engine 到 Framework 反向 dirty 通知链路:

在 Framework 新增 PlatformDispatcher.onTextureFrameAvailable 场景下,Engine 不只是自己知道“Texture 新帧到了”,现在还能告诉 Dart 类似:

然后 TextureBox attach 时会注册监听:

来源:稀土掘金 原文