标签:ted 推荐 dcl 过程 适配 opacity 动态添加 www send
离屏渲染,指的是 GPU 在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
离屏渲染的数量才是影响 app 交互性能的根源。
离屏渲染耗时是发生在离屏这个动作上面,而不是渲染。原因主要在于创建缓冲区和上下文切换。创建新的缓冲区代价都不算大,付出最大代价的是上下文切换。
不管是在 GPU 渲染过程中,还是熟悉的进程切换,上下文切换在哪里都是一个相当耗时的操作。
首先要保存当前屏幕渲染环境,然后切换到一个新的绘制环境,申请绘制资源 -> 初始化环境 -> 开始一个绘制 -> 绘制完毕后销毁这个绘制环境,如需要切换到 On-Screen Rendering 或者再开始一个新的离屏渲染重复之前的操作。
下图描述了一次 mask 的渲染操作。
一次 mask 发生了两次离屏渲染和一次主屏渲染。即使忽略昂贵的上下文切换,一次 mask 需要渲染三次才能在屏幕上显示,这已经是普通视图显示 3 倍耗时,若再加上下文环境切换,一次 mask 就是普通渲染的 30 倍以上耗时操作。
下面的情况或操作会引发离屏渲染:
官方对离屏渲染产生性能问题也进行了优化:iOS 9.0 之前 UIImageView 跟 UIButton 设置圆角都会触发离屏渲染;iOS 9.0 之后 UIButton 设置圆角会触发离屏渲染,而 UIImageView 设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。
在 APP 开发中圆角图片还是经常出现的。如果一个界面中只有少量圆角图片或许对性能没有非常大的影响,但是当圆角图片比较多的时候就会 APP 性能产生明显的影响。
我们设置圆角一般通过如下方式:
imageView.layer.cornerRadius = CGFloat(10);
imageView.layer.masksToBounds = YES;
这样处理的渲染机制是 GPU 在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作,也就是离屏渲染,这会带来额外的性能损耗,如果这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,出现掉帧现象。
使用贝塞尔曲线 UIBezierPath 和 Core Graphics 框架画出一个圆角。
UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
UIImage * image = [UIImage imageNamed:@"myImg"];
// 开始对 imageView 进行画图
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, [UIScreen mainScreen].scale);
// 使用贝塞尔曲线画出一个圆形图
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[image drawInRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
// 结束画图
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
视图上添加一个子 layer 到最上层,用于遮盖该视图及其子视图,设置 layer 的图片为刚好能够遮盖成所需圆角样子,并且图片颜色刚好是该视图父视图的背景颜色就达到想要的效果。
弊端:如果该父视图的颜色不是纯色,此时该方式就不适用了,同样,如果父视图的颜色会变化,那实现起来的代码也不那么优雅。
通过修改 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;
对于 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;
当使用阴影的视图形状发生变化时,即 shadowPath 并不会跟随 CALayer 的 bounds 属性进行变化,所以在 layer 的 bounds 产生变化以后需要手动更新 shadowPath 才能让其适配新的 bounds。具体推荐看这篇文章
我们还可以通过设置 shouldRasterize 属性值为 YES 来强制开启离屏渲染。其实就是光栅化(Rasterization)。既然离屏渲染这么不好,为什么我们还要强制开启呢?当一个图像混合了多个图层,每次移动时,每一帧都要重新合成这些图层,十分消耗性能。当我们开启光栅化后,会在首次产生一个位图缓存,当再次使用时候就会复用这个缓存。但是如果图层发生改变的时候就会重新产生位图缓存。所以这个功能一般不能用于 UITableViewCell 中,cell 的复用反而降低了性能。最好用于图层较多的静态内容的图形。而且产生的位图缓存的大小是有限制的,一般是 2.5 个屏幕尺寸。在 100ms 之内不使用这个缓存,缓存也会被删除。所以我们要根据使用场景而定。
对于离屏渲染的检测,苹果为我们提供了一个测试工具Core Animation。可以在Xcode->Open Develeper Tools->Instruments中找到,如下图:
Core Animation工具用来监测Core Animation性能,提供可见的FPS值,并且提供几个选项来测量渲染性能。如下图:
下面我们来说明每个选项的功能:
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方法,就是字节不对齐的时候。
Color Immediately:默认情况下 Core Animation 工具以每毫秒 10 次的频率更新图层调试颜色,如果勾选这个选项则移除 10ms 的延迟。对某些情况需要这样,但是有可能影响正常帧数的测试。
Color Misaligned Images:勾选此项,如果图片需要缩放则标记为黄色,如果没有像素对齐则标记为紫色。像素对齐我们已经在上面有所介绍。
Color Offscreen-Rendered Yellow:用来检测离屏渲染的,如果显示黄色,表示有离屏渲染。当然还要结合 Color Hits Green and Misses Red 来看,是否复用了缓存。
Color OpenGL Fast Path Blue:这个选项对那些使用 OpenGL 的图层才有用,像是 GLKView 或者 CAEAGLLayer,如果不显示蓝色则表示使用了 CPU 渲染,绘制在了屏幕外,显示蓝色表示正常。
Flash Updated Regions:当对图层重绘的时候回显示黄色,如果频繁发生则会影响性能。可以用增加缓存来增强性能。
使用tableView的复用机制?
作用:减少内存资源的消耗。?
注意:cell被重用时,它内部绘制的内容并不会被自动清除,因此你可能需要调用setNeedsDisplayInRect: 或 setNeedsDisplay 方法。?
提前预估高度?
提前计算并缓存好高度(布局),因为 heightForRowAtIndexPath: 是调用最频繁的方法。?
cell 内部有图片?
此时需要异步加载图片,防止卡顿(此时的 SDWebImage 的每个 cell 中都创建一个子线程吗?)但是内部开启的线程过多也会影响主线程的性能?
解决办法:尽量少用 addView 给 Cell 动态添加 View?
可以初始化时就添加,相对于一些固定的视图在初始化时就布局好,学会用 hidden 属性来控制是否显示。?
减少子视图的数目?
当 cell 上面的子视图数量过多时,会影响滑动性能,当子视图太多的时候,对适当的视图进行绘制。?
使用不透明视图?
不透明的视图可以极大地提高渲染的速度。因此如非必要,可以将 table cell 及其子视图的 opaque 属性设为 YES(默认值)。?
预渲染图像和离屏渲染?
你会发现即使做到了上述几点,当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是预渲染图像,在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕,详细做法可见《利用预渲染加速iOS设备的图像显示》。?
离屏渲染就是在 tableView 中展示多张需要切圆形的图片,此时不要使用 setCornerRadius 的方法,这样耗损性能,用 Core Graphics 绘制圆角,然后返回图片,在 SDWebImage 处理我的分类返回的图片,并进行缓存。?
UIImage:本地图片加载方式本地图片加载常用方法有两种:?
避免对象创建时过多消耗资源?
例如:日期处理,将保持日期对象全局唯一。
标签:ted 推荐 dcl 过程 适配 opacity 动态添加 www send
原文地址:https://www.cnblogs.com/dins/p/12364715.html