iOS性能优化——离屏渲染的分析探究

共 3882字,需浏览 8分钟

 ·

2022-04-24 02:07

前言


性能优化是我们开发中经常提到的话题,而离屏渲染是我们在性能优化过程中要关注的一部分。可能我们在处理过程中都了解了优化方法,但是离屏渲染背后的实现的原理是怎么样的呢?今天这篇文章就带大家了解一下离屏渲染,在说离屏渲染之前请大家先思考以下几个问题:

1、 什么是离屏渲染?

2、 离屏渲染为什么会有性能损耗?

3、 常见的离屏渲染场景有哪些?

4、 怎么避免离屏渲染?

在此之前我们需要先了解几个概念。


01
CPU&GPU



我们都知道可视化程序是由CPU和GPU协作完成的,首先我们先了解一下这两个概念:

CPU(Central Processing unit):中央处理器,作为计算机系统的控制和运算核心,是信息处理、程序运行的最终执行单元。

GPU(Graphics Processing unit):图形处理器,一种可进行绘图运算工作的专用微处理器,具有非常强的并行计算能力。

CPU擅长分支预测等复杂操作,是一个复杂劳动过程;GPU擅长对大量数据进行简单操作,是一个大量并行工作的过程,GPU可以看做是一种专用的CPU,为单指令在大量数据上工作而设计,这些数据内容都是相同的操作。


02
屏幕显示图像原理



在介绍屏幕图像显示的原理之前,我们需要先从 CRT 显示器原理说起,如下图所示,CRT 的电子枪从上到下逐行扫描,扫描完成后显示器就呈现一帧画面。然后电子枪回到初始位置进行下一次扫描。为了同步显示器的显示过程和系统的视频控制器,显示器会用硬件时钟产生一系列的定时信号。当电子枪换行进行扫描时,显示器会发出一个水平同步信号(horizonal synchronization),简称 HSync;而当一帧画面绘制完成后,电子枪回复到原位,准备画下一帧前,显示器会发出一个垂直同步信号(vertical synchronization),简称 VSync。显示器通常以固定频率进行刷新,这个刷新率就是 VSync 信号产生的频率。

一般来说,CPU计算好显示内容提交至GPU,GPU渲染完成后将渲染结果存入帧缓冲区,视频控制器会按照VSync信号逐帧读取帧缓冲区的数据,经过数据转换后最终由显示器进行显示. 下图所示为常见的CPU、GPU显示器工作方式:


    

03
渲染显示流程



APP本身并不负责渲染,渲染则是由一个独立的进程(Render Server)负责,APP通过进程间通信将渲染任务及相关数据提交给Render Server。Render Server处理完数据后,再传递给GPU,最终GPU调用iOS的图像设备进行显示。渲染显示流程可以概括为:

  • CoreAnimation提交会话,包括自己和子树的layout状态等

  • Render Server解析提交的子树状态,生成绘制指令

  • GPU执行绘制指令

  • 显示渲染后的数据

CoreAnimation提交可细分为4个阶段:布局(Layout)、显示(Display)、准备提交(Prepare)、提交(Commit)

其具体的流程图如下:


    

什么是离屏渲染


接下来我们来了解一下什么是离屏渲染。

屏幕渲染有两种方式:当前屏幕渲染和离屏渲染。

当前屏幕渲染:在当前用于显示的屏幕缓冲区(frame buffer)中进行渲染。

离屏渲染:GPU或CPU在当前屏幕缓冲区外新开辟一个缓冲区进行渲染。

其简单示例图如下:


    

离屏渲染的性能损耗


离屏渲染在当前屏幕缓冲区外另开辟了一个缓冲区进行渲染操作,造成其性能损耗的主要原因是因为创建离屏渲染区和上下文的切换,其中上下文切换主要是当发生离屏渲染时视频控制器要去读取缓存中的内容,渲染上下文需要从当前屏幕缓存区切换到屏幕外缓冲区,当非离屏渲染时候渲染上下文要切换到当前屏幕缓冲区。当一屏控件元素都发生离屏渲染时,这种从当前屏幕缓冲区切换至屏幕外缓存区会进行多次,自然就会发生性能的损耗。


离屏渲染何时发生及触发场景


从上文中我们可以了解离屏渲染产生的原因是因为离屏渲染在当前屏幕外会创建一个离屏渲染区(offscreen frame buffer),在这个渲染区内部是用来存放全局操作过程的渲染结果。layer的阴影、蒙版、模糊、光栅化、圆角等操作,因为这些操作会进行多个渲染结果的合并,需要一个额外的渲染区来暂存渲染结果。因为触发离屏渲染本质上是因为涉及到多个渲染结果合并时,需要借助离屏渲染区来暂存渲染结果。


01
圆角的设置



单纯的只设置cornerRadius和masksToBounds并不会触发离屏渲染,我们需要看它要处理几层渲染数据。下面我们通过代码来查看,通过模拟器打开离屏渲染颜色标记调试:


    

从上图中可以看到当我们仅仅设置iamgeView的layer的cornerRadius和masksToBounds时候并不会触发离屏渲染,如果我们这时候新增设置了imageView的背景色就会触发离屏渲染,这是因为我们设置背景色及图片进行渲染时候,会先处理背景色将背景色处理完成放到offScreen buffer中,接着进行图片的处理操作,当图片处理完成后会放到offScreen buffer中,继续进行圆角的设置,等所有的操作处理完成后最终结果存储进行最终的展示。我们设置layer的边框或者颜色同样也会触发离屏渲染,因为我们设置图片的背景、layer层的边框及颜色改变的是视图层的内容content,当我们再设置了masksToBounds为Yes就会导致离屏渲染。

同样UIView设置添加子视图进行圆角设置时候也会触发离屏渲染,其代码图示如下:


    

圆角设置触发离屏渲染不是设置了cornerRadius和masksToBounds就一定会触发离屏渲染,单图层的时候并不会触发离屏渲染,只有处理多图层时并且这时候masksToBunds设置为YES才会触发。


02
蒙版



官方给的蒙版渲染流程如下:

  • 渲染layer的mask纹理

  • 渲染layer的content纹理

  • 合并操作,合并mask和content纹理

由于有两层渲染结果,所以处理合并之前需要额外的缓存区来储存处理结果,这时候也就会触发离屏渲染了。我们可以用模拟器实验一下:


    

同样iOS8提供的UIVisualEffectView也会触发离屏渲染,其导致离屏渲染的原理和蒙版触发离屏渲染的原理类似,其渲染流程如下:

  • 渲染layer的内容content

  • 捕获内容 (capture coneter)

  • 对内容横向模糊

  • 对内容纵向模糊

  • 合成处理操作(Upscale and tint)

通过其设置模糊效果需要更多的处理步骤来存储渲染结果,会触发离屏渲染,性能也随之有影响的。

组透明度allowsGroupOpacity、阴影(Shadow)仅仅只设置shadowoffset同样也会触发离屏渲染。原理同上都是因为仅仅靠frameBuffer是不能实现最终效果的,只能另开辟离屏渲染区进行渲染处理合成最终的结果进行显示。


    

03
光栅化



光栅化开启后,会触发离屏渲染。当CALayer的shouldRasterize设置为YES时,可以将图层绘制的结果缓存起来方便下次使用,但是光栅化开启还是要根据我们使用场景来判断,如果我们的图像内容不经常进行改变,我们可以开启,因为图像内容不变并且有复杂内容的重绘的时候,开启光栅化后,将渲染结果缓存后,下次使用时候可以直接读取缓存,当然这个缓存我们也不能过度使用的,这个缓存大小是限制在2.5倍的屏幕大小的,并且这个缓存结果超过100ms未使用的话,就会被丢弃的。光栅化的开启导致的离屏渲染,大家有兴趣的可以自己动手写一下,这里就不做详细介绍了。


如何避免离屏渲染


1、 圆角的处理

我们开发中其实主要有对图片圆角处理、滚动视图各种Cell及其上面控件元素的圆角设置(部分特殊场景各个Section内部上下圆角的单独设置)这时候我们可以通过Core GraPhics框架处理或者配合贝塞尔曲线设置。



我在这里只简单介绍了两种处理方式,处理圆角的方式不仅仅是上面的方法,只要我们在设置圆角的时候不过多触发离屏渲染就可以,尤其是处理滚动视图及其上元素控件需要圆角设置的时候。针对imageVIew视图设置的时候,如果我们没有其他特殊的设置,不设置backgroundColor,那么我们是可以直接设置cornerRadius的。

2、 蒙版、视图模糊、光栅化、阴影我们其实完全可以具体场景具体分析,layer层的蒙版使用layer.mask会触发离屏渲染,一般蒙版使用场景是蒙版引导图,图层蒙版的效果替代我们完全可以使用添加自定义view来达到我们做到的效果,有一些特殊的使用场景,我们也可以配合CAShapeLayer进行处理。而针对视图模糊处理,我们可以避免使用系统提供的UIVisualEffectView,使用CoreImage提供的方法实现模糊效果或者Accelerate.Framework内部的方法来实现。阴影的处理我们可以使用shadowPath来处理阴影效果,而光栅化如果内容不变涉及到多图层的复杂绘制效果我们可以开启,减少性能的损耗。



总结


离屏渲染的处理仅仅是我们日常所关注的性能优化的其中一个点,我们在业务场景处理中也要根据具体场景去分析,并不是所有的离屏渲染都是必须避免的,开辟额外的帧缓冲区虽然有一定的性能损耗,但是保存渲染结果并进行最终的视图显示也是为了保证我们视图的流畅性的。最后希望本文对离屏渲染的探究对大家后续开发及离屏渲染方面的性能优化有所帮助。

浏览 98
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报