和大规模的离屏渲染Say,以及缓解方案

移步接纳优化到终极根本仍然看FPS(页面流畅程度)质量、内存占用等地点。离屏渲染也是老生常谈的一个难题,本文侧重点在普遍导致离屏渲染的因素及解决方案。

显示屏显示图像的法则:

那么为何离屏渲染会挑起品质难点?

OpenGL中,GPU显示器渲染有二种方法: On-Screen Rendering (当前显示屏渲染)
Off-Screen Rendering (离屏渲染)
,当前显示器渲染不要求至极创设新的缓存,也不要求开启新的上下文,相对于离屏渲染品质更好。可是受当前显示屏渲染的受制因素限制(唯有我上下文、屏幕缓存有限等),当前屏幕渲染有些意况下的渲染解决不了的,就动用到离屏渲染。离屏渲染的任何经过要求切换上下文环境,先从
当前屏幕切换到离屏,等收尾后,又要将上下文环境切换回来.那也是为何会消耗质量的缘故了。

离屏渲染引发因素有
cornerRadius(设置圆角)、shadows(阴影)、masks(遮罩)、edge
antialiasing(抗锯齿)、group
opacity(不透明)、shouldRasterize(光栅化)

等,至于检测离屏渲染的工具 Instruments的Core Animation
就不多说了。本文首要介绍 安装圆角阴影 的方案。

高中物理应该学过显示屏是如何展示图像的:要求出示的图像经过CRT电子枪以极快的快慢一行一行的扫描,扫描出来就显现了一帧画面,随后电子枪又会回去初始地点循环扫描,形成了大家来看的图纸或视频。

安装圆角

例行做法:

   //只需要设置layer层的两个属性
   //设置圆角
   imageView.layer.cornerRadius = imageView.frame.size.width / 2;
   //将多余的部分切掉
   imageView.layer.masksToBounds = YES;

那里提供三种幸免离屏渲染的方案

  • 1.视图上添加一个子layer到最上层,用于覆盖该视图及其子视图,设置layer的图片为刚刚可以遮盖成所需圆角样子,并且图片颜色刚好是该视图父视图的背景颜色就已毕想要的意义。
    原稿地址
    ,该小编写的很好,封装了一个UIView的分类,3个API,分别是
    设置一个四角圆角,设置一个指定地方的圆角,设置一个带边框的圆角

    github地址

/**
 设置一个四角圆角

 @param radius 圆角半径
 @param color  圆角背景色
 */
- (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color;

/**
 设置一个普通圆角

 @param radius  圆角半径
 @param color   圆角背景色
 @param corners 圆角位置
 */
- (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color corners:(UIRectCorner)corners;

/**
 设置一个带边框的圆角

 @param cornerRadii 圆角半径cornerRadii
 @param color       圆角背景色
 @param corners     圆角位置
 @param borderColor 边框颜色
 @param borderWidth 边框线宽
 */
- (void)xw_roundedCornerWithCornerRadii:(CGSize)cornerRadii cornerColor:(UIColor *)color corners:(UIRectCorner)corners borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;

下载下来那么些分类直接拖入工程就可以利用了,调用很有益于,可是使用的时候会发觉,这多少个API都需求传一个参数
cornerColor (父视图的背景象),所以也致使了那些效果的局限,即
如若该父视图的颜色不是纯色,此时该方法就不适用了,同样
假定父视图的颜色会变化,那达成起来的代码也不那么优雅,如下图,有点狼狈,那里引出了第三种方案。

边角颜色与背景观不符

  • 2.因此修改layer.mask,首先通过贝塞尔曲线创制基于矢量的途径
    ,传递给CAShapeLayer举行渲染。路径闭环,再把绘制出的Shape赋值给layer.mask,在Mask范围之外的Layer将不被出示从而达到圆角效应。代码达成很粗略,如下:

    UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(130, 330, 100, 100)];
    [btn setBackgroundColor:[UIColor colorWithRed:(226.0 / 255.0) green:(113.0 / 255.0) blue:(19.0 / 255.0) alpha:1]];
    [backgroundImageView addSubview:btn];
    //绘制曲线路径
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:btn.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:btn.bounds.size];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
    //设置大小
    maskLayer.frame = btn.bounds;
    //设置图形样子
    maskLayer.path = maskPath.CGPath;
    btn.layer.mask = maskLayer;

效果图:

个体觉得第三种方案更简单而且作用伸张性更强些

为了让显示屏的显得跟视频控制器同步,当电子枪新扫描一行的时候,准备扫描的时发送一个程度同步信号(HSync信号),显示屏的基础代谢频率就是HSync信号暴发的效用。然后CPU总括好frame等品质,将总计好的内容提交GPU去渲染,GPU渲染好之后就会放入帧缓冲区。然后视频控制器会依照HSync信号逐行读取帧缓冲区的数额,经过可能的数模转换传递给显示器,就显得出来了。那里只是简作描述,专业描述请自行查询。

安装阴影

常规做法:

//阴影的颜色
self.imageView.layer.shadowColor= [UIColorblackColor].CGColor;
//阴影的透明度
self.imageView.layer.shadowOpacity=0.8f;
//阴影的圆角
self.imageView.layer.shadowRadius=4;
//阴影偏移量
self.imageView.layer.shadowOffset=CGSizeMake(0,0);

优化方案:
幸免对shadowOffset直接修改,通过调用setShadowPath来提供一个CGPath给视图的Layer,向Core
Animation提供渲染的View的形态Shape,就会压缩离屏渲染总结

[self.imageView.layer setShadowPath:[[UIBezierPath 
    bezierPathWithRect:myView.bounds] CGPath]];

增补:当使用阴影的视图形状发生变化时,即shadowPath并不会跟随CALayer的bounds属性进行变更,所以在layer的bounds爆发变化之后必要手动更新shadowPath才能让其适配新的bounds。现实推荐看这篇作品

有关界面流畅假若想要深层探索可以看 YYKit作者 写的篇章iOS
保持界面流畅的技巧

。该小说从屏幕突显图像的原理,到改革的方案都有详细介绍。

多谢各位,欢迎指教!

GPU显示器渲染有三种艺术:

(1)On-Screen Rendering (当前屏幕渲染) 

指的是GPU的渲染操作是在当下用来浮现的显示器缓冲区进行。

(2)Off-Screen Rendering (离屏渲染)

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

当下屏幕渲染不需求额外创造新的缓存,也不须求打开新的上下文,相对于离屏渲染性能更好。然则受当前显示屏渲染的局限因素限制(只有我上下文、屏幕缓存有限等),当前屏幕渲染有些景况下的渲染解决不了的,就应用到离屏渲染。

相比之下于当下屏幕渲染,离屏渲染的代价是很高的,主要展示在多少个地方:

(1)创立新缓冲区

要想拓展离屏渲染,首先要开创一个新的缓冲区。

(2)上下文切换

离屏渲染的一体经过,要求反复切换上下文环境:先是从当前屏幕(On-Screen)切换来离屏(Off-Screen),等到离屏渲染停止之后,将离屏缓冲区的渲染结果展现到屏幕上有要求将上下文环境从离屏切换来眼前显示屏。而上下文环境的切换是要提交很大代价的。

是因为垂直同步的体制,如果在一个 HSync 时间内,CPU 或者 GPU
没有已毕内容交给,则那一帧就会被甩掉,等待下几遍机遇再显示,而此刻显示器会保留从前的情节不变。那就是界面卡顿的原故。

既是离屏渲染这么耗品质,为何有那套机制吗?

有些功力被认为不可以直接显示于屏幕,而急需在其他地方做额外的处理预合成。图层属性的混合体没有预合成从前不可能间接在显示器中绘制,所以就需求屏幕外渲染。显示器外渲染并不代表软件绘图,但是它意味着图层必须在被出示从前在一个屏幕外上下文中被渲染(不论CPU仍旧GPU)。

上面的意况或操作会抓住离屏渲染:

为图层设置遮罩(layer.mask)

– 将图层的layer.masksToBounds / view.clipsToBounds属性设置为true

– 将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0

– 为图层设置阴影(layer.shadow *)。

– 为图层设置layer.shouldRasterize=true

– 具有layer.cornerRadius,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的图层

– 文本(任何项目,包罗UILabel,CATextLayer,Core Text等)。

– 使用CGContext在drawRect
:方法中绘制半数以上景色下会招致离屏渲染,甚至唯有是一个空的贯彻。

优化方案

合法对离屏渲染发生质量难题也进展了优化:

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

iOS 9.0
之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,假使设置任何阴影效果之类的依旧会触发离屏渲染的。

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.boundscornerRadius: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。

2、shadow优化

对于shadow,借使图层是个简易的几何图形或者圆角图形,大家可以透过安装shadowPath来优化品质,能大幅升高质量。示例如下:

imageView.layer.shadowColor=[UIColorgrayColor].CGColor;

imageView.layer.shadowOpacity=1.0;

imageView.layer.shadowRadius=2.0;

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

imageView.layer.shadowPath=path.CGPath;

我们还是能通过设置shouldRasterize属性值为YES来强制开启离屏渲染。其实就是光栅化(Rasterization)。既然离屏渲染这么不佳,为何大家还要强制开启呢?当一个图像混合了五个图层,每回运动时,每一帧都要重新合成那个图层,十分消耗质量。当我们打开光栅化后,会在首次发出一个位图缓存,当再次行使时候就会复用那些缓存。可是倘使图层暴发变更的时候就会再也发生位图缓存。所以那么些职能相似不能够用来UITableViewCell中,cell的复用反而下跌了品质。最好用于图层较多的静态内容的图样。而且发生的位图缓存的深浅是有限定的,一般是2.5个显示屏尺寸。在100ms之内不采纳这一个缓存,缓存也会被剔除。所以咱们要基于使用境况而定。

3、其余的片段优化建议

当大家须求圆角意义时,可以使用一张中间透明图片蒙上去

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

使用异步举行layer渲染(脸书开源的异步绘制框架AsyncDisplayKit)

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

尽量选择不含有透明(alpha)通道的图纸资源

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

直白让美工把图纸切成圆角进行浮现,那是功效最高的一种方案

不胜枚举动静下用户上传图片进行显示,可以让服务端处理圆角

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

Core Animation工具检测离屏渲染

对此离屏渲染的检测,苹果为大家提供了一个测试工具Core
Animation。可以在Xcode->Open Develeper Tools->Instruments中找到

新普金娱乐 1

Core Animation工具用来监测Core
Animation质量,提供可知的FPS值,并且提供多少个挑选来测量渲染品质。如下图:

新普金娱乐 2

下边咱们来声明每个选项的意义:

Color Blended
Layers:这些选项若是勾选,你能看到哪些layer是晶莹剔透的,GPU正在做混合总结。突显黑色的就是晶莹的,红色就是不透明的。

Color Hits Green and Misses
Red:假如勾选这一个选项,且当我们代码中有设置shouldRasterize为YES,那么粉红色代表没有复用离屏渲染的缓存,绿色则代表复用了缓存。大家当然希望可以复用。

Color Copied
Images:依据法定的说教,当图片的颜色格式GPU不支持的时候,Core
Animation会

拷贝一份数据让CPU举行转载。例如从网络上下载了TIFF格式的图片,则要求CPU举办转向,那几个区域会体现成黄色。还有一种情状会触发Core
Animation的copy方法,就是字节不对齐的时候。如下图:

新普金娱乐 3

Color Immediately:默认情状下Core
Animation工具以每毫秒10次的作用更新图层调试颜色,就算勾选那么些选项则移除10ms的推迟。对少数景况需求这么,可是有可能影响正常帧数的测试。

Color Misaligned
Images:勾选此项,倘诺图片要求缩放则标记为黑色,借使没有像素对齐则标记为藏青色。像素对齐大家曾经在上边装有介绍。

Color Offscreen-Rendered
Yellow:用来检测离屏渲染的,借使显示绿色,表示有离屏渲染。当然还要结合Color
Hits 格林 and Misses Red来看,是不是复用了缓存。

Color OpenGL Fast Path
Blue:这几个选项对那个使用OpenGL的图层才有用,像是GLKView或者
CAEAGLLayer,若是不显得黄色则象征使用了CPU渲染,绘制在了显示器外,显示紫色代表正常。

Flash Updated
Regions:当对图层重绘的时候回显得藏紫色,即便频仍发生则会潜移默化属性。可以用增加缓存来增强质量。

相关文章