vlambda博客
学习文章列表

vsync 信号生成源码分析

vsync 信号在 SurfaceFlinger 中生成,并且有硬件、软件两种生成方式

仅通过这种高度总结的描述,我们能得到多少信息呢?具体是如何生成?是优先选硬件生成还是软件生成还是同时存在?

下面就通过源码来一探究竟,既然在 SurfaceFlinger 中生成,那先来看 SurfaceFlinger 的 init 方法:

void SurfaceFlinger::init() {
    ...
    mHwc = new HWComposer(this);
    mHwc->setEventHandler(
      static_cast<HWComposer::EventHandler*>(this));
}

HWComposer 是 vsync 信号实际生成的类,SurfaceFlinger 通过 setEventHandler 向 HWComposer 注册了一个回调,使 SurfaceFlinger 能够接受由 HWComposer 生成的 vsync 信号,EventHandler 中的回调方法如下:

virtual void onVSyncReceived(int32_t disp, nsecs_t timestamp)

下面来看实际生成 vsync 的 HWComposer,其构造方法关键代码如下:

HWComposer::HWComposer(...){
    bool needVSyncThread = true;
    loadHwcModule(); //加载硬件
    if(mHwc){ //硬件加载成功
        mCBContext->procs.vsync = &hook_vsync; //注册硬件源回调
        mHwc->registerProcs(mHwc, &mCBContext->procs);
        needVSyncThread = false//不使用软件方式
    }
    if(needVSyncThread){
        //如果硬件不支持,则用软件模拟
        mVSyncThread = new VSyncThread(*this);
    }
}

逻辑十分简单,首先去加载硬件,优先使用硬件生成方式,如果硬件不支持,则用软件模拟。

硬件源生成回调 hook_vsync 方法如下:

void HWComposer::hook_vsync(const struct hwc_procs* procs, 
        int disp, int64_t timestamp) {
    cb_context* ctx = reinterpret_cast<cb_context*>(
            const_cast<hwc_procs_t*>(procs));
    ctx->hwc->vsync(disp, timestamp); //调用 vsync 方法
}

void HWComposer::vsync(int disp, int64_t timestamp) {
    mEventHandler.onVSyncReceived(disp, timestamp); //回调出去
}

至此 HWComposer 中的 vsync 硬件生成方式流程完毕,最后通过 mEventHandler 回调交由 SurfaceFlinger 处理。

再来看软件模拟生成方式,其通过 VSyncThread 类实现,VSyncThread 继承了 Thread 类,自然也继承了 Thread 类的 threadLoop 方法。

如果 threadLoop 方法返回 true,那当工作线程启动后,基类 Thread 会循环的调用 threadLoop 方法。

而软件模拟生成的 vsync 信号就是在 threadLoop 方法中生成的,代码如下:

bool HWComposer::VSyncThread::threadLoop() {
    const nsecs_t period = mRefreshPeriod; // vsync 周期
    const nsecs_t now = systemTime(CLOCK_MONOTONIC); //当前时间
    nsecs_t next_vsync = mNextFakeVSync; //本次要生成的 vsync 信号时间
    nsecs_t sleep = next_vsync - now;
    if (sleep < 0) {
        // 错过了,重新计算本次要生成的 vsync 信号时间
        sleep = (period - ((now - next_vsync) % period));
        next_vsync = now + sleep;
    }
    mNextFakeVSync = next_vsync + period; // 下一个 vsync 信号时间
    struct timespec spec;
    spec.tv_sec  = next_vsync / 1000000000;
    spec.tv_nsec = next_vsync % 1000000000;
    int err;
    do {
        //休眠到 next_vsync 即本次 vsync 信号时间 
        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
    } while (err<0 && errno == EINTR);
    if (err == 0) {
        mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
    }
    return true//让系统循环调用此方法
}

如何在指定的时间点产生信号呢?如代码所示,在每次循环时采用休眠的方式主动等待至 vsync 时间点。

第一次执行时, next_vsync、mNextFakeVSync 都为 0,所以 sleep < 0 进而会重新计算 next_vsync 为 now + period,也就是基于当前时间再等一个 vsync 周期。

后续每次循环也会重新修正 vsync 信号的时间,避免回调 onVSyncReceived 方法或其他操作耗时导致 vsync 信号时间变得"越来越晚"。

最后总结一下:vsync 信号在 SurfaceFlinger 进程的 HWComposer 类中生成,优先使用硬件生成方式,如果硬件不支持则使用软件方式模拟生成。不管是硬件生成还是软件生成,最后统一通过相同的回调交由 SurfaceFlinger 处理。







Android 

看,  年薪百万