iOS离屏渲染

计算机软件开发 2024-9-8 06:10:38 86 0 来自 中国
界面渲染

UIView继承自UIResponder,可以处理处罚体系通报过来的事故,如:UIApplication、UIViewController、UIView,以及全部从UIView派生出来的UIKit类。每个UIView内部都有一个CALayer提供内容的绘制和体现,而且作为内部RootLayer的署理视图。
下图为CALayer的结构图:

1.png RunLoop有一个60fps的回调,即每16.7ms绘制一次屏幕,以是view的绘制必须在这个时间内完成,view内容的绘制是CPU的工作,然后把绘制的内容交给GPU渲染,包罗多个View的拼接(Compositing)、纹理的渲染(Texture)等等,末了体现在屏幕上。但是,如果无法是16.7ms内完成绘制,就会出现丢帧的标题,一样寻常情况下,如果帧率包管在30fps以上,界面卡顿效果不显着,那么就须要在33.4ms内完成View的绘制,而低于这个帧率,就会产生卡顿的效果,影响体验。
渲染的过程如下:

UIView的layer层有一个content,指向一块缓存,即backing store
UIView绘制时,会调用drawRect方法,通过context将数据写入backing store
在backing store写完后,通过render server交给GPU去渲染,将backing store中的bitmap数据体现在屏幕上

2.png ios离屏渲染

On-Screen Rendering:当前屏幕渲染,指的是 GPU 的渲染利用是在当前用于体现的屏幕缓冲区中举行
Off-Screen Rendering:离屏渲染,分为 CPU 离屏渲染和 GPU 离屏渲染两种情势。GPU 离屏渲问鼎的是 GPU 在当前屏幕缓冲区外新开发一个缓冲区举行渲染利用
为什么会利用离屏渲染:

当利用圆角,阴影,遮罩的时间,图层属性的混淆体被指定为在未预合成之前不能直接在屏幕中绘制,以是就须要屏幕外渲染被唤起。
GPU 离屏渲染的代价是很大的
离屏渲染之以是会特别斲丧性能,是因为要创建一个屏幕外的缓冲区,然后从当屏缓冲区切换到屏幕外的缓冲区,然后再完成渲染;此中,创建缓冲区和切换上下文最斲丧性能,而绘制着实不是性能斲丧的重要缘故起因。
上下文之间的切换这个过程的斲丧会比力昂贵,涉及到 OpenGL的 pipeline 跟 barrier,而且 offscreen-render 在每一帧都会涉及到,因此处理处罚不妥肯定会对性能产生肯定的影响。别的由于离屏渲染会增长 GPU 的工作量,大概会导致 CPU+GPU 的处理处罚时间超出 16.7ms,导致掉帧卡顿。
离屏渲染的场景和优化

圆角优化

方法一:
一样寻常情况下我们会用这个方法去设置圆角:
iv.layer.cornerRadius = 30;iv.layer.masksToBounds = YES;利用cornerRadius举行切圆角,在iOS9之前会产生离屏渲染,比力斲丧性能,而之后体系做了优化,则不会产生离屏渲染,但是利用最简单
方法二:
利用mask设置圆角,利用的是UIBezierPath和CAShapeLayer来完成
CAShapeLayer *mask = [[CAShapeLayer alloc] init];mask1.opacity = 0.3;mask1.path = [UIBezierPath bezierPathWithOvalInRect:iv.bounds].CGPath;iv.layer.mask = mask;方法三:
利用CoreGraphics画一个圆形上下文,然后把图片绘制上去,得到一个圆形的图片
- (UIImage *)drawCircleImageUIImage*)image{    CGFloat side = MIN(image.size.width, image.size.height);        UIGraphicsBeginImageContextWithOptions(CGSizeMake(side, side), false, [UIScreen mainScreen].scale);    CGContextAddPath(UIGraphicsGetCurrentContext(), [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, side, side)].CGPath);    CGContextClip(UIGraphicsGetCurrentContext());        CGFloat marginX = -(image.size.width - side) * 0.5;    CGFloat marginY = -(image.size.height - side) * 0.5;    [image drawInRect:CGRectMake(marginX, marginY, image.size.width, image.size.height)];        CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();        return newImage;}三种方法内里,方法三是性能最好的。
固然了,直接让美工画一个圆角的图服从是最高的。
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;组不透明

开启CALayer的 allowsGroupOpacity 属性后,子 layer 在视觉上的透明度的上限是其父 layer 的 opacity (对应UIView的 alpha ),而且从 iOS 7 以后默认全局开启了这个功能,如许做是为了让子视图与其容器视图保持同样的透明度。
以是,可以关闭 allowsGroupOpacity 属性,按产物需求本身控制layer透明度。
关闭抗锯齿

allowsEdgeAntialiasing属性为YES(默认为NO)
离屏渲染的检测

Instruments的Core Animation工具中有几个和离屏渲染干系的查抄选项:
Color Offscreen-Rendered Yellow
开启后会把那些须要离屏渲染的图层高亮成黄色,这就意味着黄色图层大概存在性能标题。
Color Hits Green and Misses Red
如果shouldRasterize被设置成YES,对应的渲染效果会被缓存,如果图层是绿色,就体现这些缓存被复用;如果是赤色就体现缓存会被重复创建,这就体现该处存在性能标题了。
iOS版本上的优化

iOS 9.0 之前UIimageView跟UIButton设置圆角都会触发离屏渲染
iOS 9.0 之后UIButton设置圆角会触发离屏渲染,而UIImageView里png图片设置圆角不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。
善用离屏渲染

只管离屏渲染开销很大,但是当我们无法克制它的时间,可以想办法把性能影响降到最低。优化思路也很简单:既然已经花了不少精力把图片裁出了圆角,如果我能把效果缓存下来,那么下一帧渲染就可以复用这个结果,不须要再重新画一遍了。
CALayer为这个方案提供了对应的解法:shouldRasterize。一旦被设置为true,Render Server就会欺凌把layer的渲染效果(包罗其子layer,以及圆角、阴影、group opacity等等)保存在一块内存中,如许一来在下一帧仍旧可以被复用,而不会再次触发离屏渲染。有几个须要注意的点:
shouldRasterize的主旨在于低落性能丧失,但总是至少会触发一次离屏渲染。如果你的layer原来并不复杂,也没有圆角阴影等等,打开这个开关反而会增长一次不须要的离屏渲染
离屏渲染缓存有空间上限,最多不凌驾屏幕总像素的2.5倍巨细
一旦缓存凌驾100ms没有被利用,会主动被丢弃
layer的内容(包罗子layer)必须是静态的,因为一旦发生变化(如resize,动画),之前辛劳处理处罚得到的缓存就失效了。如果这件事频繁发生,我们就又回到了“每一帧都须要离屏渲染”的景象,而这正是开发者须要努力克制的。针对这种情况,Xcode提供了“Color Hits Green and Misses Red”的选项,资助我们查察缓存的利用是否符合预期
着实除了办理多次离屏渲染的开销,shouldRasterize在另一个场景中也可以利用:如果layer的子结构非常复杂,渲染一次所需时间较长,同样可以打开这个开关,把layer绘制到一块缓存,然后在接下来复用这个效果,如许就不须要每次都重新绘制整个layer树了
什么时间须要CPU渲染

绝大多数情况下,得益于GPU针对图形处理处罚的优化,我们都会倾向于让GPU来完成渲染任务,而给CPU留出充足时间处理处罚各种各样复杂的App逻辑。为此Core Animation做了大量的工作,尽量把渲染工作转换成适当GPU处理处罚的情势(也就是所谓的硬件加速,如layer composition,设置backgroundColor等等)。
但是对于一些情况,如笔墨(CoreText利用CoreGraphics渲染)和图片(ImageIO)渲染,由于GPU并不善于做这些工作,不得不先由CPU来处理处罚好以后,再把效果作为texture传给GPU。除此以外,偶然候也会碰到GPU着实忙不外来的情况,而CPU相对空闲(GPU瓶颈),这时可以让CPU分担一部门工作,进步整体服从。
一个范例的例子是,我们经常会利用CoreGraphics给图片加上圆角(将图片中圆角以外的部门渲染成透明)。整个过程全部是由CPU完成的。如许一来既然我们已经得到了想要的效果,就不须要再别的给图片容器设置cornerRadius。另一个利益是,我们可以机动地控制裁剪和缓存的机遇,奇妙避开CPU和GPU最繁忙的时段,到达平滑性能颠簸的目标。
但要注意的是:
渲染不是CPU的刚强,调用CoreGraphics会斲丧其相称一部门计算时间,而且我们也不乐意因此壅闭用户利用,因此一样寻常来说CPU渲染都在配景线程完成(这也是AsyncDisplayKit的重要头脑),然后再回到主线程上,把渲染效果传回CoreAnimation。如许一来,多线程间数据同步会增长肯定的复杂度
同样因为CPU渲染速率不敷快,因此只适当渲染静态的元素,如笔墨、图片(想象一下没有硬件加速的视频解码,性能惨不忍睹)
作为渲染效果的bitmap数据量较大(情势上一样寻常为解码后的UIImage),斲丧内存较多,以是应该在利用完实时释放,并在须要的时间重新天生,否则很轻易导致OOM
如果你选择利用CPU来做渲染,那么就没有来由再触发GPU的离屏渲染了,否则会同时存在两块内容类似的内存,而且CPU和GPU都会比力辛劳
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 18:26, Processed in 0.182646 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表