当前显示屏渲染不须求额外创造新的缓存,然后视频控制器会根据HSync信号逐行读取帧缓冲区的数码

活动拔取优化到结尾根本仍然看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 格林 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 法斯特 Path
Blue:那些选项对那几个使用OpenGL的图层才有用,像是GLKView或者
CAEAGLLayer,若是不出示灰色则表示使用了CPU渲染,绘制在了显示器外,彰显青色代表正常。

Flash Updated
Regions:当对图层重绘的时候回显得蓝色,假如频仍暴发则会潜移默化属性。可以用扩展缓存来增强品质。

相关文章