跨平台开发里的 PlatformView 实现一直是一个经久不衰的话题,在之前的 《深入 Flutter 和 Compose 的 PlatformView 实现对比》 我们就详细聊过 Flutter 和 Compose 在 PlatformView 实现上的异同之处,也聊到了 Compose 为什么在相同实现上对比 Flutter 会更有优势的原因。
那么随着 3.29 的发布,恰好关注到其实 Flutter 在 androids 的 PlatformView 上其实正在落地另外一种实现,而这种实现目前看来可以做到在 HC 的基础上得到更好的性能,所以也被暂时称为 HCPP。
在聊 HCPP 之前我们再简单回顾下 Flutter 在 androids 上的 PlatformView 实现模式:
- VD:最老的实现模式,利用副屏 的相关支持在内存实现原生控件的模拟绘制和纹理提取
- HC:通过直接将原生控件 add 到 上,然后通过新的 提供新的 Surface 来实现控件 UI 堆叠合成
- TLHC:还是直接将原生控件 add 到 上,但是中间利用通过 parent 替代掉 child 的 canvas,让原生控件绘制的对应的 上
有点抽象?,没关系,后面有简单的直观例子。
在这个过程中几种模式各有优劣,比如:
- TLHC 模式不支持 等控件,因为 有自己独立的 Surface 和 Canva ,它的 Surface 直接来自 ,也就是当前 Window 下
- TLHC 模式与异步更新 View 一起使用时(如 TextureView 或基于 GL 的渲染器),需要在更新内容时在对应 PlatformView 显式调用 才能保证正确渲染,例如 的 调用
- HC 模式因为不同 API 版本和线程问题,会有同步和额外的性能开销
- ····
所以目前这三种模式是协同工作,例如:
那么,回到本次的主题 ,针对全新的 HCPP 实现,Flutter 提供了全新的 API ,可以看到,它需要 Vulkan 和 API 34 的环境才支持使用,如果从这点看,它的通用性又相对较低:
那为什么它需要 API 34 呢?这和它直接使用 的 API 逻辑有很大关系,另外,从 Engine 的判断逻辑上可以看到,目前除了判断 Vulkan 和 API 之后,还需要配置对应的 才可以测试 HCPP,也就是在 增加:
接着就让我们来看看 HCPP 和其他几种模式有什么区别,其实主要就是和 HC 和 TLHC 进行比较,这里首先做一个容器 Demo ,主要是通过混合 Flutter 和原生控件的效果来区分它们的实现,让 渲染在两个 Flutter Widget 之间:
之后我们可以通过 强制 PlatformView 使用 HC 模式,可以看到,在 HC 模式下出现很多经典的原生层,特别是多了 的转换还有它的子类 :
我们通过 3D 图可以看到,红色的原生 正常被渲染,然后在其之上的 Flutter 控件(一部分黄色条),是通过 的子类 提供的 Surface 独立渲染:
然后我们再通过 来使用 TLHC 模式,可以看到此时是通过 这个 parent 作为容器来承载,而 会替换掉原生 的 Canvas,让原生控件的内容渲染到指定 Surface 上 :
我们通过原生 3D 图可以看到,此时的 其实在原生层并没有绘制任何东西,因为其 Canvas 是被替换到内存的 上:
说到 ,这个插个题外话,对于 THLC 和 VD 而言,现在创建纹理时是会根据 androids API 来使用不同实现,其中 比较特殊:
因为在此之前,androids 上的 Flutter 引擎支持两个外部渲染源:SurfaceTexture (OpenGLES 纹理)和 ImageReader(GPU-ready buffer),其中 需要 API 28 支持。
而为了适配 Impeller 团队提出了 概念,让 androids 在运行时选择“最佳”渲染 Surface,除了 PlatformView 场景,在外界纹理场景也需要适配的情况:
那么我们看 HCPP,通过 我们启用了 HCPP 模式,可以看到,此时 UI 的层级结构类似 TLHC, 但是 parent 使用的是 HC 模式中的 :
然后我们看 3D 效果,原生控件 其实可以被完整被渲染,证明其 Canvas 并没有被替代,那么这里就有一个神奇的问题了:Flutter 的黄色控件,是如何渲染到红色的 之上的?
这就不得不提 ,作为一个 HCPP 的临时对象,它的实现里有一个关键的对象 ,并且在事务提交时通过 设置了 z 轴为 :
我们可以看提交更改里,基本上全新的 核心逻辑都在于操作 :
在 androids 里, 是一种用于管理和操作与显示系统相关的图形资源的类,简单说就是与 相关的操作, 可以用于创建和管理 ,它是和 交互的一个主要接口,交互的方式则是通过 。
而在 HCPP 里,我们可以看到,此时的 正是通过一个全新的 创建得到,而这个 的 来自 :
也就是,在 HCPP 模式里,Flutter 通过 构造了一个全新的 用于 合成,并且还通过 将 Surface 的 z 轴设置到了 1000 ,而这个 1000 就是黄色 Flutter 控件可以渲染到原生红色方块之上的原因。
举个例子,我们将 这部分代码复制到一个简单的纯原生项目里,并且同样对创建的 设置 1000 和绘制红色:
然后我们看最终绘制的效果,可以看到绿色背景的 是在 下方的,但是通过 创建的 因为 z 轴为 1000 的原因,最终红色方块会绘制到 之上:
另外,在目前逻辑中,Engine 如果判断当前帧如果不存在 PlatformView ,并且上一帧存在 PlatformView,那么就会调用 从而直接触发 Java 层面的 ,进而隐藏不需要的 Layer :
所以可以看到,HCPP 主要就是通过 来构造一个高层级的 从而实现最终绘制时混合覆盖的问题,这和我们之前聊 《深入 Flutter 和 Compose 的 PlatformView 实现对比》 里 Compose 可以在 PlatformView 里直接使用 的道理类似,都是 合成时的层级操作。
至于为什么需要 API 34, 主要也是 SurfaceControl 对应的一些 API 需要的版本都很高,另外我依稀记得, androids 14 在通过 SurfaceControl 实现低延迟绘图时,可以更好支持 Canvas API 通过硬件加速绘制到 HardwareBuffer :
如果对于 Engine 部份逻辑感兴趣的,也可以看 这部分逻辑里如何通过 GetLayer 去创建 。
目前 HCPP 还处于 main 分之的 beta 状态,如果后续正式落地,那对于 androids PlatformView 实现将会是存在 4 种组合模式,相比较 ioses 端多个 CALayer 的合成模式,androids 的 PlatformView 可以说是一路坎坷至今。
最后,你觉得 HCPP 会成为落地为全新的 PlatformView 支持吗?
PS : 标识还用于控制 Impeller 内部使用 Vulkan swapchain 或者 androids SurfaceControl (AHB swapchain),在 androids SurfaceControl 模式下,Java 端创建的 Transaction 会链接到 AHB swapchain。
当然, AHBSwapchainVK 交换链实现并非在所有 androids 版本上都可用,一般不支持的话,会回退到 KHR swapchain。
http://github.com/flutter/flutter/issues/163073
http://github.com/flutter/flutter/pull/161829
http://github.com/flutter/flutter/issues/144184