Android性能分析:卡顿丢帧基础CPU/GPU原理(4)
Android显示系统中,包括 CPU、GPU、display 三部分。CPU 计算数据然后交给 GPU 渲染,渲染好后放到 buffer 中存起来,然后 display 将 buffer 数据显示到屏幕上。
渲染过程CPU和GPU分工
渲染的2个核心:CPU 和 GPU。
CPU:Measure、Layout、等。
GPU:Rasterization(栅格化)。
在GPU中有一块缓冲区叫做 Frame Buffer,这个帧缓冲区可以认为是存储像素值的二位数组。GPU 除了帧缓冲区用以交给手机屏幕进行绘制外,还有一个缓冲区 Back Buffer ,这个用以交给应用的,让CPU往里面填充数据。
GPU会定期交换 Back Buffer 和 Frame Buffer ,也就是对Back Buffer中的数据进行栅格化后将其转到 Frame Buffer,然后交给屏幕进行显示,同时让原先的Frame Buffer 变成 Back Buffer 让程序处理。GPU会获取图形数据进行渲染,然后硬件负责把渲染后的内容呈现到屏幕上,两者不停的进行协作。
但有时候显示后会出现2种问题:
tearing
撕裂。当 CPU/GPU 将数据准备好存入 buffer 中,但 display 还没来得及显示,这时 CPU/GPU 把下一帧的数据往 buffer 中写,还没写完的时候,display 开始读取 buffer 来显示(也就是绘图速度大于显示速度)。这时就会出现显示的上半部分是下一帧的数据,下半部分为上一帧的数据,就是所说的撕裂。刷新频率和帧率并不是总能够保持相同的节奏,如果发生帧率与刷新频率不一致的情况,就会容易出现Tearing的现象(画面上下两部分显示内容发生断裂,来自不同的两帧数据发生重叠):
显示结果:
jank
丢帧。绘图速度过慢的时候,同一帧在屏幕上至少出现2次。
撕裂的原因是 display 还没来得及读, buffer 就被重写了,那么就可以准备2个 buffer 即双缓冲。back buffer 用于 CPU/GPU 后台绘制,front buffer 用于显示。back buffer 准备好后才可以交换,这样就可以避免撕裂问题。但是此时屏幕还没有完整显示上一帧的内容时是不能交换的。那么只有等屏幕处理完成当前帧才能进行交换操作。当扫描完一屏后,会回到第一行进入下一次的循环,中间会有一段空隙(VBI),这个空隙为缓冲区交换的最佳时间。VSYNC 就是利用这个空隙出现的垂直刷新脉冲来保证双缓冲的最佳时间点。
双缓冲下,没有 VSYNC 的丢帧情况:
Display 为显示屏, VSYNC 仅指双缓冲的交换:
Step1:Display 显示第0帧,此时 CPU/GPU 渲染第1帧画面,并且在 Display 显示下一帧前完成。
Step2:Display 正常渲染第一帧。
Step3:出于某种原因,如 CPU 资源被占用,系统没有及时处理第2帧数据,当 Display 显示下一帧时,由于数据没处理完,所以依然显示第1帧,即发生Jank。
是因为第2帧没有在显示前及时处理,导致屏幕多显示第1帧一次,导致后面的帧都延时了。
引入VSYNC,CPU-GPU理想绘制情况
当且仅当 VSYNC 出现时,CPU 就会立即处理下一帧数据,大大降低 Jank 概率。而且杜绝了 CPU/GPU 不停的绘制,导致帧生成速度高于屏幕刷新速度,生成的帧不能显示而被丢弃,这样导致的丢帧情况。
引入 VSYNC 后,绘制速度和屏幕刷新速度保持一致。早期经典的Android 设备屏幕刷新频率为 60HZ,那么 CPU/GPU 渲染的时间需要在 16ms 内完成。当 CPU/GPU 的 FPS 高于 60 HZ 显示效果会很完美,像这样:
当VSync 信号产生时,先完成Back Buffer 到 Frame Buffer的复制操作(交换内存地址),然后通知 CPU/GPU 绘制下一帧图像。也只有VSync 信号发生时,才绘制下一帧。将绘制工作都统一到VSYNC时间点上,是Choreographer的作用,在Choreographer指挥下,CPU/GPU绘制工作井井有条。但如果设备硬件性能较差,无法达到也会丢帧:
超时了,刷新频率>帧率,此时刷新屏幕,发出VSYNC 信号,由于CPU/GPU的渲染操作还没有完成,就不把Back Buffer的数据复制到 Frame Buffer,此时就从Frame Buffer去取旧数据,这样在两个刷新周期里,显示的是同一帧数据。
双缓冲区的丢帧
当第1个 VSYNC 到来时 GPU 还在处理数据,这时 B 缓冲区被占用了,那么就无法进行交换,屏幕依然显示 A 缓冲区数据。下一个信号到来时,此时 GPU 已经处理完了,那么就可以交换缓冲区,此时屏幕显示 B 缓冲区,CPU/GPU 开始操作 A。下一个信号到来时,A 被占用,那么屏幕依然显示 B 的数据。这种情况就是因为 GPU/CPU 无法在 16ms 内处理完数据而导致缓冲区交换延迟。
第一次的Jank看起来是没有办法的,除非升级硬件配置来加快FPS。设备不能升级硬件,无法改变 CPU/GPU 渲染时间,那么第一次 Jank 是无法避免的。重点关注 CPU 第一次和第二次执行中间浪费的时间。当第1次信号到来时,由于 GPU 占用了 B,导致屏幕会一直占用 A。两个缓冲区都被占用了,即使此时 CPU 是空闲的,它也没有办法处理下一帧的数据。当 CPU/GPU 绘制一帧的时间超过 16 ms 时,产生 Jank。产生 Jank 的那一帧的显示期间,GPU/CPU 在闲置的。造成CPU/GPU无事可做的假象是因为当前已经没有可用的buffer了。如果有第三个 Buffer 能让 CPU/GPU 在这个时候继续工作,那就完全可以避免第二个 Jank 的发生。
增加一个 buffer,三缓冲
当第一个信号到来时,A、B 都被占用,此时 CPU 开始使用 C 缓冲区来处理下一帧数据。之前第二次发生的 Jank 就避免了。双缓冲和三重缓冲都会有 lag(延时)问题,C 缓冲区延时了16ms才显示。
获取丢帧率的adb shell命令:
adb shell dumpsys SurfaceFlinger packagename
卡顿丢帧除了CPU/GPU层面,另外,也需要特别注意整机低内存情况。kswapd0 是一个内核工作线程,内存不足时会被唤醒,做内存回收工作。 当内存频繁在低水位的时候,kswapd0 会被频繁唤醒,占用 cpu ,造成卡顿和耗电。通常kswapd0占用大核,而且是满频在跑,耗电、温升,如果此时有前台应用主线程跑到与kswapd0相同的核上,很大可能会出现 cpu 竞争,导致调度不到而丢帧卡顿。HeapTaskDaemon 通常也会在低内存时候跑的很高。
adb shell dumpsys meminfo
导出内存状况。
adb shell free
adb shell cat /proc/meminfo
当系统处于低内存的情况时候 , MemFree 和 MemAvailable 的值都很小。
MemTotal:内存总数
可以简单理解为总内存。
MemFree:空闲内存数
表示系统尚未使用的内存。MemUsed=MemTotal-MemFree,是已被用掉的内存。
Cached:缓存区内存数
当你读写文件的时候,Linux内核为了提高读写性能与速度,会将文件在内存中进行缓存,也就是Cache Memory(缓存内存)。这会导致系统中程序频繁读写文件后,可用物理内存会很少。
MemAvailable:可用内存数
应用程序可用内存数。系统中有些内存虽然已被使用但是可以回收的,比如cache/buffer,有一部分可以回收,所以MemFree不能代表全部可用的内存,这部分可回收的内存加上MemFree才是系统可用的内存,即:MemAvailable ≈ MemFree + Buffers + Cached,是一个估计值。
Android性能:SurfaceFlinger与BufferQueue(3)-CSDN博客文章浏览阅读490次,点赞16次,收藏14次。t 时长,20s,20秒的trace文件。CPU返回后,会直接将GraphicBuffer提交给SurfaceFlinger,告诉SurfaceFlinger进行合成,但是这个时候GPU可能并未完成之前的图像渲染,这时候就牵扯到一个同步,Android中,用的是Fence机制,SurfaceFlinger合成前会查询Fence,如果GPU渲染没有结束,则等待GPU渲染结束,GPU结束后,会通知SurfaceFlinger进行合成,SF合成后,提交显示,最终完成图像的渲染显示。就是 Buffer。https://blog.csdn.net/zhangphil/article/details/138631517Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制(2)-CSDN博客文章浏览阅读749次,点赞12次,收藏17次。t 时长,20s,20秒的trace文件。CPU返回后,会直接将GraphicBuffer提交给SurfaceFlinger,告诉SurfaceFlinger进行合成,但是这个时候GPU可能并未完成之前的图像渲染,这时候就牵扯到一个同步,Android中,用的是Fence机制,SurfaceFlinger合成前会查询Fence,如果GPU渲染没有结束,则等待GPU渲染结束,GPU结束后,会通知SurfaceFlinger进行合成,SF合成后,提交显示,最终完成图像的渲染显示。就是 Buffer。
https://blog.csdn.net/zhangphil/article/details/138628225卡顿丢帧分析adb shell命令-CSDN博客文章浏览阅读359次,点赞5次,收藏3次。Android ADB调试真机设备Android ADB(Andorid Debug Bridge),是Android开发中有用的测试和调试工具。使用Android ADB调试设备,直接在Windows的dos命令窗口输入命名adb即可,如图:为什么执行adb命令后是这样?Android ADB(Andorid Debug Bridge)调试真机设备_adb在线执行器_zhangphil的博客-CSDN博客。-t 时长,20s,20秒的trace文件。
https://blog.csdn.net/zhangphil/article/details/137919380Android adb shell命令捕获systemtrace_android 抓trace-CSDN博客文章浏览阅读1.7k次,点赞2次,收藏5次。Android ADB调试真机设备Android ADB(Andorid Debug Bridge),是Android开发中有用的测试和调试工具。使用Android ADB调试设备,直接在Windows的dos命令窗口输入命名adb即可,如图:为什么执行adb命令后是这样?Android ADB(Andorid Debug Bridge)调试真机设备_adb在线执行器_zhangphil的博客-CSDN博客。-t 时长,20s,20秒的trace文件。-o 保存文件路径。_android 抓trace
https://blog.csdn.net/zhangphil/article/details/131249820