Android重学系列SurfaceFlinger的概述
前⾔
本⽂将会作为开启SurfaceFlinger的系列第⼀篇⽂章。然⽽SurfaceFlinger⼏乎贯通了整个Android领域中所有的知识。从HAL硬件抽象层到Framework层,从CPU绘制到OpenGL等硬件绘制。
为了让整个系列的书写更有逻辑性。这⼀次我将⼀反常态,先把整个架构的设计思想概述写出来,作为后⾯的系列⽂章的指导。本⽂之后都会将SurfaceFlinger称为SF。
正⽂
SF的渲染第⼀定律:
SF是整个Android系统渲染的核⼼进程。所有应⽤的渲染逻辑最终都会来到SF中进⾏处理,最终会把处理后的图像数据交给CPU或者GPU进⾏绘制。
姑且让我们先把这句话当作Android渲染系统的第⼀定律。SF在整个Android系统中,并⾮担当渲染的⾓⾊,⽽是作为图元抛射机⼀样,把所有应⽤进程传递过来的图元数据加⼯处理后,交给CPU和GPU做真正的绘制。
SF的渲染第⼆定律:
在每⼀个应⽤中都以Surface作为⼀个图元传递单元,向SF这个服务端传递图元数据。
这是Android渲染体系的第⼆定律。把这两个规律组合起来就是如下⼀个简单⽰意图。
SF交互设计图.png
SF的渲染第三定律:
SF是以⽣产者以及消费者为核⼼设计思想,把每⼀个应⽤进程作为⽣产者⽣产图元保存到SF的图元队列中,SF则作为消费者依照⼀定的规则把⽣产者存放到SF中的队列⼀⼀处理。
⽤图表⽰就如下:
图元消费核⼼原理.png
SF体系渲染的第四定律:
为了能够跨进程的传输⼤容量的图元数据,使⽤了匿名共享内存内存作为⼯具把图元数据都输送到SF中处理。
众所周知,我们需要从应⽤进程跨进程把图元数据传输到SF进程中处理,就需要跨进程通信,能考虑的如socket这些由于本⾝效率以及数据拷贝了2份(从物理内存页层⾯上来看),确实不是很好的选择。⼀个本⾝拷贝⼤量的数据就是⼀个疑问。那么就需要那些⼀次拷贝的进程间通信⽅式,⾸先能想到的当然是Binder,然⽽Binder进程间通信,特别是应⽤的通信数据总量只有1M不到的⼤⼩加上应⽤其他通信,势必会出现不⾜的问题。
为了解决这个问题,Android使⽤共享内存,使⽤的是匿名共享内存(Ashmem)。匿名共享内存也是⼀种拷贝⼀次的进程间通信⽅式,其核⼼⽐起binder的复杂的mmap更加接近Linux的共享内存的概念。制作android软件流程
Ashmem.png
SF体系渲染的第五定律:
SF底层有⼀个时间钟在不断的循环,或从硬件中断发出,或从软件模拟发出计时唤起,每隔⼀段时间都会获取SF中的图元队列通过CPU/GPU绘制在屏幕。
第五定律的诞⽣实际上很符合Android系统的设计情况,除了需要Android应⽤有办法通知SF需要渲染的模式,当然需要SF⾃⼰不断的把图元绘制到屏幕的⾏为的⾃⼰回调⾃⼰的⾏为,SF⾃⼰不断的绘制在SF中的图元数据。
SF Vsync.png
其中EventThread扮演⼀个极其重要的⾓⾊,在SF中设计⼤致如下:
EventThread.png
Vsync的介绍
这⾥⾯出现了⼀个新的名次VSync,其实这就是我们玩游戏经常说的垂直同步信号。我以前⽤渣电脑玩游戏的时候,经常掉帧数卡的不⾏,之后关闭了垂直信号后感觉好了点,让我有⼀段时间以为这是个不好的东西。
这⾥就先介绍⼀下Android曾经迭代为ui体验更好上的努⼒,黄油计划。黄油计划故名思议就是为了让系统的ui表现如黄油表⾯⼀样顺滑。为此诞⽣了两个重要的概念Vsync以及Triple Buffer,即垂直信号和三重缓冲。
双缓冲的概念⼤家应该都熟悉,在OpenGL我已经解释过了,双缓冲就是渲染第⼀帧的同时已经在绘制第⼆帧的内容,等到第⼆帧绘制完毕后就显⽰出来。这么做的好处很明显,如果⼀帧画完,才开始画下⼀帧,势必有⼀个计算的过程导致ui交互迟缓。
双缓冲.png
通过这种⽅式显⽰前⼀帧的时候提前绘制好下⼀帧图元,放在背后等待时机交换,这样就能从感官上流畅不少。
这么做理想⼗分显⽰,但是怎么到⼀个合适的时机进⾏交换前后两帧这是⼀个问题?如果有⼈在想那就按照屏幕刷新频率来,⼀般按照通⽤屏幕刷新60fps也就是约16ms刷新⼀次即可。
理想是很丰满,但是现实很⾻⼲,这么做好像没有问题,我们深⼊考虑⼀下,其实这个过程中有两个变量,⼀个是绘图速度,⼀个是显⽰速度。就算是绘图速度中也有分CPU和GPU的绘制速度。
这⾥就沿⽤⼀下当年google在宣传黄油计划时候的⽰意图。让我们先看看没有缓冲正常运作的⽰意图:
draw_vsync.png
最好的情况就是上图,在显⽰第0帧的时候,CPU/GPU合成绘制完成第1帧在16ms内,当vsync信号来了,就把第1帧交换到显⽰屏显⽰。
vsync是什么?玩游戏的时候经常看到垂直同步就是它。它的作⽤是通过屏幕硬件中断来告诉系统应该什么时候刷新屏幕。通过这样的⽅式,⼤致上16ms的发送⼀次中断让系统刷新。
但是很可能出现下⾯这种情况,CPU因为繁忙来不及,显⽰完第⼀帧的时候,还没空渲染第⼆帧,就算SF接受到了Vsync的信号,也只能拿出已经渲染好的第⼀帧显⽰在屏幕上。这样就重复显⽰了第⼀帧,Google开发团队称这种为jank。
jank.png
能看到显⽰第⼀帧因为第⼆帧没准备好,只能重复显⽰第⼀帧了。
再来看看带着多重缓冲的的⼯作原理流程:
double_buffer.png
能看到此时就不是简单的第⼀第⼆帧,⽽是分为A缓冲,B缓冲。能看到在正常情况下,先显⽰A缓冲
的内容,同时准备B缓冲,当⼀切正常的时候,B缓冲应该在下⼀个vsync来之前准备好,⼀旦vsync到来则显⽰B缓冲,A缓冲回到后台继续绘制。
那么这种⽅式⼀旦遇到jank会是怎么⼀个情况呢?
double_jank.png
如果是双缓冲好像没有问题,但是⼀旦出现jank了之后,之后显⽰屏就会不断的出现jank。如果缓冲A在显⽰,⽽B准备的时间超过16ms,就会

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。