开启离屏渲染
1.大小:屏幕像素的2.5倍!
首先了解CPU和GPU在计算机中的作用
1、CPU(central processing Unit)中央处理器。是一块超大规模的集成电路,是一台计算机的运算核心和控制核心。它的功能主要是解释计算机指令以及计算机软件的中的数据。
2、GPU(Graphics Processing Unit)图形处理器,是一种专门在个人电脑、工作站、游戏机和一些移动设备(如平板电脑、智能手机等)上图像运算工作的微处理器。是连接计算机和显示终端的纽带。
撕裂
完美情况:在60FPS内每扫描一张图就不断显示不断刷新图像。
掉帧就是因为帧缓存区(旧的数据)显示到了屏幕中而帧缓存区已经拿到了下一帧数据的图像,从而显示到屏幕上出现撕裂的现象。
图像撕裂是我们肉眼看到的一张图片上下发生错位,即断层,其本质是拿到图像后,GPU进⾏渲染->帧缓存区⾥ ->视频控制器->读取帧缓存区信息(位图) -> 数模转化(数字信号处->模 拟型号) ->(逐⾏扫描)显示,当第一帧图像扫描到某个位置时,GPU拿到新的数据并存到帧缓冲区,这个时候视频控制器从帧缓冲区扫描的是新拿到的一帧的图像,最后就形成了在我们肉眼看到的断层现象,即我们看到的一张图片其本质是两张图片组合而来,究其原因就是视频控制器显示速度大于了GPU处理图形的速度
解决撕裂
为了解决撕裂,苹果爸爸引入了: 垂直同步Vsync + 双缓存区 DoubleBuffering
(1)垂直同步Vsync:帧缓存区加锁 防⽌出现撕裂情况
(2)双缓存区 DoubleBuffering :就是GPU开辟AB两个帧缓冲区
执行流程就是当A帧缓冲区拿到第一帧数据,给A缓冲区加上一把锁,屏幕控制器从A拿到数据并逐行扫描完成,A帧缓冲区解锁,并且屏幕控制器指向B帧缓冲区,B帧缓冲区加锁并逐行扫描显示,在屏幕控制器扫描B帧缓冲区的时候,A帧缓冲区拿到GPU传过来的新一帧数据,以此类推,解决撕裂问题
掉帧
掉帧即重复渲染同一帧数据。
为了减少掉帧(注意不是解决,掉帧问题只能尽量的减少,而不是解决,三级缓冲区也有可能出现掉帧),引入三级缓存区,三级缓冲区是为了充分利用CPU/GPU的空余时间,开辟ABC三个帧缓冲区,A显示屏幕, B也渲染好,C再从GPU拿取渲染数据,当屏幕缓冲区和帧缓冲区都弄好了,然后视频控制器再指向帧缓冲区的另外一个,再显示,这样交替,达到减少掉帧的情况,这样做就比二级缓冲区多了一个确认的操作
屏幕卡顿的原因:
1、CPU/GPU渲染流水线耗时过长出现掉帧
2、垂直同步Vsync + 双缓存区DoubleBuffering 以掉帧作为代价解决屏幕撕裂的问题
3、三缓存区:合理使用CPU/GPU,减少掉帧次数。
显示器显示的内容来自帧缓冲区,
视频控制器:控制刷新部件,帧缓冲区与显示器的对应关系进行显示
帧缓存区:颜色值(帧缓冲,显存)
内存:连续的计算机存储器,主要存储刷新图像信息;
位图:60 * 60 = 3600 * 4(RGBA)= 1440;
图形渲染显示流程
什么是离屏渲染
如果要在显示屏上显示内容,我们至少需要一块与屏幕像素数据量一样大的frame buffer,作为像素数据存储区域,而这也是GPU存储渲染结果的地方。如果有时因为面临一些限制,无法把渲染结果直接写入frame buffer,而是先暂存在另外的内存区域,之后再写入frame buffer,那么这个过程被称之为离屏渲染。
离屏渲染(Offscreen Rendering)GPU在当前屏幕缓冲区以外开辟了一个缓冲区进行操作。
app进行额外的渲染和合并->offscreen Buffer组合->frame Buffer->屏幕,离屏渲染一个额外的存储空间(offscreen Buffer)渲染完成写入frame Buffer,离屏渲染的大小限制为屏幕像素点的2.5倍。作者:安静的泼猴链接:https://www.jianshu.com/p/67265c9ef2c4来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
离屏渲染的性能
不开启离屏渲染的逻辑为帧缓冲区(双缓存区)直接显示到屏幕,画完即可丢弃从而节省空间。
离屏渲染需要在屏幕外开辟内存空间,提前使用CPU渲染复杂的视图,保证视频控制器能够及时地从缓存区读到新的渲染结果。它在GPU面临性能瓶颈时,将压力转移一部分给比较空闲的CPU,然而CPU的渲染能力远没有GPU高效,有点杀鸡出牛刀的意思。同时这也是一种以空间换取时间的策略。离屏渲染的整个过程,需要多次切换上下文环境;等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的
离屏渲染什么时候被触发
使用了mask的layer(mask.layer)
需要进行裁剪的layer(layer.maskToBounds/View.clipsToBounds)
设置了组透明度为YES,并且透明度不为1的layer(layer.allowsGroupOpacity/layer.opacity)
添加了投影的layer(layer.shadow)
采用了光栅化的layer(layer.shouldRasterize)
绘制了文字的layer(UILable、CATextLayer、CoreText等)
光栅化使用建议
如果layer不能被复用,则没有必要打开光栅化。
如果layer不是静态的,需要被频繁修改,比如处于动画之中,那么开启离屏渲染反而影响效率了。
离屏渲染缓存内容有时间限制,缓存内容100ms内容如果没有被使用,那么就会被丢弃;无法进行复用了。
离屏渲染缓存空间有限,超过2.5倍屏幕像素大小的话,也会失效且无法进行复用了。
离屏渲染解决办法
UI绘制圆角图片,前台进行设置
对于 contents 无内容或者内容的背景透明(无涉及到圆角以外的区域)的layer,直接设置layer的 backgroundColor 和 cornerRadius 属性来绘制圆角。
使用混合图层,在layer上方叠加相应mask形状的半透明layer
sublayer.contents=(id)[UIImage imageNamed:@"xxx"].CGImage;
[view.layer addSublayer:sublayer];
-(UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius corners:(UIRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor borderLineJoin:(CGLineJoin)borderLineJoin此方法为YY_image处理圆角的方法,你可以去下载YY_image查看源码=
着色器提供数据的三种方式
attributes属性:用来传递顶点数据,投影矩阵,模型矩阵,只能传递到顶点着色器。
Uniform值:统一的批次类,Uniform最常见的应用是在顶点渲染中设置变换矩阵,顶点着色器和片段着色器都可以有Uniform变量
Texture纹理数据:纹理坐标给到顶点着色器,桥接给片元着色器。也可以直接给到片元着色器