ios--离屏渲染详解

目录:

  • 1.图像显示原理
  • 2.图像显示原理
    • 2.1 图像到屏幕的流程
    • 2.2 显示器显示的流程
  • 3.卡顿、掉帧
    • 3.1 垂直同步 Vsync + 双缓冲机制 Double Buffering
    • 2.3 掉帧和屏幕卡顿的本质
  • 4.离屏渲染
    • 4.1 什么事离屏渲染、离屏渲染的过程
    • 4.2 既然离屏渲染影响界面,为什么还要用
  • 5.触发离屏渲染
  • 6.如何优化
1.引言

先来聊聊为什么要了解离屏渲染?
看看现在app开发的大环境,14年的时候在深圳,基本上每个公司都要做一个app。不做一个app你都不一定能拉倒更多的投资。再看看现在,死了一大半,现在的用户也不想去下载太多的app。一般手机上只留一些常用的,基本全是大厂的app。然后ios这行问的也就越来越难。性能优化这个绝对会问,在网上也有许多性能优化的总结,但是你不能不知道为什么这么做能优化,要知道其为什么。那么,这时候你就需要知道界面是怎么渲染的,什么时候会掉帧,什么时候会卡顿,这些都使得我们非常有必要去了解离屏渲染。
离屏渲染过程

2.图像显示原理
2.1 图像到屏幕的流程

先来看一张图,我们结合这张图来说


Core Animation 流水线.png

首先要明白的一个东西是Render Server 进程,app本身其实并不负责渲染,渲染是有独立的进程负责的,它就是Render Server 。

当我们在代码里设置修改了UI界面的时候,其实它本质是通过Core Animation修改CALayer。在后续的核心动画总结中 我们会说到UIView和CALayer的关系,以及核心动画的设置等等,这个知识点有点多,需要单独详细的总结出来。所以最后按照图片中的流程显示。

  • 首先,有app处理事件(Handle Events),例如:用户点击了一个按钮,它会触发其他的视图的一个动画等
  • 其次,app通过CPU完成对显示内容的计算 例如:视图的创建,视图的布局 图片文本的绘制等。在完成了对显示内容的计算之后,app对图层进行打包,并在下一次Runloop时,将其发送至Render Server
  • 上面我们提到,Render Server负责渲染。Render Server通过执行Open GL、Core Graphics Metal相关程序。 调用GPU
  • GPU在物理层完成了对图像的渲染。

说到这我们就要停一下,我们来看下一个图


GPU.png

上面的流程图 细化了GPU到控制器的这一个过程。
GPU 拿到位图后执行顶点着色、图元装配、光栅化、片段着色等,最后将渲染的结果交到了Frame Buffer(帧缓存区当中)
然后视频控制器从帧缓存区中拿到要显示的对象,显示到屏幕上
图片中的黄色虚线暂时不用管,下面在说垂直同步信号的时候,就明白了。
这是从我们代码中设置UI,然后到屏幕的一个过程。

2.2 显示器显示的过程

现在从帧缓存中拿到了渲染的视图,又该怎么显示到显示器上面呢?

先来看一张图
扫描显示.png

从图中我们也能大致的明白显示的一个过程。

显示器的电子束从屏幕的左上方开始逐行显示,当第一行扫描完之后接着第二行 又是从左到右,就这样一直到屏幕的最下面扫描完成。我们都知道。手机它是有屏幕的刷新次数的。安卓的现在好多是120的,ios是60。1秒刷新60次,当我们扫描完成以后,屏幕刷新,然后视图就会显示出来。

3.UI卡顿 掉帧
3.1垂直同步 Vsync + 双缓冲机制 Double Buffering

首先我们了解了上面渲染的过程以后,需要考虑遇到一些特别的情况下,该怎么办?在我们代码里写了一个很复杂的UI视图,然后CPU计算布局、GPU渲染,最后放到缓存区。如果在电子束开始扫描新的一帧时,位图还没有渲染好,而是在扫描到屏幕中间时才渲染完成,被放入帧缓冲器中 。
那么已扫描的部分就是上一帧的画面,而未扫描的部分就是新一帧的图像,这样是不是就造成了屏幕撕裂了。

但是,在我们平常开发的过程遇到过屏幕撕裂的问题吗?没有吧,这是为什么呢?
显然是苹果做了优化操作了。也就是垂直同步 Vsync + 双缓冲机制 Double Buffering。

垂直同步 Vsync
垂直同步 Vsync相当于给帧缓存加了锁,还记得上面说到的那个黄色虚线嘛,在我们扫描完一帧以后,就会发出一个垂直同步的信号,通知开始扫描下一帧的图像了。他就像一个位置秩序的,你得给我排队一个一个来,别插队。插队的后果就是屏幕撕裂。
双缓冲机制 Double Buffering
扫描显示排队进行了,这样在进行下一帧的位图传入的时候,也就意味着我要立刻拿到位图。不能等CPU+GPU计算渲染后再给位图,这样就影响性能。要怎么解决这个问题呢?肯定是 在你快要渲染之前你就要把这些都完成了。你就像排队打针一样,为了节省时间肯定事先都会挽起袖子,到医生那时,直接一针下去了事。扯远了 哈哈。想预先渲染好,就需要另外一个缓存来放下一帧的位图,在它需要扫描的时候,再把渲染好的位图给了帧缓存,帧缓存拿到以后 开始快乐的扫描 显示。
一个图解释

双缓存.png

3.2 掉帧卡顿

垂直同步和双缓存机制完美的解决了屏幕撕裂的问题,但是又引出一个新的问题:掉帧。
掉帧是什么意思呢?从网上copy了一份图


掉帧.png

其实很好理解,上面我们说了ios的屏幕刷新是60次,那么在一次刷新的过程中,我们CPU+GPU它没有把新渲染的位图放到帧缓存区,这时候是不是还是显示的原来的图像。当下刷新下一帧的时候,拿到了新的位图,这里是不是就丢失了一帧。

卡顿的根本原因:
CPU和GPU渲染流水线耗时过长 掉帧
我们平常写界面的时候,通过一些开源的库或者自己使用runloop写的库来检测界面卡顿的时候,屏幕刷新率在50以上就很可以了。一般人哪能体验到掉了10帧。你要刷新率是30,那卡顿想过就很明显了。

4 离屏渲染
4.1什么是离屏渲染 离屏渲染的过程

是指在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作.
过程:首先会创建一个当前屏幕缓冲区以外的新缓存区,屏幕渲染会有一个上下文环境,离屏渲染的过程就是切花上下文环境,现充当前屏幕切换到离屏,等结束以后又将上下文切换回来。所以需要更长的时间来处理。时间一长就可能造成掉帧。
并且 Offscreen Buffer离屏缓存 本身就需要额外的空间,大量的离屏渲染可能造成内存过大的压力。而且离屏缓存区并不是没有限制大小的,它是不能超过屏幕总像素的2.5倍。

4.2为什么要使用离屏渲染

1.一些特殊效果需要使用额外的 Offscreen Buffer 来保存渲染的中间状态,所以不得不使用离屏渲染。
2.处于效率目的,可以将内容提前渲染保存在 Offscreen Buffer 中,达到复用的目的。
当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前(下一个VSync信号开始前)不能直接在屏幕中绘制,所以就需要屏幕外渲染。

5.触发离屏渲染
  1. 为图层设置遮罩(layer.mask)
  2. 图层的layer. masksToBounds/view.clipsToBounds属性设置为true
  3. 将图层layer. allowsGroupOpacity设置为yes和layer. opacity<1.0
  4. 为图层设置阴影(layer.shadow)
  5. 为图层设置shouldRasterize光栅化
    6 复杂形状设置圆角等
    7 渐变
    8 文本(任何种类,包括UILabel,CATextLayer,Core Text等)
    9 使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。
6 离屏渲染的优化
1 圆角优化

方法一

iv.layer.cornerRadius = 30;
iv.layer.masksToBounds = YES;

方法二
利用mask设置圆角,利用贝塞斯曲线和CAShapeLayer来完成

CAShapeLayer *mask1 = [[CAShapeLayer alloc] init];
mask1.opacity = 0.5;
mask1.path = [UIBezierPath bezierPathWithOvalInRect:iv.bounds].CGPath;
iv.layer.mask = mask1;

方法三
利用CoreGraphics画一个圆形上下文,然后把图片绘制上去

- (void)setCircleImage
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        UIImage * circleImage = [image imageWithCircle];
        dispatch_async(dispatch_get_main_queue(), ^{
            imageView.image = circleImage;
        });
    });
}


#import "UIImage+Addtions.h"
@implementation UIImage (Addtions)
//返回一张圆形图片
- (instancetype)imageWithCircle
{
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0);
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
    [path addClip];
    [self drawAtPoint:CGPointZero];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
}
shadows(阴影)

设置阴影后,设置CALayer的shadowPath

view.layer.shadowPath = [UIBezierPath pathWithCGRect:view.bounds].CGPath;
mask(遮罩)

不使用mask
使用混合图层 使用混合图层,在layer上方叠加相应mask形状的半透明layer

sublayer.contents = (id)[UIImage imageNamed:@"xxx"].CGImage;
[view.layer addSublayer:sublayer];
allowsGroupOpacity(组不透明)

关闭 allowsGroupOpacity 属性,按产品需求自己控制layer透明度

edge antialiasing(抗锯齿)

不设置 allowsEdgeAntialiasing 属性为YES(默认为NO)

当视图内容是静态不变时,设置 shouldRasterize(光栅化)为YES,此方案最为实用方便

view.layer.shouldRasterize = true;
view.layer.rasterizationScale = view.layer.contentsScale;

如果视图内容是动态变化的,例如cell中的图片,这个时候使用光栅化会增加系统负荷。

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

推荐阅读更多精彩内容