关于离屏渲染

自述:
引言:一款优秀的应用,流畅很关键,用户使用60的fps的应用,跟使用30的fps的应用感受是完全不一样的。类似于半糖这种优秀的应用肯定花了大把精力去优化界面网上关于优化的界面的文章一搜一大把本文并不是讲界面优化的,优化的话推荐下面几篇文章。;

YYKit作者:“iOS保持界面流畅的技巧”(我相信认真看一定有收获!)

离屏渲染的优化(这篇文章很强)

jim:浅谈iOS中的视图优化

有些东西研究起来挺费劲的。但是想要更多的收获,还真就必须得研究。知识零零散散,时间久了再次用起来难免会生疏,又得重新找资料,看文章。很麻烦。索性就整理个比较全面的知识点供以后复习。

屏幕渲染
OpenGL的中,GPU屏幕渲染有两种方式。
屏幕渲染(当前屏幕渲染)

指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。

离屏渲染(离屏渲染)

指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。

几个名词“GPU”“缓冲区”。

不知道GPU的就行百度吧( - - 宝宝说不清)。说下缓冲区。要明白缓冲区,首先就得要知道图像显示出来的原理,本文的第一篇博客里面介绍的很详细。
显示器显示出来的图像是经过CRT电子枪一行一行的扫描。(可以是横向的也可以是纵向,具体CRT电子枪又是什么,百度文库介绍的很详细。),扫描出来就呈现了一帧画面,随后电子枪又会回到初始位置循环扫描,为了让显示器的显示跟视频控制器同步,当电子枪新扫描一行的时候。准备扫描的时候,会发送一个水平同步信号(HSync信号),显示器一般是固定刷新频率的,这个刷新的频率其实就是行同步信号产生的频率。然后CPU计算好帧等属性,就将计算好的内容提交给GPU去渲染,GPU渲染完成之后就会放入帧缓冲区,然后视频控制器会按照行同步信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器。就显示出来了。
原理图就不放了,过一遍概念。

离屏渲染的代价很高,想要进行离屏渲染,首选要创建一个新的缓冲区,屏幕渲染会有一个上下文环境的一个概念,离屏渲染的整个过程需要切换上下文环境,先从当前显示幕切换到离屏,等结束后,又要将上下文环境切换回来。这也是为什么会消耗性能的原因了。
。由于垂直同步的机制,如果在一个VSync时间内,CPU或GPU没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

那有个问题:为什么离屏渲染这么耗性能,为什么有这套机制呢?

当使用圆角,阴影,遮罩的时候,图层属性的混合体被指定为在未预合成之前(下一个HSync信号开始前)不能直接在屏幕中绘制,所以就需要屏幕外渲染。这么理解。老板叫我短时间间内做一个app。我一个人能做,但是时间太短,所以我得让我朋友一起来帮着我做。(性能消耗:也就是耗你跟你朋友之间沟通的这些成本,多浪费啊)。但是没办法谁让你做不完呢。
这么一讲会不会比较明白点。

哪些操作会触发离屏渲染?
官方公开的资料里关于离屏渲染的信息最早是在2011年的WWDC上,在多个会话里都提到了尽量避免会触发离屏渲染的效果,包括:mask,shadow,group opacity,edge antialiasing。

shouldRasterize(光栅化)

面具(遮罩)

阴影(阴影)

边缘抗锯齿(anti-锯齿)

group opacity(不透明)

复杂形状设置圆角等

渐变

文本(UILabel,CATextLayer,核心文本等)…

介绍一些属性。

应光栅化:将图转化为一个个栅格组成的图像。光栅化特点:每个元素对应帧缓冲区中的一像素。
shouldRasterize = YES在其它属性触发离屏渲染的同时,会将光栅化后的内容缓存起来,如果对应的layer或者sublayers没有发生改变,在下一帧的时候可以直接复用,从而减少渲染的频率。
当使用光栅化是,可以开启“Color Hits Green and Misses Red”来检查该场景下是否适合选择光栅化,绿色表示缓存被复用,红色表示缓存在被重复创建。对于经常变动的内容,不要开启,否则会造成性能的浪费。

如果cell里面的内容不断变化(cell的复用),如果设置了cell.layer.shouldRaseterize = YES则会降低图形性能,造成离屏渲染。

掩模(遮罩)。
掩模是层的一个属性。

/一个其alpha通道被用作掩码的图层来选择
*图层的背景和合成图层的结果
*过滤背景的内容。默认为零。当用作
*蒙板图层的“compositingFilter”和“backgroundFilters”
*属性被忽略。将遮罩设置为新图层时,
*新图层必须有一个零超层,否则的行为是
*未定义。嵌套蒙版(带有自己的蒙版的蒙版图层)是
不受支持。 /

@属性(可为空,强)CALayer * mask;
大概的意思。当透明度改变的时候,这个mask就是覆盖上去的那个阴影。该层的alpha的决定了多少层背景跟内容通过并显示,完全或者部分不透明的像素允许潜在的内容通过并显示。
默认是nil,当配置一个遮罩的时候,记得设置遮罩的大小,位置。已确保跟盖图层对齐。(这是官方文档说的如果不对齐会怎样.you can try。我试过是只能显示对齐的那一部分。)
如果你想给这个属性赋值,前提是必须没有superLayer,如果有superLayer,这个行为则是无效的。(你也可以尝试一下反的。)

提示:用掩模可以做一些转场动画这里就不介绍了。

那说了这么多,他就是生来会触发离屏渲染的。所以要谨慎设置透明度。提到透明度,另外补充一个概念。)

彩色混合层
用吉姆的话来介绍它就是

屏幕上的每个像素点的颜色是由当前像素点上的多层层(如果存在)共同决定的,GPU会进行计算出混合颜色的RGB值,最终显示在屏幕上。而这需要让GPU计算,所以我们要尽量避免设置alpha,这样GPU会忽略下面所有的layer,节约计算量。再提一下opaque这个属性,网上普遍认为view.opaque = YES,GPU就不会进行图层混合计算了。这个结论是错误的,其实view.opaque事实上并没什么卵用。
如果你真的想达到这个效果,可以用layer.opaque,这个才是正确的做法

阴影(阴影)。
略过…(自己可以随便尝试一下)
再介绍一下edge antialiasing(抗锯齿)这个吧。因为之前自己也没接触过,很多人估计也是没接触过吧。

177286BA-E7BE-4CFE-B992-AF18B30FE1DE.png
翻译:

是否允许执行反锯齿边缘。
默认的值是NO。(不使用抗锯齿,也有人叫反锯齿),当值为YES的时候,在layer的edgeAntialiasingMask属性层依照这个值允许抗锯齿边缘,(参照这个值)可以在info.plist里面开启这个属性。

0068582C-92E8-4A80-AAD8-C89E22668964.png
放一个plist常见的key value表info.plist配置表(楼主真的好…)

说了这么多。那抗锯齿又是啥???

抗锯齿的概念(随便看看。)
在我们iOS中表现参考这里

CALayer *图层= [CALayer图层]; 
layer.position = CGPointMake(100,100);
layer.bounds = CGRectMake(0,0,100,100);
layer.backgroundColor = [UIColor redColor] .CGColor;
//layer.allowsEdgeAntialiasing = YES;
[self.view.layer addSublayer:layer]; ```

正常添加层在视图上是这样的。

![模拟器不旋转巴纽](http://upload-images.jianshu.io/upload_images/1315383-c2502546a0ca0a32.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

下一步。
CGFloat角度= M_PI / 30.0;
[layer setTransform:CATransform3DRotate(layer.transform,angle,0.0,0.0,1.0)];```
在模拟器表现是这样的。

模拟器NO.png
如果layer.allowsEdgeAntialiasing = YES;

—在模拟器上是

模拟器YES.png
—在真机上。

真机YES.png
可见真机效果跟模拟器还是有差距的,(模拟器边缘比真机模糊,)官方文档也有提到这点

绘制与像素边界不对齐的图层时使用抗锯齿。此选项允许在模拟器中进行更复杂的渲染,但可能会对性能产生显着影响。

这是UIView的的抗锯齿,在模拟器上还是有性能的消耗的。

看看效果就行,具体的不研究太多了,知道怎么避免就行。

group opacity(不透明)
组不透明巴纽
大概的意思就是这个属性决定了Core Animation框架下子layers从他们Superlayer。继承过来的不透明度。
iOS 6之前是默认NO,iOS7以后就默认是YES。文档也是说可以在模拟器上呈现。但是对性能有明显的影响。
这里我就不测试了。这个属性过一遍,重点是下一个属性。

复杂形状设置圆角。
我们在开发中经常会对一些图片或者按钮进行圆角处理,需求还是特别多的,设置圆角有多种方法,我列一下常见的方式。

设置层层的圆角大小。经常我们还会设置masksToBounds,
//按正方形来算。长的一半就是半径。按照这个去设置就是圆角了,长方形的话则按短的那一边
_imageView.layer.cornerRadius = iamgeView.width / 2;
_imageView.layer.masksToBounds = YES;```
这样做对于少量的图片,这个没有什么问题,但是数量比较多的时候,UITableView的滑动可能不是那么流畅,屏幕的帧数下降,影响用户体验。
2.使用层的遮罩遮罩和CAShapLayer
创建圆形的CAShapeLaer对象,设置为View的mask属性,这样也可以达到圆角的效果,但是前面提到过了,使用mask属性会离屏渲染,不仅仅这样,还曾加了一个CAShapLayer对象。着实不可以取。

3.使用带圆形的透明图片。(求个切图大师 - - )
4. CoreGraphics自定义绘制圆角。

提到CoreGraphics,还有一种特殊的“离屏渲染”方式不得不提,那就是drawRect方法。触发的方式:
如果我们重写了drawRect方法,并且使用CoreGraphics技术去绘制。就设计到了CPU渲染,整个渲染,由CPU在应用程序内同步完成,渲染之后再交给GPU GPU显示。(这种方式对性能的影响不是很高)

提示:CoreGraphic通常是线程安全的,所以可以进行异步绘制,然后在主线程上更新。

(void)display {
dispatch_async(backgroundQueue,^ {
CGContextRef ctx = CGBitmapContextCreate(…);
CGImageRef img = CGBitmapContextCreateImage(ctx);
CFRelease(CTX);
dispatch_async(mainQueue,^ {
layer.contents = img;
});
});
}

  • ###如何检测我的项目里面离屏渲染了
    *之前看了一些文章说在intruments里面的CoreAnimation里面有工具。检测。(没找着。求补充)

打开的正确方式:
模拟器的debug - >选取颜色Offscreen-Rendered。

![BB55F33F-97CC-4C28-B3F1-22456A2A7BD8.png](http://upload-images.jianshu.io/upload_images/1315383-752efaac717c3947.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240

开启后会把那些需要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。

正常:是这样的
![正常渲染巴纽](http://upload-images.jianshu.io/upload_images/1315383-de2aad921928fa7b.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

有问题的图层:

![调试巴纽](http://upload-images.jianshu.io/upload_images/1315383-61746e58c6057b02.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240)
可以看见我设置了圆角的ImageView的有问题。

###项目开发中怎么去处理?

抛出一个问题:需求就是有很多圆角那我们项目中应该怎么去处理圆角呢?

1.使用[YYWebImage去处理](http://www.jianshu.com/p/60cd5f8bb4cb)
2. [iOS中圆角图片的处理](http://www.jianshu.com/p/82e68984711f)

相信看完两篇文章,多少都会能收获一点!

有些人说:

iOS 9.0之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的(这句话不知道谁说的。自己有没有去尝试呢???)

结论:经过测试

![70915C7C-7523-4008-9A88-B5682407926D.png](http://upload-images.jianshu.io/upload_images/1315383-a58dfe9543375e1d.png?imageMogr2/auto-orient/strip|imageView2/2/w/1240

大家可以看到,
UIButton的masksToBounds = YES下发生离屏渲染与背景图存不存在有关系,如果没有给按钮设置btn.image = [UIImage imageName:@“xxxxx”];是不会产生离屏渲染的。

关于UIImageView,现在测试发现(现版本:iOS10),在性能的范围之内,给UIImageView设置圆角是不会触发离屏渲染的,但是同时给UIImageView设置背景色则肯定会触发。触发离屏渲染跟png.jpg格式并无关联(可能采取的压缩格式不同,这里不做探讨,这里我给出结果是没有关系)


  • ###总结:
    *对于网上一些文章得出的结论,我觉得大家得理性分析,并不是每个人都是对的,只有经过自己实践才能得出较好的定论。本文也是,我希望哪里有理解错或者其它什么错误请提出(认真脸),人无完人,我希望在学习的道路上能碰见更多一起进步的人!

作者:uncleRX
链接:HTTPS://www.jianshu.com/p/57e2ec17585b
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值