该系列文章总纲链接:Android GUI系统之SurfaceFlinger 系列文章目录
本章关键点总结 & 说明:
本章节思维导图如上。主要讲述了黄油计划和Vsync机制 涉及的 5个关键线程。
1 Android的 黄油计划
1.1 Vsync机制
Vsync(Vertical Synchronization,垂直同步)是一种在PC上很早就广泛使用的技术,可以理解为是一种定时中断。而在Android 4.1(JB)中已经开始引入VSync机制来同步渲染,让App和SurfaceFlinger可以按硬件产生的VSync节奏进行工作。 Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,屏幕的刷新过程是每一行从左到右(行刷新,水平刷新,Horizontal Scanning),从上到下(屏幕刷新,垂直刷新,Vertical Scanning)。当整个屏幕刷新完毕,即一个垂直刷新周期完成,会有短暂的空白期,此时发出 VSync 信号。
当没有VSync机制时,以下图为例,第一个16ms之内一切正常。第二个16ms之内因为CPU最后阶段才计算出了数据,导致GPU也是在第二段的末尾时间才进行了绘制,整个动作延后到了第三段内,从而影响了下一个画面的绘制。这时会出现Jank(闪烁,可以理解为卡顿或者停顿)。此时CPU和GPU可能被其他操作占用了,这就是卡顿出现的原因
当使用Vsync同步时,收到Vsync命令的时候,CPU和GPU才会必须立刻进行刷新/显示的动作。CPU/GPU接收vsync信号提前准备下一帧要显示的内容,所以能够及时准备好每一帧的数据,保证画面的流畅。如下所示:
1.2 triple buffering机制
当系统资源紧张性能降低时,导致GPU在处理某帧数据时太耗时,在Vsync信号到来时,buffer B的数据还没准备好,此时不得不显示buffer A的数据,这样导致后面CPU/GPU没有新的buffer准备数据,如下图,如果GPU忙不过来了,没有能及时完成,又会导致Jank,如下所示:
空白时间无事可做,后面Jank频出,因此 在VSync的基础上 又引入了 triple buffering,如下所示:
当出现上面所述情况后,新增一个buffer C 可以减少CPU和GPU在Vsync同步间的空白间隙,此时CPU/GPU能够利用buffer C 继续工作,后面buffer A 和 buffer B 依次处理下一帧数据。这样仅是产生了一个Jank,可以忽略不计,以后的流程就顺畅了。
这里总结下,要注意以下2点:
- 在多数正常情况下还是使用二级缓冲机制,三级缓冲只是在需要的时候才使用。
- 三级缓冲并不是 解决了jank 的问题,而是出现jank时 不影响后续的显示。
1.3 vsync虚拟化
虽然vsync使得CPU/GPU/Display同步了,但App UI和SurfaceFlinger的工作显然是一个流水线的模型。即对于一帧内容,先等App UI画完了,SurfaceFlinger再出场对其进行合并渲染后放入framebuffer,最后整到屏幕上。而现有的VSync模型是让大家一起开始干活,这样对于同一帧内容,第一个VSync信号时App UI的数据开始准备,第二个VSync信号时SurfaceFlinger工作,第三个VSync信号时用户看到Display内容,这样就两个VSync period(每个16ms)过去了,影响用户体验。解决这个问题的思路是:SurfaceFlinger在App UI准备好数据后及时开工做合成。Android 4.4(KitKat)中引入了VSync的虚拟化,即把硬件的VSync信号先同步到一个本地VSync模型中,再从中一分为二,引出两条VSync时间与之有固定偏移的线程。示意图如下:
即 App开始工作的时候是Vsync+offset1,surface工作的时刻在Vsync+offset1+offset2。而这个offset1 和 offset2 都是可调的。
2 VSync机制框架详解
这里首先对SF中VSync的代码进行分析,之后绘制出整体的框架图,如下所示:
产生Vsync的源头主要是硬件(HardwareComposer,一般)或者软件(VsyncThread);由线程(DispSyncThread)来虚拟化Vsync信号,将Vsync分为Vsync-APP和Vsync-Surface,Vsync-App和Vsync-Surface是按需起作用的。
2.1 APP需要更新界面 和 SF合成界面 流程解读
- 当APP需要更新界面时,发送请求Vsync信号请求给EventThread(APP) ,EventThread(APP) 再向DispSyncThread线程发送请求,DispSyncThread线程收到请求后,延迟offset1后将VSync-APP发送给EventThread(APP) 并唤醒, EventThread(APP) 在收到Vsync-APP后唤醒APP,APP开始产生新界面(dequeueBuffer操作)。
- APP将产生的界面提交Buffer时会调用queueBuffer的操作,最后会通知SF合成界面。SF需要合成界面的时候,发送请求Vsync信号请求给EventThread(SF),EventThread(SF) 再向DispSyncThread线程发送请求,DispSyncThread线程收到请求后,延迟offset2后将VSync-SF发送给EventThread(SF) 并唤醒, EventThread(SF)在收到Vsync-SF后唤醒SF,SF开始合成新界面。
2.2 Vsync框架引入的五个关键线程说明
这里通过源码分析 来 关注 5个关键线程是如何创建的,从SurfaceFlinger进程创建(main_surafceflinger)开始直到找到5个线程的创建位置,代码如下:
int main(int, char**) {
// When SF is launched in its own process, limit the number of
// binder threads to 4.
ProcessState::self()->setThreadPoolMaxThreadCount(4);
// start the thread pool
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
// instantiate surfaceflinger
//关键点1
sp<SurfaceFlinger> flinger = new SurfaceFlinger();
//...
// initialize before clients can connect
//关键点2
flinger->init();
// publish surface flinger
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
// run in this thread
//关键点3
flinger->run();
return 0;
}
如上所示,重点关注 标识 关键点1 2 3 部分的代码。
2.2.1 SurfaceFlinger创建
下面的代码 省略了部分与我们此次分析无关的成员变量的初始化相关操作,如下:
SurfaceFlinger::SurfaceFlinger()
: BnSurfaceComposer(),
//...
mDaltonize(false),
mHasColorMatrix(false)
{
ALOGI("SurfaceFlinger is starting");
// debugging stuff...
char value[PROPERTY_VALUE_MAX];
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
mGpuToCpuSupported = !atoi(value);
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
property_get("debug.sf.ddms", value, "0");
mDebugDDMS = atoi(value);
if (mDebugDDMS) {
if (!startDdmConnection()) {
// start failed, and DDMS debugging not enabled
mDebugDDMS = 0;
}
}
ALOGI_IF(mDebugRegion, "showupdates enabled");
ALOGI_IF(mDebugDDMS, "DDMS debugging enabled");
}
可以看到 主要是初始化变量,以及 调试和属性 相关的代码,5个关键线程的创建并未体现。但在SurfaceFlinger.h 中 留意下一个关键的成员变量:
DispSync mPrimaryDispSync;
是DispSync类型的,DispSync的构造器代码如下:
DispSync::DispSync() :
mRefreshSkipCount(0),
mThread(new DispSyncThread()) {
mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);
reset();
beginResync();
if (kTraceDetailedInfo) {
if (!kIgnorePresentFences) {
addEventListener(0, new ZeroPhaseTracer());
}
}
}
这里创建了一个线程DispSyncThread(虚拟化Vsync信号产生Vsync-APP和Vsync-SF的线程),并在创建该变量时就执行了线程的run操作。
2.2.2 flinger->init()
下面是经过整理后的代码,主要关注剩下的关键代码,如下:
void SurfaceFlinger::init() {
//...
// Initialize the H/W composer object. There may or may not be an
// actual hardware composer underneath.
//关键点1
mHwc = new HWComposer(this,
*static_cast<HWComposer::EventHandler *>(this));
//...
// start the EventThread
//关键点2
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);
//...
}
@1 这里首先 继续分析HWComposer的实现,代码如下:
HWComposer::HWComposer(
const sp<SurfaceFlinger>& flinger,
EventHandler& handler)
: mFlinger(flinger),
//...
mDebugForceFakeVSync(false)
{
//...
if (needVSyncThread) {
// we don't have VSYNC support, we need to fake it
mVSyncThread = new VSyncThread(*this);
}
}
此时如果没有硬件支持,则needVSyncThread为真,此时 会创建线程VSyncThread(软件实现VSync的线程)。
@2 继续分析EventThread的创建
对于 EventThread(APP)线程和EventThread(SF)线程,对比下两者的创建:
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc);
他们的创建仅仅是参数的不同,一个是最后的字符串参数,另一个就是偏移量,即前面一直说的offset1 和 offset2。这里创建了两个EventThread线程(Vsync-APP和Vsync-SF感兴趣的EventThread(APP)线程和EventThread(SF)线程 )。
2.2.3 flinger->run()
最后来看下 SF线程的实现,run的代码如下:
void SurfaceFlinger::run() {
do {
waitForEvent();
} while (true);
}
这里WaitForEvent实现如下:
void SurfaceFlinger::waitForEvent() {
mEventQueue.waitMessage();
}
SF线程 主要是 启动后开始休眠,一直等待来自APP和EventThread(SF)的消息。
至此,5个线程的创建 分析到此结束。