ios 图像显示原理及掉帧卡顿、离屏渲染的原因分析与优化

1.图像显示原理简介

有关概念:

时钟信号:垂直同步信号V-Sync / 水平同步信号H-Sync

iOS设备双缓冲机制:前/后帧缓冲区

图像显示原理01

1.CPU和GPU通过总线连接起来,工作流程如上图示:

在CPU中的工作完成会生成一个位图(bitmap),位图再经由总线在合适的时机上传给GPU处理,GPU拿到位图之后会进行相应位图的图层渲染(顶点变换、纹理混合等操作),之后会把结果放到帧缓冲区(Frame Buffer)中,由视屏控制器根据Vsync信号,在指定时间前提取对应帧缓冲区中的屏幕显示内容,最终显示到手机屏幕上。


图像显示原理02

2.UI视图显示到屏幕上的大概流程如上图示:创建一个UIView,它的显示部分是由CALayer负责的,CALayer有一个contents属性(即最终要绘制到屏幕上的位图),比如说创建的是一个显示“Hello world”的UILable,那么contents中放置的结果就是一个关于"Hello world"的文字位图。另外系统会在合适的时机回调一个drawRect:方法,在此基础上我们就可以绘制一些想要自定义的内容,绘制好的位图,会经由CoreAnimation框架,提交给GPU部分的OpenGL渲染管线进行位图的渲染,最终显示到屏幕上。

3.CPU工作:负责UI布局、文本计算,绘制,图片解码,绘制纹理交给GPU

CPU工作示意图

4.GPU工作:纹理混合,顶点变换,渲染到帧缓冲区

GPU渲染

2.UI掉帧、卡顿原因

1.通常来说页面滑动的流畅性是60fps(指每一秒会有60帧画面更新),人眼看到的是流畅的效果。也因此每隔 \frac{1}{60} s(即16.7ms)就要产生一帧画面,也就是说在这16.7ms时间内GUP和CPU要协同完成产生一帧的数据,比如CPU花了一定的时间做UI布局、文本计算、视图的绘制和图片解码,并把产生的位图提交给GPU,GPU又要花一定的时间进行纹理混合渲染,然后在下一帧的VSync垂直信号到来之前显示这一帧画面。


UI卡顿、掉帧原因图示

在上图中,如果CPU和GPU协同工作的时间在16.7ms内,那么图像的显示是流畅的;如果CPU花费了大量时间来做frame的布局、视图绘制和图片解码,那么留给GPU的时间就不多了,GPU要想把图层合成、纹理渲染全部完成就要超过16.7ms,那么在下一帧的垂直信号VSync到来之前,还没有准备好当前的一帧画面,这就产生了掉帧,体现在滑动上的就是上下滑动卡顿的效果。

总而言之,在规定的16.7ms内,CPU和GPU并没有在下一帧的Vsync信号到来之前把当前的一帧画面生产完成,由此产生了掉帧卡顿。

2.滑动优化方案

CPU资源消耗分析

•对象创建:对象的创建会分配内存、调整属性、甚至还有读取文件等操作,比较消耗CPU资源。尽量采取轻量级对象,尽量放到后台线程处理,尽量推迟对象的创建时间。(如UIView  /  CALayer)

•对象调整:frame、bounds、transform及视图层次等属性调整很耗费CPU资源。尽量减少不必要属性的修改,尽量避免调整视图层次、添加和移除视图。

•布局计算:随着视图数量的增长,Autolayout带来的CPU消耗会呈指数级增长,所以尽量提前算好布局,在需要时一次性调整好对应属性。

•文本渲染:屏幕上能看到的所有文本内容控件,包括UIWebView,在底层都是通过CoreText排版、绘制为位图显示的。常见的文本控件,其排版与绘制都是在主线程进行的,显示大量文时是,CPU压力很大。对此解决方案唯一就是自定义文本控件,用CoreText对文本异步绘制。(很麻烦,开发成本高)

•图片解码:当用UIImage或CGImageSource创建图片时,图片数据并不会立刻解码。图片设置到UIImageView或CALayer.contents中去,并且CALayer被提交到GPU前,CGImage中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。SD_WebImage处理方式:在后台线程先把图片绘制到CGBitmapContext中,然后从Bitmap直接创建图片。

•图像绘制:图像的绘制通常是指用那些以CG开头的方法把图像绘制到画布中,然后从画布创建图片并显示的一个过程。CoreGraphics方法是线程安全的,可以异步绘制,主线程回调。

GPU资源消耗分析

•纹理渲染:尽量减少短时间内大量图片的显示,尽可能将多张图片合成一张进行显示。

•视图混合:尽量减少视图层次和数量,并在不透明的视图里标明opaque属性以避免无用的Alpha通道合成。

•图形生成:尽量避免离屏渲染,尽量采用异步绘制,尽量避免使用圆角、阴影、遮罩等属性。必要时用静态图片实现展示效果,也可尝试光栅化缓存复用属性。

基于上述的分析保持界面流畅,优化的时候就要从CPU和GPU两方面考虑。

优化参考iOS如何优化项目

参考文章Texture的异步渲染和布局引擎

3.离屏渲染

1.有关概念:

•On-Screen-Rendering: 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行

•Off-Screen-Rendering: 离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作

CPU渲染及非GPU缓冲区的渲染统称为离屏渲染

2.离屏渲染消耗性能的原因

•需要创建新的缓冲区

•离屏渲染的整个过程,需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上,又需要将上下文环境从离屏切换到当前屏幕

3.何时会触发?

•圆角(当和masksToBounds或者clipsToBounds为YES一起使用时)(对于UIimageview的圆角iOS9之后做了优化,在不加背景颜色的情况下,同时设置两者不会触发离屏渲染。但是UIButton等控件依然会触发,考虑通过 CoreGraphics 绘制裁剪image矩形圆角,或者叫美工提供圆角图片)

•图层蒙版 (layer.mask)  无法取消离屏渲染

•抗锯齿(edgeAntialiasing)

•阴影 (layer.shadowXXX,如果设置了 layer.shadowPath 就不会产生离屏渲染)可以通过指定路径来取消离屏渲染

•不透明(GroupOpacity)

•光栅化 (layer.shouldRasterize置为YES)

4.检测离屏渲染

模拟器:Debug->Color Offscreen-Rendered离屏渲染的图层高亮成黄,可能存在性能问题真机:Xcode->Debug->View Debugging->Rendering->选中 Color Offscreen-Rendered Yellow  或者   Instrument->Core Animation->选中 Color Offscreen-Rendered Yellow

5.光栅化

光栅化简介:隐式创建一个位图,各种阴影遮罩等效果也会保存到位图中缓存起来,从而减少渲染的频度,把GPU的操作转到CPU上,生成位图缓存,直接读取调用。(注:对于经常变动的内容,不要开启光栅化,防止性能浪费,如Cell的复用)

光栅化的检测:Color Hits Green and Misses Red 开启后,若shouldRasterize设置为YES,对应的渲染结果会缓存,如果图层是绿色,表示缓存被复用;如果是红色,就表示缓存被重复创建,可能存在性能问题。

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

•Rasterization:适用于静态内容的视图,也就是内部结构和内容不发生变化的视图,对上面的所有效果而言,在实现成本以及性能上最均衡的。即使是动态变化的视图,开启 Rasterization 后能够有效降低 GPU 的负荷,不过在动态视图里是否启用还是看 Instruments 的数据。

•规避离屏渲染,用其他手法来模拟效果,混合图层是个性能最好、耗能最少的通用优化方案,尤其对于 rounded corer 和 mask

关于离屏渲染触发及优化可以看下面的文章:

离屏渲染优化详解:实例示范+性能测试

4.图像撕裂

图像撕裂原因:当视频控制器还未读取完成时,GPU将新的一帧内容提交到帧缓冲区并把两个帧缓冲区进行更新后,视频控制器就会把新的一帧数据的下半段显示到屏幕上,造成画面撕裂的现象。

•解决方案:垂直同步机制

•弊端:GPU会等待显示的V-Sync信号发出后,才进行新的一帧渲染和缓存区更新。能解决画面撕裂现象,也增加了画面流畅度,但需要消耗更多的计算资源,由此可能导致卡顿。

参考文章ios图像显示原理

Texture相关参考资料

官方文档:http://texturegroup.org/docs/getting-started.html

官方文档部分译文一:https://juejin.im/post/5a16acf56fb9a04509092ce5

官方文档部分译文二(布局系统):https://juejin.im/post/5a1be41351882561a20a32e9#heading-17

即刻技术团队关于ASDK:

一、https://zhuanlan.zhihu.com/p/25371361

二、https://zhuanlan.zhihu.com/p/26283742

三、https://zhuanlan.zhihu.com/p/29537687

iOS 保持界面流畅的技巧:https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/

ASDK源码剖析:http://beelearning.cn/2017/11/ASDK/

从 Auto Layout 的布局算法谈性能 :https://draveness.me/layout-performance

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

推荐阅读更多精彩内容