Android----屏幕UI显示系统基础

屏幕显示系统基础概念

帧--->视频由连续的静态图像连续快速切换播放形成,每一张静态图像称为视频的一帧。

利用人眼的“视觉暂留现象”只要连续图像播放大于24帧/秒,即感受到的就是连续的动态图像。

帧率(fps) ---> 每秒屏幕播放的帧数,帧率低于24人眼会感觉卡顿不流畅,越高越流畅
显示器垂直分辨率:显示器垂直方向上可显示的像素数目
显示器水平分辨率:显示器水平方向上可显示的像素数目
屏幕刷新率(HZ)-->又称为场频(垂直扫描频率)

最初的CRT(阴极射线管)技术时代的屏幕,屏幕刷新像素都是从左上角开始从左到右,从上到下逐行扫描;所以完成一次垂直扫描即可完成整屏幕的刷新故而场频也称屏幕刷新率.

行频--->水平扫描率,每秒在屏幕上扫描的行数
行频 = 场频 * 垂直分辨率 * K(一个常数)

为什么会有一个常数?答案也很简单,由于每次扫描到最后一个像素后,下次又要重定位到左上角,存在一个性能损耗,所以“行频”会比“场频*垂直分辨率”大,即K一般大于1。

典型现代屏幕显示系统角色分工:

构建视图内容: 遍历所有视图,将需要绘制的操作缓存下来,交给单独的Render线程使用GPU进行硬件加速渲染。(通常在主线程中使用CPU构建)

绘制阶段 : 调用OpenGL(即使用GPU)对构建好的视图进行绘制渲染,绘制的内容保存在Graphic Buffer 并交由 SurfaceFlinger 显示。(Android 5.0+ 使用Render Thread线程,专门负责 UI 渲染和动画显示。)

显示“卡顿”类型

Tearing --> "图像撕裂",屏幕呈现的图像由两帧以上构成,画面出现撕裂感(当前帧还未完全由屏幕更新到屏幕,即被下一帧的数据替换)
Jank --> 同一个帧在屏幕上连续出现2次即通常说的卡顿
Lag --> 屏幕延时,从用户输入到屏幕呈现效果之间存在延迟

Screen Tearing原因

屏幕显示图像本质是一个粗略的生产-消费者模型:CPU&GPU生产图像数据到帧Buffer,显卡从buffer获取数据显示到屏幕。


image.png

CPU & GPU生产帧数据时间是不定的,而屏幕刷新率基本由硬件(显卡)决定通常60Hz。显示控制器从Buffer获取帧数据更新到屏幕有一个时间间隔(CRT技术的屏幕都是从屏幕左上从左到右,从上到下依次刷新屏幕像素)。

在屏幕的一个刷新周期内,buffer数据被cpu/gpu更改,就出现了图像前部分是A帧,后半部分是B帧的“撕裂Tearing”.

由于帧buffer是一个竞争性资源,CPU/GPU 与 屏幕display会争抢buffer,不同步就会导致Tearing.

显示出现tearing的本质原因是,屏幕刷新率与CPU/GPU生成图像的帧率不同步, 加入同步lock即可解决;每次屏幕或者GPU只能有一方在操作buffer.

双缓冲+VSync同步机制

单buffer+同步虽然能解决tearing,但是效率太低,GPU和屏幕竞争同一块buffer大部分时间都在互相等待中浪费了资源。

于是双buffer机制适时出现(事实上单bufer机制只存在于30年前了):
backBuffer用于CPU/GPU后台绘制,frameBuffer则用于显示,当backBuffer准备就绪后,它们才进行交换。

双缓冲解决了效率问题,但是如何解决同步问题---> 何时交换backBuffer和frameBuffer?

当扫描完一个屏幕后,设备需要重新回到第一行以进入下一次的循环,此时有一段时间空隙,称为VerticalBlanking Interval(VBI)。这个时间点就是我们进行缓冲区交换的最佳时间,因为此时屏幕没有在刷新,也就避免了交换过程中出现 screentearing的状况。VSync(垂直同步)是VerticalSynchronization的简写,它利用VBI时期出现的vertical sync pulse来保证双缓冲在最佳时间点才进行交换。

双缓冲+Vsync工作模型:

without_vsync
draw_with_vsync.png

在这种模型下,只有当 VSync 信号产生时,交换back & frame buffer,CPU/GPU 才会开始绘制。当帧率大于刷新频率时,帧率就会被迫跟刷新频率保持同步,从而避免“tearing”现象。开启VSync的本质就是强制拉平我们的GPU每秒绘制的帧数和屏幕的刷新频率。

Google在Android的实践

为改善Android上的UI体验, Google在2012年的I/O大会上宣布了Project Butter,并在Android 4.1中正式搭载了实现机制。

从Android4.1版本开始,Android对显示系统进行了重构,引入了三个核心元素:VSYNC, Tripple Buffer和Choreographer。VSYNC是Vertical Synchronized的缩写,是一种定时中断;Tripple Buffer是显示数据的缓冲区;Choreographer起调度作用,将绘制工作统一到VSYNC的某个时间点上,使应用的绘制工作有序进行。

那么Triple buffer是干啥的?
看下上面的方案,在CPU/GUP帧率大于屏幕刷新的情况下皆大欢喜,万一帧率低于屏幕刷新率呢?使用A、B代表两块buffer看下图不同情况下的工作情况:


double_buffer_with_vsync.png
double_buffer_jank.png

若以60fps为屏幕刷新率,当CPU/GPU的处理时间超过16ms时,第一个VSync到来时,缓冲区B中的数据还没有准备好,于是只能继续显示之前A缓冲区中的内容。而B完成后,又因为缺乏VSync pulse信号,它只能等待下一个signal的来临。于是在这一过程中,有一大段时间是被浪费的。当下一个VSync出现时,CPU/GPU马上执行操作,此时它可操作的buffer是A,相应的显示屏对应的就是B。这时看起来就是正常的。只不过由于执行时间仍然超过16ms,导致下一次应该执行的缓冲区交换又被推迟了——如此循环反复,便出现了越来越多的“Jank”。


triple_buffer_vsync.png

Triple Buffering是MultipleBuffering的一种,指的是系统使用3个缓冲区用于显示工作。当第一次VSync发生后,CPU不用再等待了,它会使用第三个buffer C来进行下一帧数据的准备工作,有效地降低了系统显示错误的机率。

那是Buffer的数量越多越好?那肯定是不科学的,triple buffer是为了充分发挥CPU/GPU的工作效率,降低等待是充分利用流水线思路;各项任务的处理能力上限触顶使用多的buffer也只是增加额外的buffer管理调度任务。

Choreographer的作用

上面提到了硬件会产生Vsync信号作为触发标志,如果帧率是60ms那如何统一16.6ms内的任务等待Vsync到来才执行呢?

Choreographer就是用来统一协调任务的,动画、绘制和输入事件的任务会先进入Choreographer的队列;由Choreographer申请硬件的Vsync信号,收到Vsync信号后统一回调任务队列内的任务。

参考文章

屏幕成像原理以及FPS优化Tips
Android屏幕刷新显示机制
Android绘制优化----系统显示原理

你可能感兴趣的:(Android----屏幕UI显示系统基础)