iOS 离屏渲染

离屏渲染在iOS中代价高昂,涉及创建新缓冲区和上下文切换。CPU的绘制操作、某些属性如光栅化、遮罩、阴影等会触发离屏渲染。应尽量减少离屏渲染,使用On-Screen Rendering,或通过Core Graphics优化。开启CALayer.shouldRasterize可缓存内容,但要考虑内存消耗。最佳方案是使用Core Graphics绘制圆角UIImage。
摘要由CSDN通过智能技术生成

CPU 和 GPU渲染

OpenGL中,GPU屏幕渲染有以下两种方式:

  1. On-Screen Rendering 
    意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。

  2. Off-Screen Rendering 
    意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。 
    按照这样的说法,如果将不在GPU的当前屏幕缓冲区中进行的渲染都称为离屏渲染,那么就还有另一种特殊的“离屏渲染”方式:CPU渲染。如果我们重写了drawRect方法,并且使用任何Core Graphics的技术进行了绘制操作,就涉及到了CPU渲染。整个渲染过程由CPU在App内同步地完成,渲染得到的bitmap最后再交由GPU用于显示。

相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体现在两个方面:

  1. 创建新缓冲区 
    要想进行离屏渲染,首先要创建一个新的缓冲区。

  2. 上下文切换 
    离屏渲染的整个过程,需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。

设置了以下属性时,都会触发离屏绘制:

  1. shouldRasterize(光栅化)

  2. masks(遮罩)

  3. shadows(阴影)

  4. edge antialiasing(抗锯齿)

  5. group opacity(不透明) 
    需要注意的是,如果shouldRasterize被设置成YES,在触发离屏绘制的同时,会将光栅化后的内容缓存起来,如果对应的layer及其sublayers没有发生改变,在下一帧的时候可以直接复用。这将在很大程度上提升渲染性能。 
    而其它属性如果是开启的,就不会有缓存,离屏绘制会在每一帧都发生。

在开发时需要根据实际情况来选择最优的实现方式,尽量使用On-Screen Rendering。简单的Off-Screen Rendering可以考虑使用Core Graphics让CPU来渲染。

减少离屏渲染

CALayer 的 border、圆角、阴影、遮罩(mask),CAShapeLayer 的矢量图形显示,通常会触发离屏渲染(offscreen rendering).与之相对的是当前屏幕渲染(On-Screen Rendering),指的是渲染操作是用于在当前屏幕显示的缓冲区进行.离屏渲染的概念来自于 OpenGL 中 GPU 渲染屏幕的两种方式: On-Screen Rendering(当前屏幕渲染)Off-Screen Rendering(离屏渲染)。 指的是:在当前屏幕以外新开辟一个缓冲区进行渲染操作。离屏渲染造成卡顿的原因是:离屏渲染需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上又需要将上下文环境从离屏切换到当前屏幕,而上下文环境的切换是一项高开销的动作。
通常我们会一个 view 设置阴影会使用 shadowoffset

1
2
3
4
UIView *diamondView = [[UIView alloc] init];
diamondView.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
diamondView.layer.shadowRadius = 5.0f;
diamondView.layer.shadowOpacity = 0.5;

但是这种方式会触发离屏渲染造成不必要的开销,那么既要实现阴影图层,又要减少离屏渲染,提高性能的话.有什么更好的方式么?

1
2
3
UIView *diamondView = [[UIView alloc] init];
diamondView.layer.shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(diamondView.bounds.origin.x + 1, diamondView.bounds.origin.y + 1, diamondView.bounds.size.width, diamondView.bounds.size.height)].CGPath;
imageView.layer.shadowOpacity = 0.5;

但是 shadowPath 只适用于给规则的矩形生成阴影路径.如果我们迫不得已要使用 shadowoffset,可以尝试开启 CALayer.shouldRasterize 属性, 图像将会被缓存起来并绘制到实际图层的 contents 和子图层.将原本在 GPU 中的一些工作让 CPU 来做,让两者达到一个平衡.但是这并不是有一个全优解,因为光栅化原始图像需要时间,而且会消耗额外的内存.所以一定要避免在内容不断变动的图层上使用,不然缓存的优势将荡然无存.
最完美的解决方案是使用 Core Graphics 绘制圆角 UIImage 设置给 UIImageView 然后插入到 UIView 中去。




•纹理的渲染,所有的 Bitmap,包括图片、文本、栅格化的内容,最终都要由内存提交到显存,绑定为GPUTexture。不论是提交到显存的过程,还是GPU 调整和渲染Texture的过程,都要消耗不少GPU 资源。当图片过大,超过GPU 的最大纹理尺寸时,图片需要先由CPU 进行预处理,这对CPU 和 GPU 都会带来额外的资源消耗


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值