iOS-初识离屏渲染

NSLog(@"SHOW TIME");

 

【 ⚠️⚠️⚠️  离屏渲染易造成滑动卡顿掉帧,要尽量避免离屏渲染~~ 】

 

一、离屏渲染是什么?

Off-Screen-Rendering,发生在GPU上,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染工作。

- 离屏渲染造成卡顿掉帧的因素:

1、要消耗资源新建一个缓冲区;

2、上下文切换代价大。离屏渲染过程需要多次切换上下文:先从当前屏幕(On-Screen)切换到离屏(Off-Screen)进行渲染,渲染结束后上下文再从离屏切换到当前屏幕,而上下文的切换代价较大。

以上因素使得GPU在离屏渲染时产生了额外的开销由于垂直同步的机制,如果在一个 HSync 时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面掉帧卡顿的原因。

二、什么情况下会触发离屏渲染

- 设置圆角(同时设置maskToBounds=YES才触发);

- 图层蒙版

- 设置阴影

- 光栅化

三、优化方案

官方对离屏渲染产生性能问题也进行了优化:

iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染。

iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。

3.1、圆角优化

在APP开发中,圆角图片还是经常出现的。如果一个界面中只有少量圆角图片或许对性能没有非常大的影响,但是当圆角图片比较多的时候就会APP性能产生明显的影响。

我们设置圆角一般通过如下方式:

imageView.layer.cornerRadius = CGFloat(10);

imageView.layer.masksToBounds = YES;

这样处理的渲染机制是GPU在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会给我们带来额外的性能损耗,如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,性能的代价会宏观地表现在用户体验上——掉帧。

优化方案1:

使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100,100,100,100)];

imageView.image = [UIImage imageNamed:@"myImg"];

//开始对imageView进行画图

UIGraphicsBeginImageContextWithOptions(imageView.bounds.size,NO,1.0);

//使用贝塞尔曲线画出一个圆形图

[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width]addClip];

[imageView drawRect:imageView.bounds];

imageView.image=UIGraphicsGetImageFromCurrentImageContext();

//结束画图

UIGraphicsEndImageContext();

[self.view addSubview:imageView];

优化方案2(推荐):

使用CAShapeLayer和UIBezierPath设置圆角

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];

imageView.image = [UIImage imageNamed:@"myImg"];

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];

CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];

//设置大小

maskLayer.frame = imageView.bounds;

//设置图形样子

maskLayer.path = maskPath.CGPath;

imageView.layer.mask = maskLayer;

[self.view addSubview:imageView];

对于方案2需要解释的是:

CAShapeLayer继承于CALayer,可以使用CALayer的所有属性值;

CAShapeLayer需要贝塞尔曲线配合使用才有效果;

使用CAShapeLayer(属于CoreAnimation)与贝塞尔曲线可以实现不在view的drawRect(继承于CoreGraphics走的是CPU,消耗的性能较大)方法中画出一些想要的图形

CAShapeLayer动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况。

总的来说就是用CAShapeLayer的内存消耗少,渲染速度快,建议使用优化方案2。

 

3.2、shadow阴影优化

对于shadow,如果图层是个简单的几何图形或者圆角图形,我们可以通过设置shadowPath来优化性能,能大幅提高性能。示例如下:

imageView.layer.shadowColor = [UIColor grayColor].CGColor;

imageView.layer.shadowOpacity = 1.0;

imageView.layer.shadowRadius = 2.0;

UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageView.frame];

imageView.layer.shadowPath = path.CGPath;

我们还可以通过设置shouldRasterize属性值为YES来强制开启离屏渲染。其实就是光栅化(Rasterization)。既然离屏渲染这么不好,为什么我们还要强制开启呢?当一个图像混合了多个图层,每次移动时,每一帧都要重新合成这些图层,十分消耗性能。当我们开启光栅化后,会在首次产生一个位图缓存,当再次使用时候就会复用这个缓存。但是如果图层发生改变的时候就会重新产生位图缓存。所以这个功能一般不能用于UITableViewCell中,cell的复用反而降低了性能。最好用于图层较多的静态内容的图形。而且产生的位图缓存的大小是有限制的,一般是2.5个屏幕尺寸。在100ms之内不使用这个缓存,缓存也会被删除。所以我们要根据使用场景而定。

 

3.3、其他的一些优化建议

当我们需要圆角效果时,可以使用一张中间透明图片蒙上去

使用ShadowPath指定layer阴影效果路径

使用异步进行layer渲染(Facebook开源的异步绘制框架AsyncDisplayKit)

设置layer的opaque值为YES,减少复杂图层合成

尽量使用不包含透明(alpha)通道的图片资源

尽量设置layer的大小值为整形值

直接让美工把图片切成圆角进行显示,这是效率最高的一种方案

很多情况下用户上传图片进行显示,可以让服务端处理圆角

使用代码手动生成圆角Image设置到要显示的View上,利用UIBezierPath(CoreGraphics框架)画出来圆角图片

 

NSLog(@"END...");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值