onScreen和offScreen渲染(一)

版本记录

版本号 时间
V1.0 2017.06.15

前言

我们要在界面上看到东西,是因为控件被渲染到屏幕上的原因,说到渲染大家都知道包括当屏渲染和离屏渲染,这里就和大家说一下onScreen和offScreen。

onScreen渲染

顾名思义,就是将视图直接渲染到屏幕上。onscreen render指的是GPU在当前用于显示的屏幕缓冲区进行渲染。


offScreen渲染

一、基本概念

offscreen rendring指的是在图像在绘制到当前屏幕前,需要先进行一次渲染,之后才绘制到当前屏幕。 那么为什么offscreen渲染会耗费大量资源呢? 原因是显卡需要另外alloc一块内存来进行渲染,渲染完毕后在绘制到当前屏幕,而且对于显卡来说,onscreen到offscreen的上下文环境切换是非常昂贵的(涉及到OpenGL的pipelines和barrier等)。
  与当前屏幕渲染相比,离屏渲染的代价是很高的,主要体现在两个方面:

  • 创建新缓冲区:要想进行离屏渲染,首先要创建一个新的缓冲区。
  • 上下文切换:离屏渲染的整个过程,需要多次切换上下文环境。先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上又需要将上下文环境从离屏切换到当前屏幕。但是,上下文环境的切换是要付出很大代价的。

二、离屏渲染的几种情况

  • Any layer with a mask (layer.mask)
  • Any layer with layer.masksToBounds / view.clipsToBounds being true
  • Any layer with layer.allowsGroupOpacity set to YES and layer.opacity is less than 1.0
  • Any layer with a drop shadow (layer.shadow*).
  • Any layer with layer.shouldRasterize being true
  • Any layer with layer.cornerRadius, layer.edgeAntialiasingMask, layer.allowsEdgeAntialiasing
    Text (any kind, including UILabel, CATextLayer, Core Text, etc).
  • Most of the drawing you do with CGContext in drawRect:. Even an empty implementation will be rendered offscreen.

下面我就给大家翻一下,英语不好,翻译不对的地方请指出。

  • 带有遮罩的layer
  • layer.masksToBounds和view.clipsToBounds设置为YES
  • layer.allowsGroupOpacity设置为YES或者layer.opacity<1.0
  • layer带有阴影shadow
  • layer.shouldRasterize设置为YES
    • layer设置shouldRasterize=YES之后,会把被光栅化的图层保存成位图并缓存起来,其中圆角或者阴影之类的效果也是直接保存到位图当中,当需要渲染到屏幕上的时候只需要到缓存中去取对应的位图进行显示就行了,加快了整个渲染过程。可以通过勾选instruments core animation中的Color Hits Green and Misses Red选项来查看图层是否被缓存了,如果图层显示为绿色则表示已经被缓存起来了,也就是这个缓冲区的内容被复用了,不用在去重新创建缓冲区,反之则是用红色标示。设置shouldRasterize之后,如果滑动过程中发现都是红色的证明就有问题了。也可以这么表述:如果在设置为YES后,在快速滚动的时候,绿色较少,而红色较多,说明并不可观,表示不建议使用光栅化。相反,如果在快速滚动的时候,基本都是绿色的,只有极少数偶尔出现红色,那么使用光栅化来优化是可以达到很好的效果的。
  • layer设置了 layer.cornerRadius, layer.edgeAntialiasingMask, layer.allowsEdgeAntialiasing Text(任何种类,包括UILabel、CATextLayer、CoreText等)
  • 在drawRect中利用上下文CGContext绘图,即使什么都没画,只是空的实现也会导致离屏渲染。

大家都知道离屏渲染多了,系统性能就会降低,变的很卡。所以要尽量避免以下几点:

  • 尽量将视图背景色和superView的背景色一致/背景色不要为clearColor(会引起图层混合)。

  • 假如最上层的view是不透明的,那直接使用这个view的对应颜色之就可以,但如果view是透明的,在计算像素的颜色值时就需要计算它下面图层,透明的视图越多,计算量就越大,因此也会对图形的性能产生一定的影响,所以可以的话也尽量减少透明图层的数目。

  • 光栅化对于那些有很多子view嵌套在一起、view的层级复杂或者有很复杂特效效果的图层有很明显的提升,因为这些内容都被缓存到位图当中了。但是使用光栅化需要注意一些内容:

    • 适用于内容基本不变的图层
      假如图层的内容经常变化,比如cell里面有涉及到动画之类的,那么缓存的内容就无效了,GPU需要重新创建缓存区,导致离屏渲染,这又涉及到OpenGL的上下文环境切换,反而降低性能。
    • 不要过度使用
      缓存区的大小被设置为屏幕大小的2.5倍,假如过分使用同样会导致大量的离屏渲染。
    • 如果缓存的内容超过100ms没有被使用则会被回收。
  • 避免离屏渲染。将图层的alpha设为1(如何其不为1,图层合成过程中,将引起大量的计算),uiview的圆角,遮罩 也会引起离屏渲染。

  • 减少不必要的drawrect()操作。

  • 处理好UIView 与 CALayer的关系。UIView层次不要太多。因为这样会加多合成。

  • 将线程安全的操作挪到非主线程。比如绘制图片等。

三、界面卡顿的原因

可能大家有所疑惑,为什么有的时候说性能低界面卡,到底是什么原因,其实可以从软硬件结合的角度讲。

原因:GPU垂直同步信号(VSync,硬件产生的),固定1/60S发出一次。在此时间间隔内,CPU、GPU需要准备好数据。在 VSync 信号到来后,系统图形服务会通过 CADisplayLink 等机制通知 App,App 主线程开始在 CPU 中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后 CPU 会将计算好的内容提交到 GPU 去,由 GPU 进行变换、合成、渲染。随后 GPU 会把渲染结果提交到帧缓冲区去,等待下一次 VSync 信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个 VSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。即FPS降低。

  • CPU做的工作:对象的创建;调整;layout;布局的计算;文本的计算,渲染;图片的渲染,解码,渲染等。
  • GPU做的工作:纹理的渲染,图形混合,图形生成。(离屏渲染发生在GPU中)。

四、离屏渲染的分类

离屏渲染分为两种:一种是CPU的渲染,另外一种就是GPU渲染。

CPU离屏渲染

使用CPU来完成渲染操纵,通常在你使用:

  • drawRect (如果没有自定义绘制的任务就不要在子类中写一个空的drawRect方法,因为只要实现了该方法,就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以 contentsScale的值,造成资源浪费)。
  • 使用Core Graphics。

上面的两种情况使用的就是CPU离屏渲染,首先分配一块内存,然后进行渲染操作生成一份bitmap位图,整个渲染过程会在你的应用中同步的进行,接着再将位图打包发送到iOS里一个单独的进程--render server,理想情况下,render server将内容交给GPU直接显示到屏幕上。

GPU离屏渲染

使用GPU在当前屏幕缓冲区以外开辟一个新的缓冲区进行绘制,通常发生的情况有:

  • 设置cornerRadius, masks, shadows,edge antialiasing(抗锯齿)等
  • 设置layer.shouldRasterize = YES

具体的渲染过程如下图所示。

渲染过程图

通常大家说的离屏渲染指的是GPU这块(当然CPU这块也会有影响,也需要消耗一定的资源),比如修改了layer的阴影或者圆角,GPU需要做额外的渲染操作。通常GPU在做渲染的时候是很快的,但是涉及到offscreen-render的时候情况就可能有些不同,因为需要额外开辟一个新的缓冲区进行渲染,然后绘制到当前屏幕的过程需要做onscreen跟offscreen上下文之间的切换,这个过程的消耗会比较昂贵,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一帧都会涉及到,因此处理不当肯定会对性能产生一定的影响,所以可以的话尽量减少offscreen-render的图层,查看哪些图层需要离屏渲染可以用Instruments的Core Animation工具进行检测,Color Offscreen-Rendered Yellow选项会将对应的图层标记为黄色。

五、提高性能的几种方式

  • 对于圆角可以使用一张中间圆形透明的图覆盖在上面,虽然这会引入blending操作,但是大部分情况下性能会比离屏渲染好。
  • 让你的view层次结构平坦一些,因为OpenGL在渲染layer的时候,在碰到有子层级layer的时候可能需要停下来把两者合成到一个buffer里再接着渲染。(When the OpenGL renderer goes to draw each layer, it may have to stop for some subhierarchies and composite them into a single buffer).
  • 延迟加载图片
    有时候在边滚动边设置图片的时候可能会有一定的影响,因此可以在滚动的时候imageview不执行setimage的操作,滚动停止的时候才加载图片,由于滚动的时候NSRunloop是处于UITrackingRunLoopMode模式下,可以采用如下的方式,将设置图片放到NSDefaultRunLoopMode模式下才进行:
UIImage *downloadedImage = ...;
  [self.avatarImageView performSelector:@selector(setImage:)
                             withObject:downloadedImage
                             afterDelay:0
                                inModes:@[NSDefaultRunLoopMode]];

图片加载的极限优化方式:FastImageCache

五、offScreen检测

  利用instruments可以检测offScreen,在Instruments->Core Animation下,如下图。

offScreen检测

注意:要在真机上才可以。

参考文章

1.iOS app性能优化的那些事(二)

后记

未完,待续,希望大家能喜欢~~~

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

推荐阅读更多精彩内容

  • 相比于当前屏幕渲染,离屏渲染的代价是很高的,这也是iOS移动端优化的必要部分。 OpenGL中,GPU屏幕渲染有以...
    一个人在路上走下去阅读 8,849评论 0 74
  • 一、概述 OpenGL ES是一套多功能开放标准的用于嵌入系统的C-based的图形库,用于2D和3D数据的可视化...
    湾里有桃树阅读 526评论 0 0
  • 离屏渲染 通常来说,计算机系统中 CPU、GPU、显示器是以上面这种方式协同工作的。CPU 计算好显示内容提交到 ...
    Colleny_Z阅读 1,017评论 0 2
  • CALayer与UIView iOS界面中,看到的界面元素基本都是UIView,例如按钮,文本,图片等都是集成自U...
    conowen阅读 1,360评论 0 2
  • 卷首语 欢迎来到 objc.io 的第三期! 这一期都是关于视图层的。当然视图层有很多方面,我们需要把它们缩小到几...
    评评分分阅读 1,765评论 0 18