关于离屏渲染优化的思考

原文地址 好文 很深入 总结了一下

离屏渲染(Offscreen Render)

objc.io 出品的 Getting Pixels onto the Screen 的翻译版绘制像素到屏幕上应该是国内对离屏渲染这个概念推广力度最大的一篇文章了。文章里提到「直接将图层合成到帧的缓冲区中(在屏幕上)比先创建屏幕外缓冲区,然后渲染到纹理中,最后将结果渲染到帧的缓冲区中要廉价很多。因为这其中涉及两次昂贵的环境转换(转换环境到屏幕外缓冲区,然后转换环境到帧缓冲区)。」触发离屏渲染后这种转换发生在每一帧,在界面的滚动过程中如果有大量的离屏渲染发生时会严重影响帧率。

官方公开的的资料里关于离屏渲染的信息最早是在 2011年的 WWDC, 在多个 session 里都提到了尽量避免会触发离屏渲染的效果,包括:mask, shadow, group opacity, edge antialiasing。

最初应该是从英文开发者那里传开的:使用 Core Graphics 里的绘制 API 也会触发离屏渲染,比如重写 drawRect:。
为什么几年前会产生这样的认识不得而知。在 WWDC 2011: Understanding UIKit Rendering 这个 session 里演示了「Core Animation Instruments」里使用「Color Offscreen-Renderd Yellow」选项来检测离屏渲染,在 WWDC 2014: Advanced Graphics and Animations for iOS Apps 也专门演示了这个工具。

Designing for iOS: Graphics & Performance 这篇文章也提到了使用 Core Graphics API 会触发离屏渲染,这引出了 Andy Matuschak,苹果 iOS 4.1-8 时期 UIKit 组成员 ,WWDC 2011: Understanding UIKit Rendering 主讲人之一,对这个观点的回复,主要意思是:「Core Graphics 的绘制 API 的确会触发离屏渲染,但不是那种 GPU 的离屏渲染。使用 Core Graphics 绘制 API 是在 CPU 上执行,触发的是 CPU 版本的离屏渲染。」

本文以「Color Offscreen-Renderd Yellow」为触发离屏渲染的标准,除非还有这个标准无法检测出来的引发离屏渲染的行为。那么 Core Graphics API 是不会触发离屏渲染的,比如重写drawRect:,而除了以上四种效果会触发离屏渲染,使用系统提供的圆角效果也会触发离屏渲染,比如这样:

1
2
view.layer.cornerRadius = 5
view.layer.masksToBounds = true

优化方案

RoundedCorner 在仅指定cornerRadius时不会触发离屏渲染,仅适用于特殊情况:

  • contents为 nil 或者contents不会遮挡背景色圆角;
  • Shawdow 可以通过指定路径来取消离屏渲染;
  • Mask 无法取消离屏渲染;

以上效果在同等数量的规模下,对性能的影响等级:Shadow > RoundedCorner > Mask > GroupOpacity(迷之效果)。

任何时候优先考虑避免触发离屏渲染,无法避免时优化方案有两种:

  • Rasterization:适用于静态内容的视图,也就是内部结构和内容不发生变化的视图,对上面的所有效果而言,在实现成本以及性能上最均衡的。即使是动态变化的视图,开启
  • Rasterization 后能够有效降低 GPU 的负荷,不过在动态视图里是否启用还是看 Instruments 的数据。
    规避离屏渲染,用其他手法来模拟效果,混合图层是个性能最好、耗能最少的通用优化方案,尤其对于 rounded corer 和 mask。