https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/
https://objccn.io/issue-3-1/
http://www.imlifengfeng.com/blog/?p=593
https://robots.thoughtbot.com/designing-for-ios-graphics-performance
Snip20180524_2.png
离屏渲染
- On-Screen Rendering (当前屏幕渲染)
指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。 - Off-Screen Rendering (离屏渲染)
指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。 - Core Graphics 的绘制 API 的确会触发离屏渲染,但不是那种 GPU 的离屏渲染。使用 Core Graphics 绘制 API 是在 CPU 上执行,触发的是 CPU 版本的离屏渲染。
CPU的offscreen-render
- drawRect (如果没有自定义绘制的任务就不要在子类中写一个空的drawRect方法,因为只要实现了该方法,就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以 contentsScale的值,造成资源浪费)
- 文本(任何类型,包括UILabels,CATextLayers,核心文本等)。
- 使用Core Graphics
上面的两种情况使用的就是CPU离屏渲染,首先分配一块内存,然后进行渲染操作生成一份bitmap位图,整个渲染过程会在你的应用中同步的进行,接着再将位图打包发送到iOS里一个单独的进程--render server,理想情况下,render server将内容交给GPU直接显示到屏幕上。
GPU的offscreen-render
- 设置cornerRadius, masks, shadows,edge antialiasing等
- 设置layer.shouldRasterize = YES
与硬件(GPU)加速绘图相比,上述CPU处理的任何类型的绘图都很慢,但它们不一定会减慢您的应用程序速度,因为它们不需要每帧都发生。例如,第一次在视图上绘制投影时速度很慢,但在绘制之后它会被缓存,并且只有在视图更改大小或形状时才会重绘。
为了获得良好的性能,诀窍是避免使用软件绘制来改变每一帧的视图。例如,如果您需要动画矢量形状,使用CAShapeLayer或OpenGL比使用drawRect和Core Graphics获得更好的性能。但是如果你画一次形状然后不需要改变它,它就没有太大的区别。
三种观点
- GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作,这种离屏渲染损耗了性能,因为该过程中开辟空间和上下文切换等损耗性能
- 导致性能低下的原因并不是因为离屏渲染,因为offscreen 和 onscreen 几乎一样快。重要的是,在渲染的时候是否使用了硬件加速。
- 切换上下文不是直接导致性能损耗的原因,多做的 Rendering 工作才是。iOS 里讲 CALayer 的一些 properties 会触发「离屏渲染」导致 app 性能下降,其实更多的是指 GPU 要为此多做很多额外的处理步骤(pass),这些多出来的步骤必然消耗更多的设备性能。
个人认为具体情况具体处理,当遇到性能瓶颈的时候需要看是CPU还是GPU造成的性能瓶颈,并进行相应优化
具体的优化方法
- 圆角
使用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];
或
view.layer.backgroundColor = [UIColor blueColor].CGColor;
view.layer.cornerRadius = 10;
- 阴影,使用UIBezierPath绘制
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;
- 先显示UI页面,再异步合并数据到本地
- 尽量避免使用透明视图,因为使用透明视图的时候GPU需要花费很大工作量做合成计算。图片也尽量不要用有透明通道的图片
- 当shouldRasterize设成true时,layer被渲染成一个bitmap,并缓存起来,等下次使用时不会再重新去渲染了。实现圆角本身就是在做颜色混合(blending),如果每次页面出来时都blending,消耗太大,这时shouldRasterize = yes,下次就只是简单的从渲染引擎的cache里读取那张bitmap,节约系统资源。
如果你最后设置了 shouldRasterize 为 YES,那也要记住设置 rasterizationScale 为 contentsScale
然而,这是一个权衡。第一,这可能会使事情变得更慢。创建额外的屏幕外缓冲区是 GPU 需要多做的一步操作,特殊情况下这个位图可能再也不需要被复用,这便是一个无用功了。然而,可以被复用的位图,GPU 也有可能将它卸载了。所以你需要计算 GPU 的利用率和帧的速率来判断这个位图是否有用。 - 文本渲染优化
屏幕上能看到的所有文本内容控件,包括 UIWebView,在底层都是通过 CoreText 排版、绘制为 Bitmap 显示的。常见的文本控件 (UILabel、UITextView 等),其排版和绘制都是在主线程进行的,当显示大量文本时,CPU 的压力会非常大。对此解决方案只有一个,那就是自定义文本控件,用 TextKit 或最底层的 CoreText 对文本异步绘制。尽管这实现起来非常麻烦,但其带来的优势也非常大,CoreText 对象创建好后,能直接获取文本的宽高等信息,避免了多次计算(调整 UILabel 大小时算一遍、UILabel 绘制时内部再算一遍);CoreText 对象占用内存较少,可以缓存下来以备稍后多次渲染。 - 尽量减少试图层次,必要时可以把多个视图预先渲染为一张图片来显示
- 其他优化
https://heisenbean.me/2018/01/iOS-Optimize-Part1/
FlexBox布局
https://juejin.im/post/5a33a6926fb9a045104a8d3c
网友评论