iOS离屏渲染

屏幕显示完整流程

2022080801.jpg

2022080802.jpg

整体渲染流程可以分为三个阶段:

1、CPU阶段: CPU的计算主要是通过CoreAnimation来处理,通过OpenGL ES/Metal将数据传递给GPU。
2、GPU阶段: GPU渲染主要是将接收到的渲染数据进行一系列渲染之后将帧数据存储在帧缓存(Frame Buffer)里面,供视频控制器调用。
3、屏幕显示: 视频控制器从帧缓存中获取到帧数据显示在屏幕上。

屏幕显示图像原理

CRT显示器原理

2022080803.jpg

2022080804.jpg

CRT显示器原理主要是通过【电子束】激发屏幕内表面的荧光粉来显示图像,由于荧光粉点亮后很快就会熄灭,所以【电子枪】需要不断的【从上到下】进行扫描,扫描完成后显示器就呈现一帧画面,电子枪回到【初始位置】开始下一次的扫描。

水平同步信号:当电子枪换行扫描时会发出一个水平同步信号。
垂直同步信号:当一帧完成绘制后,电子枪恢复到原来的位置准备扫描下一帧时显示器会发出一个垂直同步信号。

GPU渲染完成后将渲染结果存入帧缓存区,视频控制器根据【垂直同步信号】逐帧读取帧缓冲区的数据,经过数据转换之后由显示器进行显示。

帧缓存(Frame Buffer)

1、屏幕刷新频率
Refresh Rate,单位hz,指的是设备刷新屏幕的频率,这个频率一般是60hz,所以每隔16.67ms屏幕会刷新一次。
2、帧率
Frame Rate,单位fps,指的是GPU生成帧的速率。
3、帧缓存
也叫显存,它是屏幕所显示画面的一个直接映像,也叫做位映射图(bitmap)或光栅,帧缓存的每一存储单元对应屏幕上一个像素,整个帧缓存对应一帧图像。

理想情况下,屏幕刷新频率和帧率完全一致,也就是说当屏幕显示完一帧的时候刚好下一帧画面也生成直接显示在屏幕上,但实际上这两个频率并不完全一致,为了解决这个问题,引入的【帧缓存】的概念。

图像撕裂

图像撕裂现象

2022080805.jpg

原因分析
当【帧率】大于【屏幕刷新频率】时,当视频控制器刚读完一帧的上半部分时,GPU已经把下一帧准备好并提交到帧缓存,这样视频控制器就会读到下一帧的下半部分在屏幕显示。
解决方案
苹果使用的是【双缓存】和【垂直同步信号】。

垂直同步信号保证GPU的渲染只有等到显示器发出【垂直同步信号】之后才会进行下一帧的渲染。
双缓存保证显示器会交叉读取两个缓存区的内容,相当于是拿空间换时间的一种策略。
好处在于

  1. 不浪费CPU、GPU资源,保证提前渲染好的位图有一个缓存区来保存,这样GPU可以就可以进行下一帧的处理。
  2. 减少掉帧的出现。


    2022080806.jpg

卡顿

当显示器的【垂直同步信号】发出的时候,GPU没有完成相应的渲染就会出现【卡顿】的现象,这也是为了解决画面撕裂的问题带来的副作用,如下图所示:


2022080807.jpg

补充

  1. 掉帧指的是重复渲染同一帧数据而不是指某一帧丢掉了不渲染。
  2. 为了减少【掉帧】的情况,有的会引入【三缓存】+【垂直同步信号】,比如安卓设备。

离屏渲染

什么是离屏渲染?

当GPU无法直接把渲染结果存放到帧缓存中,而是先是暂时把中间的一个临时状态存放在另外的区域。之后再存放到帧缓存,这个过程叫离屏渲染。

即是说:GPU需要再当前屏幕缓存区以外开辟一个新的缓冲区进行操作。
原因
GPU渲染采用的是【画家算法】,只能一层一层的输出,所以当一层不能直接生成图片的话就需要额外开辟新的缓冲区来存放这些临时图层直到最后生成了一张完整的图片之后再写入帧缓存里面。

如下:


2022080808.jpg
2022080809.jpg

当前屏幕渲染和离屏渲染

2022080810.jpg

离屏渲染的性能损耗

离屏渲染在当前屏幕缓冲区外新开辟一个缓冲区进行渲染操作,造成其性能损耗的主要原因在于:创建离屏渲染和上下文切换。
切换上下文主要是当发生离屏渲染时,渲染上下文需从当前屏幕缓冲区切换到屏幕外缓冲区然后再完成渲染。
如果一屏元素都发生离屏渲染,这个从当前屏幕缓冲区切换到屏幕外的缓冲区就会发生多次,自然就会有一定的性能损耗。

常见的离屏渲染场景

设置圆角

2022080811.jpg

结论
满足以下条件的就会发生离屏渲染:

  1. clipsToBounds开启,圆角 > 0,contents上有内容
  2. 同时修改了contents+backgroundColor 或 contents+border(iOS9之后)

优化

  1. 直接让UI提供带圆角的图片
  2. 利用UIBezierPath和CAShapeLayer
  3. 利用UIBezierPath和CoreGraphics

设置遮罩

2022080812.jpg

设置遮罩的流程如下

  1. 渲染layer的mask纹理
  2. 渲染layer的content纹理
  3. 合并操作:合并mask 和 content纹理

结论
满足以下条件的都会触发离屏渲染:
设置了mask + 任意contents(比如设置UILabel文字、背景颜色、图片等)

设置阴影(shadow)

2022080813.jpg

结论
阴影的本质和layer类似,都是在layer下一层多添加一层,根据前面提到的【画家算法】无法一次性生成,所以会发生离屏渲染。

优化
利用UIBezierPath给视图添加一个阴影路径,相当于提前告诉GPU这个阴影的几何形状,这样阴影就可以独立渲染。

光栅化(shouldRasterize)

2022080814.jpg

光栅化
光栅化是一种缓存机制,开启后会缓存这个图片的bitmap,如果对应的layer和sublayers没有发生变化,就可以直接使用缓存而不用GPU再进行渲染,从而提高性能。
注意
光栅化只能缓存100ms,而且只能存储屏幕大小2.5倍的数据,缓存空间十分有限。

组不透明(allowsGroupOpacity)

2022080815.jpg

allowsGroupOpacity
alpha并不是分别应用到每一层上,而是整个layer图层树完成之后,再统一加上alpha,然后和底下其他像素进行融合。
注意:
iOS7之后allowsGroupOpacity默认为YES,这样做的原因是为了保持子视图和父视图保持同样的透明度。

allowsGroupOpacity触发离屏渲染的条件

  1. 当视图上有其他子视图
  2. 视图View的alpha值在0 ~ 1之间
  3. 视图view.layer.allowsGroupOpacity = YES

总结

离屏渲染的处理仅仅是我们日常所关注的性能中的其中一个点,在处理的时候也要根据具体场景具体分析,要注意并不是所有的离屏渲染都是必须要去避免的,开辟额外的帧缓存虽然有一定的性能损耗,但是保存渲染结果并进行最终的视图显示也是为了保持视图的流畅性。

更多技术文章欢迎移步Sunny的个人技术博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • 目录: 1.图像显示原理 2.图像显示原理2.1 图像到屏幕的流程2.2 显示器显示的流程 3.卡顿、掉帧3.1...
    Harry__Li阅读 1,788评论 1 17
  • 界面渲染 UIView继承自UIResponder,可以处理系统传递过来的事件,如:UIApplication、U...
    奶茶大叔阅读 886评论 0 15
  • 很多人都知道设置了layer的圆角属性cornerRadius并裁减clipsToBounds/layer.mas...
    K哥的贼船阅读 1,138评论 4 4
  • 目录 离屏渲染的本质如何设置圆角(三种方法)Shadow 阴影MaskGroupOpacityEdgeAntial...
    路飞_Luck阅读 1,858评论 0 9
  • 屏幕渲染的原理: 需要显示的图像经过CRT电子枪以极快的速度一行一行的扫描,扫描出来就呈现了一帧画面,随后电子枪又...
    一条鱼的星辰大海阅读 522评论 1 4