Android GUI系统之SurfaceFlinger(05)VSync机制

该系列文章总纲链接: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点:

  1. 在多数正常情况下还是使用二级缓冲机制,三级缓冲只是在需要的时候才使用。
  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合成界面 流程解读

  1. 当APP需要更新界面时,发送请求Vsync信号请求给EventThread(APP) ,EventThread(APP) 再向DispSyncThread线程发送请求,DispSyncThread线程收到请求后,延迟offset1后将VSync-APP发送给EventThread(APP) 并唤醒, EventThread(APP) 在收到Vsync-APP后唤醒APP,APP开始产生新界面(dequeueBuffer操作)。
  2. 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个线程的创建 分析到此结束。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值