凡仁心之发,必一鼓作气,尽吾力之所能为。稍有转念,则疑心生,私心亦生。疑心生则计较多,而出纳吝矣;私心生则好恶偏,而轻重乖矣。------------------------------------曾国藩
想到好的事就立刻去做,别犹豫!
首先,我们提出我们的疑问,什么是GPU?什么是离屏渲染?GPU与CPU渲染有什么不同等等?下面就让我们带着这些疑问来一一解答。
GPU 概念:
GPU英文全称Graphic Processing Unit,中文翻译为“图形处理器”。GPU是相对于CPU的一个概念,GPU是显示卡的“心脏”,也就相当于CPU在电脑中的作用,它决定了该显卡的档次和大部分性能,同时也是2D显示卡和3D显示卡的区别依据。
CPU与GPU的不同:
CPU擅长逻辑控制,串行的运算。和通用类型数据运算不同,GPU擅长的是大规模并发计算,这也正是密码破解等所需要的。所以GPU除了图像处理,也越来越多的参与到计算当中来。
OpenGL中,GPU屏幕渲染有两种方式:
1、On-Screen Rendering (当前屏幕渲染)
指的是GPU的渲染操作是在当前用户现实的屏幕缓冲区中进行。
当前屏幕渲染不需要额外创建新的缓存,也不需要开启新的上下文,相对于离屏渲染性能更好。但是受当前屏幕渲染的局限因素限制(只有自身上下文、屏幕缓存有限等),当前屏幕渲染有些情况下的渲染解决不了的,就使用到离屏渲染。
2、Off-Screen Rendering (离屏渲染)
指的是在GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。
离屏渲染的代价很高,想要进行离屏渲染,首选要创建一个新的缓冲区,屏幕渲染会有一个上下文环境的一个概念,离屏渲染的整个过程需要切换上下文环境,先从当前屏幕切换到离屏,等结束后,又要将上下文环境切换回来。这也是为什么会消耗性能的原因了。
特殊的“离屏渲染”:CPU渲染
如果我们重写了drawRect方法,并且使用任何Core Graphics的技术进行了绘制操作,就涉及到了CPU渲染。整个渲染过程由CPU在App内同步地完成,渲染得到的bitmap(位图)最后再交由GPU用于显示。
离屏渲染场景
1、为图层设置遮罩(layer.mask)
2、将图层的layer.masksToBounds / view.clipsToBounds属性设置为true
3、将图层layer.allowsGroupOpacity属性设置为YES和layer.opacity小于1.0
4、为图层设置阴影(layer.shadow )。
5、为图层设置layer.shouldRasterize=true
6、具有layer.cornerRadius,layer.edgeAntialiasingMask, * layer.allowsEdgeAntialiasing的图层
7、文本(任何种类,包括UILabel,CATextLayer,Core Text等)。
8、使用CGContext在drawRect :方法中绘制大部分情况下会导致离屏渲染,甚至仅仅是一个空的实现。
优化方案:
iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。
圆角优化
方案一:使用贝塞尔曲线UIBezierPath和Core Graphics框架画出一个圆角
要点:使用Core Graphics框架与贝塞尔曲线可以实现不在view的drawRect(继承于CoreGraphics走的是CPU,消耗的性能较大)方法中画出一些想要的图形。
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"1"];
//开始对imageView进行画图
UIGraphicsBeginImageContextWithOptions(imageView.bounds.size, NO, 1.0);
//使用贝塞尔曲线画出一个圆形图
[[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.frame.size.width] addClip];
[imageView drawRect:imageView.bounds];
imageView.image = UIGraphicsGetImageFromCurrentImageContext();
//结束画图
UIGraphicsEndImageContext();
[self.view addSubview:imageView];
方案二:使用CAShapeLayer和UIBezierPath设置圆角
要点:CAShapeLayer动画渲染直接提交到手机的GPU当中,相较于view的drawRect方法使用CPU渲染而言,其效率极高,能大大优化内存使用情况。
UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
imageView.image = [UIImage imageNamed:@"1"];
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];
shadow优化
方案一:如果图层是个简单的几何图形或者圆角图形,我们可以通过设置shadowPath来优化性能,能大幅提高性能。
方案二:还可以通过设置shouldRasterize属性值为YES来强制开启离屏渲染。其实就是光栅化(Rasterization)。当一个图像混合了多个图层,每次移动时,每一帧都要重新合成这些图层,十分消耗性能。当我们开启光栅化后,会在首次产生一个位图缓存,当再次使用时候就会复用这个缓存。但是如果图层发生改变的时候就会重新产生位图缓存。
注意:一般不用于位图缓存,复用的cell反而降低性能。
知识点来源:
https://www.zhihu.com/question/19903344
https://www.zhihu.com/question/19903344
网友评论