发布网友 发布时间:2022-09-05 02:20
共1个回答
热心网友 时间:2024-07-09 02:25
主要苹果在底层是如何渲染画面到屏幕上的过程,以此来进一步分析屏幕卡顿的原理,最后进行屏幕卡顿的解决。
说明:
大体上分为三部分处理,第一部分是CPU的计算,第二部分是GPU的渲染,第三部分是屏幕显示
CPU的计算苹果底层主要是通过核心动画来实现,包含两部分,第一是对CALayer的计算,第二是调用OpenGL ES/Metal库进行调用GPU
CoreGraphics
UIKit:
CoreAnimation:
在苹果官方的描述中,Render、Compose,and animate visual elements,因此不能被名字欺骗了,其实CoreAnimationg中的动画只是一部分,它其实是一个复合引擎,主要的职责包括 渲染、构建和动画实现。
我们平常用的CALayer来自于CoreAnimation框架,CALayer是屏幕上用户可见内容的基础,主要是由于可视化内容到最后都会被分解成独立的图层(layer),被存储在图层树中。
核心动画所处的位置
说明:
CoreImage
OpenGL ES/Metal
渲染API,详情可以查看博客 音视频开发:OpenGL + OpenGL ES + Metal 系列文章汇总
2018年之后苹果底层已经从OpenGL ES切换到Metal渲染了。
说明:
扩展:
UIView和CALayer的区别:
UIView
CALayer:
二者关系:
GPU的渲染流程不再追溯,当前使用Metal来进行渲染,因此Metal的渲染流程可以看 十三、Metal - 初探 。这里进行简单说明
说明:
屏幕显示的操作是从帧缓存区中拿到帧数据,并且显示到显示屏上
说明:
说明:
通过引入双缓存机制来解决读取效率低的问题
如果只有一个帧缓冲区,帧缓冲区的读取和刷新都有较大的效率问题。因此必须等上一帧读取完之后才能才能将下一帧写入到缓存区中。效率较低。
因此苹果引入双缓冲机制,也就是两个帧缓冲区,GPU会预先渲染好一帧放入到帧缓冲区,视频控制区进行读取,在读取的过程中,就可以将新渲染好的一帧放到另一个帧缓冲区,这样就可以一直不停的进行刷新帧缓冲区,而当视频控制器读取完成,GPU会主动的把指针指向第二个缓冲区,这样读取和刷新帧缓冲区的效率都提高了
通过垂直同步机制来较大程度解决画面撕裂问题
上面的双缓冲机制有一个很大的问题,就是GPU会一直不停的将渲染好的一帧数据放到帧缓冲区中,并且在提交完成后,会主动的把指针指向第二个缓冲区,这样如果此时视频控制器还未读取完成,比如读取到一半,下一半就变成了下一帧的数据,就会造成画面撕裂现象
解决:
VSync信号到来后,才开始CPU->GPU->缓冲区,而此时视频控制器会把上一次的帧数据读取到,可以说是读取和更新是同时的,但是读取和更新都依赖于VSync
帧缓冲区的更新和读取时同时进行,而且都收到VSync信号的控制,读取上一个帧数据时,更新下一个帧数据
上面我们已经知道了视图数据渲染到屏幕上所需要经历的过程,最后视频控制器是按照双缓存机制+垂直同步信号来获取帧数据的。因此我们按照这个认知来分析卡顿原理
上面所说的双缓冲机制+垂直同步机制,需要VSync到来时,更新帧数据,下一个VSync到来时,会读取这次更新的帧数据,而如果下一个VSync到来时,因为CPU或GPU的原因,帧数据还没有更新到帧缓冲区,就会继续读取上一个帧数据,在一个VSync时间内显示了两次帧数据,就会造成卡顿现象
思路一实现方法:用 CADisplayLinker 来计数
CADisplayLink可以以屏幕刷新的频率调用指定selector,iOS系统中正常的屏幕刷新率为60次/秒,只要在这个方法里面统计每秒这个方法执行的次数,通过次数/时间就可以得出当前屏幕的刷新率了。
思路二实现方法:通过子线程监测主线程的RunLoop,判断两个状态RunLoop的状态区域之间的耗时是否达到一定阈值。
开启子线程,实时计算这两个状态区域之间的耗时是否到达某个阀值,便能揪出这些性能杀手,假定连续6次超时50ms认为卡顿(当然也包含了单次超时300ms)
总的来说有两种,第一是避免使用不必要的操作,第二是必需的操作尽量放到后台执行
避免不必要的操作
必要的操作放到后台执行
GPU的认识:
可进⾏绘图运算⼯作的专⽤微处理器,是连接计算机和显示终端的纽带。
他所做的事情概括起来:1、接收提交的纹理(Textture)和顶点描述。2、应用变换(transform)3、混合并渲染 4、输出到屏幕上
纹理的渲染
视图的混合
图像绘制