[Android禅修之路] 解读Vsync(二)

2,124 阅读23分钟

[Android禅修之路] 解读Vsync(二)

Android禅修之路

Android禅修之路 解读Vsync(一)

Android禅修之路 解读Vsync(二)

上篇 解读Vsync(一) 说明了 Vsync 信号在 SurfaceFlinger 中的传递过程,最后留下了一个问题,就是 SurfaceFlinger 中的事件是怎么来的。这个事件是怎么从硬件信号转变成 SurfaceFlinger 中的事件的?

本篇围绕着这个问题展开,大致如下

  • Vsync 信号源 DispSync [节四和节五]
  • Vsync 信号的控制器 EventControlThread [节六合节七]
  • 硬件 Vsync 到 软件 Vsync 的转换 [节八]

四 DispSync

前面我们说过了,Vsync 事件是通过 DispSync 产生的。在 SurfaceFlinger 的初始化的过程中,会创建一个 DispSync,当时对它的描述是这个 DispSync 就是 Vsync 的信号源,其实准确来说,它是一個模擬 HW-VSYNC 的軟件模型。

首先说为什么需要 DispSync。

我们知道 Vsync 信号是通过硬件产生的。当我们的应用和系统接收到 Vsync 信号之后应该怎么做?如果马上开始显示相关的刷新工作,那么必然会造成 CPU 和 GPU 工作的拥挤。理想的情况是,系统的刷新工作执行一会儿,我们的应用的刷新工作执行一会儿,两者交互的进行,这样才能让系统执行的效率最大化。这种情况下就需要 DispSync 了。如下,就是 Google 官方提供的 DispSync 示意图,它的作用就是将 Vsync 信号转换为 SF-VSYNC 和 APP-VSYNC。

dispsync.png

4.1 DispSync 的创建

DispSync 是在在 Scheduler 的构造函数中创建的, 代码如下

[frameworks/native/services/surfaceflinger/Scheduler/Scheduler.cpp]
auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
mPrimaryDispSync = std::move(primaryDispSync);

4.2 DispSync::init

在 DispSync 创建时,调用了它的初始化函数 init。

[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
    mIgnorePresentFences = !hasSyncFramework;
    mPresentTimeOffset = dispSyncPresentTimeOffset;

    // 开启了一个名为 DispSync 的线程,另外的两个参数是线程的优先级,
    // 分别是 PRIORITY_URGENT_DISPLAY(-8)和PRIORITY_MORE_FAVORABLE(-1),
    // 所以它的线程优先级和 SF 是一样的-9。我们知道优先级的数字越低,代表优先级越高,
    // 所以这里的 DispSync 线程优先级是非常高的
    mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE);

    struct sched_param param = {0};
    param.sched_priority = 2;
    // 设置 SCHED_FIFO 尽量提高 DispSyncThread 的优先级,以减少误差
    if (sched_setscheduler(mThread->getTid(), SCHED_FIFO, &param) != 0) {
        ALOGE("Couldn't set SCHED_FIFO for DispSyncThread");
    }

    reset();
    beginResync();

    if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
        mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
        addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0);
    }
}

DispSync 的 init 函数中调用了 mThread 的 run,这个 mThread 是一个 DispSyncThread。

DispSyncThread 是 DispSync 的一個内部类,它是 Thread 的子类,主要功能就是模拟 HW-VSYNC 信号。 这个线程大多数时候都是处于阻塞状态。

它会算出下一个 Vsync 信号的时间节点,然后周期性的醒来并通知 Vsync 的监听器 Listener。

五 DispSyncThread

DispSyncThread 继承自 Thread,定义如下

[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
class DispSyncThread : public Thread

// 构造函数
DispSyncThread(const char* name, bool showTraceDetailedInfo)
      : mName(name),
        mStop(false),
        mModelLocked(false),
        mPeriod(0),
        mPhase(0),
        mReferenceTime(0),
        mWakeupLatency(0),
        mFrameNumber(0),
        mTraceDetailedInfo(showTraceDetailedInfo) {}

注意,这里的 mPeriod 和 mPhase 在构造函数中都是0。

  • mPeriod 代表 Vsync 信号的周期,也就是两个 Vsync 信号之间的时间差,如果每秒60帧那它就是 16.6667 ms
  • mPhase 代表 Vsync 信号周期的初始偏移,因为我们需要错开 sf 和 app 的 Vsync 信号,所以需要加上对应的初始化偏移

DispSyncThread 它是 Thread 的子类,它的工作函数就是 threadLoop,这个函数就是产生 Vsync 信号的函数

5.1 threadLoop 的第一部分

threadLoop 这个函数可以分为两个部分,因为 DispSyncThread 在创建的时候 mPhase 还是0,所以这里我们先看第一部分

virtual bool threadLoop() {
    status_t err;
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    // 死循环
    while (true) {
        std::vector<CallbackInvocation> callbackInvocations;
        nsecs_t targetTime = 0;

        { // 自动锁
            Mutex::Autolock lock(mMutex);
            ++mFrameNumber;
            if (mStop) {
                return false;
            }
            // 如果 mPeriod 为0,则会在这里处于等待状态
            // 在 DispSyncThread 的构造函数中,它被初始化为0,也就是说一开始它是处于等待状态
            if (mPeriod == 0) {
                err = mCond.wait(mMutex);
                if (err != NO_ERROR) {
                    return false;
                }
                continue;
            }
          ...

第一部分很简单,我们看到一个死循环,然后在这个循环中会判断 mPeriod 的值,如果它为0就会陷入等待,也就是说线程在等待其他地方来修改这个 mPeriod 的值,来触发这个线程继续执行的逻辑。

关于 mPeriod 的修改,其实是由 SurfaceFlinger 触发的。我们知道 SurfaceFlinger 是 Android 系统中非常重要的一个系统服务,所以在系统启动的时候,会跟着启动 SurfaceFlinger 这个系统服务,关于 SurfaceFlinger 的启动,可以查看 SurfaceFlinger的启动

在 Android 启动系统服务的时候,经常会调用一个函数用来绑定 Binder 的生命周期,用来处理 Binder 挂掉的情况,这个函数就是 binderDied

5.2 SurfaceFlinger

5.2.1 SurfaceFlinger::binderDied

[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
{
    // 初始化显示设备
    initializeDisplays();
    // 启动开机动画
    startBootAnim();
}

在 binderDied 中做了两件事,一个是显示设备的初始化,一个是显示开机动画。开机动画不是本篇的重点,这里我们看它是如何初始化显示设备的

5.2.2 SurfaceFlinger::initializeDisplays

[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]
void SurfaceFlinger::initializeDisplays() {
    // 异步处理,调用 onInitializeDisplays
    postMessageAsync(
            new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { onInitializeDisplays(); }));
}

5.2.3 SurfaceFlinger::onInitializeDisplays

onInitializeDisplays 就是初始化显示设备的参数。

[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]

void SurfaceFlinger::onInitializeDisplays() {
    const auto display = getDefaultDisplayDeviceLocked();
    if (!display) return;
    const sp<IBinder> token = display->getDisplayToken().promote();

    // 重设屏幕参数
    Vector<ComposerState> state;
    Vector<DisplayState> displays;
    DisplayState d;
    d.what = DisplayState::eDisplayProjectionChanged |
             DisplayState::eLayerStackChanged;
    d.token = token;
    d.layerStack = 0;
    d.orientation = DisplayState::eOrientationDefault;
    d.frame.makeInvalid();
    d.viewport.makeInvalid();
    d.width = 0;
    d.height = 0;
    displays.add(d);
    setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {});

    // 设置电源模式,见[5.2.5]
    setPowerModeInternal(display, HWC_POWER_MODE_NORMAL);
  
    // 获取 Vsync 的 Period
    const nsecs_t vsyncPeriod = getVsyncPeriod();
    // 设置 Vsync 的 Period
    mAnimFrameTracker.setDisplayRefreshPeriod(vsyncPeriod);

    ...
}

onInitializeDisplays 函数有点长,但逻辑并不复杂,它主要都是获取显示设备的参数并且设置,然后获取 Vsync 的 Period。

看到这我们就明白了,因为 Vsync 涉及到显示的刷新率,所以系统需要先知道显示设备支持的刷新率是多少,是60帧还是120帧。所以必须在初始化显示设备的时候来获取这个 Period,因为 Period 就是一帧需要的时间。如果每秒60帧, 那么 period 就是16.6667ms

5.2.4 SurfaceFlinger::getVsyncPeriod

[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]

nsecs_t SurfaceFlinger::getVsyncPeriod() const {
    const auto displayId = getInternalDisplayIdLocked();
    if (!displayId || !getHwComposer().isConnected(*displayId)) {
        return 0;
    }
        
    // 获取 displayId 对应显示设备的 config,然后再通过 config 拿到对应的刷新时间
    // 这里 getHwComposer 获取到的就是 HWComposer
    // 拿到的 Config其实就是 HWC2::Display::Config
    const auto config = getHwComposer().getActiveConfig(*displayId);
    return config ? config->getVsyncPeriod() : 0;
}

5.2.5 SurfaceFlinger::setPowerModeInternal

在 getVsyncPeriod 之前,还有一个函数 setPowerModeInternal,函数名的意思是设置电源模式,它的任务也就是处理电源模式的切换,电源的模式有很多,我们这里值列举两个比较重要的息屏和亮屏

[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]

void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int mode) {
    // 判断是否是虚拟显示
    if (display->isVirtual()) {
        return;
    }
    // 获取显示设备的id
    const auto displayId = display->getId();
    // 获取电源模式
    int currentMode = display->getPowerMode();
    // 如果模式没有发生改变,就直接返回
    if (mode == currentMode) {
        return;
    }
    // 设置显示设备的电源模式
    display->setPowerMode(mode);
    // 如果存在 mInterceptor,则修改 mInterceptor 中的电源模式
    if (mInterceptor->isEnabled()) {
        mInterceptor->savePowerModeUpdate(display->getSequenceId(), mode);
    }
    // 注意,下面的判断分别判断了 currentMode 和 mode
    // currentMode 是当前的电源状态,mode 是电源修改的目标状态
    if (currentMode == HWC_POWER_MODE_OFF) {
        // 如果 currentMode 是关,那么首先要做的肯定需要开启显示器,也就是亮屏
        // 修改 HwComposer 的电源模式
        getHwComposer().setPowerMode(*displayId, mode);
        if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
            // 调用 Scheduler 的 onScreenAcquired,最后会调用到 EventThread 的 onScreenAcquired
            // 这里传递的是 App 的 ConnectionHandle,也就是说会调用到 App 的 EventThread
            mScheduler->onScreenAcquired(mAppConnectionHandle);
            // 调用 Scheduler 的 resyncToHardwareVsync,这里又获取了一次 getVsyncPeriod,
            // 并且将 Period 作为参数传递了进去
            mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
        }
        mVisibleRegionsDirty = true;
        mHasPoweredOff = true;
        // 亮屏了,当然要重绘所有的事物
        repaintEverything();
        struct sched_param param = {0};
        param.sched_priority = 1;

    } else if (mode == HWC_POWER_MODE_OFF) {
        // 如果 mode 是关,那么说明要息屏
        struct sched_param param = {0};

        // 关闭 Display,当然需要关闭 Vsync
        if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) {
            mScheduler->disableHardwareVsync(true);
            mScheduler->onScreenReleased(mAppConnectionHandle);
        }

        getHwComposer().setPowerMode(*displayId, mode);
        mVisibleRegionsDirty = true;
        // SF 将停止绘制工作
    } ...

    if (display->isPrimary()) {
        mTimeStats->setPowerMode(mode);
        mRefreshRateStats.setPowerMode(mode);
    }

}

这里我们先看逻辑简单的 repaintEverything

5.2.6 repaintEverything

[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]

void SurfaceFlinger::repaintEverything() {
    mRepaintEverything = true;
    signalTransaction();
}

5.2.7 signalTransaction

[frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp]

void SurfaceFlinger::signalTransaction() {
    mScheduler->resetIdleTimer();
    // 通知 SurfaceFlinger 中的消息队列进行重绘
    mEventQueue->invalidate();
}

六 Scheduler

Scheduler 就是 SurfaceFlinger 中的调度器,它里面有很多重要的信息,我们先看它的构造函数

Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
                     const scheduler::RefreshRateConfigs& refreshRateConfig)
      : mHasSyncFramework(running_without_sync_framework(true)),
        mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)),
        mPrimaryHWVsyncEnabled(false),
        mHWVsyncAvailable(false),
        mRefreshRateConfigs(refreshRateConfig) {
          
    // 注意:临时使用real DispSync实现类型创建一个本地临时文件,
    // 以便在使用接口类型将其存储以供更一般的使用之前,可以使用配置的值对其进行初始化。
    auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync");
    primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset);
    mPrimaryDispSync = std::move(primaryDispSync);
    
    // 创建了一个 EventControlThread 对象,参数是外界传递进来的 function
    // 这个 function 是 impl::EventControlThread::SetVSyncEnabledFunction
    // 这个 funtion 其实就是 SF 在 init 时传递进来的 SF 中的函数 setPrimaryVsyncEnabled
    mEventControlThread = std::make_unique<impl::EventControlThread>(function);
    ...
}

SurfaceFlinger 中的 setPrimaryVsyncEnabled,其实就是设置 HwComposer 的 Vsync 开关,也就是硬件的 Vsync

void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
    ATRACE_CALL();
    Mutex::Autolock lock(mStateLock);
    if (const auto displayId = getInternalDisplayIdLocked()) {
        getHwComposer().setVsyncEnabled(*displayId,
                                        enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
    }
}

6.1 Scheduler::resyncToHardwareVsync

接下来我们接着看修改电源模式 setPowerModeInternal 中,调用而来的 resyncToHardwareVsync

void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
    {
        std::lock_guard<std::mutex> lock(mHWVsyncLock);
        // setPowerModeInternal 中传递进来的参数是 true,所以这里 mHWVsyncAvailable 也被设置为 true
        // 也就是说使用硬件 Vsync
        if (makeAvailable) {
            mHWVsyncAvailable = makeAvailable;
        } else if (!mHWVsyncAvailable) {
            // 硬件 Vsync当前不可用,因此暂时中止重新同步尝试
            return;
        }
    }
        
    // 如果刷新时间小于0,显然是错误的,我们假定系统是每秒60帧,那么这里的 period 应该是 16.6667ms
    if (period <= 0) {
        return;
    }
    // 设置 Vsync 的周期时间
    setVsyncPeriod(period);
}

6.2 Scheduler::setVsyncPeriod

void Scheduler::setVsyncPeriod(const nsecs_t period) {
    std::lock_guard<std::mutex> lock(mHWVsyncLock);
    // 调用 mPrimaryDispSync 的 setPeriod 设置刷新时间,前面我们一家知道了 mPrimaryDispSync 其实就是 DispSync
    // 所以这里其实调用的是 DispSync 的 setPeriod
    mPrimaryDispSync->setPeriod(period);

    // mPrimaryHWVsyncEnabled 是在 Scheduler 的构造函数中被设置为 false 的
    if (!mPrimaryHWVsyncEnabled) {
        mPrimaryDispSync->beginResync();
        // 调用 EventControlThread 的 setVsyncEnabled [6.4.3]
        mEventControlThread->setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}

6.3 DispSync::setPeriod

void DispSync::setPeriod(nsecs_t period) {
    Mutex::Autolock lock(mMutex);
    mPendingPeriod = period;
}

在 DispSync 的 setPeriod 中,修改了 mPendingPeriod 的值为 period。还记得之前因为 period 为0而陷入的等待吗,这里终于将 period 的值改变了。

接下来我们看看这个 EventControlThread 是如何打开 Vsync 开关的。

6.4 EventControlThread

通过刚才的代码,我们已经能猜到,这个 EventControlThread 其实就是控制 Vsync 的开关。首先我们来看这个控制器的定义

6.4.1 EventControlThread 的定义

class EventControlThread final : public android::EventControlThread {
    std::thread mThread{&EventControlThread::threadMain, this};
}

EventControlThread 的定义比较简单,但是在它的最后一行有一个 mThread,这个 mThread 执行的就是 EventControlThread 的 threadMain 函数

6.4.2 EventControlThread的构造函数

EventControlThread 的构造函数则是设置了线程的名字和优先级。接下来我们继续之前的分析

EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function)
      : mSetVSyncEnabled(function) {
    // 设置线程名
    pthread_setname_np(mThread.native_handle(), "EventControlThread");

    pid_t tid = pthread_gettid_np(mThread.native_handle());
    // 设置优先级
    setpriority(PRIO_PROCESS, tid, ANDROID_PRIORITY_URGENT_DISPLAY);
    set_sched_policy(tid, SP_FOREGROUND);
}

6.4.3 EventControlThread::setVsyncEnabled

void EventControlThread::setVsyncEnabled(bool enabled) {
    std::lock_guard<std::mutex> lock(mMutex);
    mVsyncEnabled = enabled;
    mCondition.notify_all();
}

在 setVsyncEnabled 中修改了 mVsyncEnabled,并且调用了 mCondition 的 notify_all,之前已经说了 EventControlThread 中有一个 Thread,想必这个通知就会回调到这个 Thread 中

6.4.4 EventControlThread::threadMain

void EventControlThread::threadMain() NO_THREAD_SAFETY_ANALYSIS {
    auto keepRunning = true;
    auto currentVsyncEnabled = false;

    // 死循环
    while (keepRunning) {
        mSetVSyncEnabled(currentVsyncEnabled);

        std::unique_lock<std::mutex> lock(mMutex);
        mCondition.wait(lock, [this, currentVsyncEnabled, keepRunning]() NO_THREAD_SAFETY_ANALYSIS {
            return currentVsyncEnabled != mVsyncEnabled || keepRunning != mKeepRunning;
        });
        currentVsyncEnabled = mVsyncEnabled;
        keepRunning = mKeepRunning;
    }
}

threadMain 中只做了一件事,就是修改 currentVsyncEnabled,并且将他传递到 mSetVSyncEnabled。看来和这个类的名字一样,这真的是一个 Event 的控制线程。

至于这个 mSetVSyncEnabled 函数,我们在构造函数中就已经看到,它其实就是外面传递进来的 function,而在最开始,我们已经看到了在 SF 的 init 函数中,传递进来的 setPrimaryVsyncEnabled,所以这里最终调用到的就是 SF 的 setPrimaryVsyncEnabled。

七 setPrimaryVsyncEnabled 的调用过程

setPrimaryVsyncEnabled 首先调用到 SurfaceFlinger 中,然后在 SurfaceFlinger 中,又调用到 HWComposer,这里设置的 Vsync 就是硬件的 Vsync。

7.1 SurfaceFlinger::setPrimaryVsyncEnabled

void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
    Mutex::Autolock lock(mStateLock);
    if (const auto displayId = getInternalDisplayIdLocked()) {
        // 获取 HwComposer,设置 Vsync 的 enabled
        getHwComposer().setVsyncEnabled(*displayId,
                                        enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
    }
}

7.2 HWComposer::setVsyncEnabled

void HWComposer::setVsyncEnabled(DisplayId displayId, hal::Vsync enabled) {
    RETURN_IF_INVALID_DISPLAY(displayId);
    auto& displayData = mDisplayData[displayId];

    if (displayData.isVirtual) {
        LOG_DISPLAY_ERROR(displayId, "Invalid operation on virtual display");
        return;
    }
    // 注意:我们在这里使用我们自己的内部锁,因为我们必须在锁保持的情况下调用HWC,
    // 并且我们希望确保即使HWC阻塞(它不应该阻塞),它也不会影响其他线程。
    std::lock_guard lock(displayData.vsyncEnabledLock);
    if (enabled == displayData.vsyncEnabled) {
        return;
    }
        // 调用 hwcDisplay 的 setVsyncEnabled
    auto error = displayData.hwcDisplay->setVsyncEnabled(enabled);
    RETURN_IF_HWC_ERROR(error, displayId);

    displayData.vsyncEnabled = enabled;
}

在 HWComposer 中,会设置硬件是否产生 Vsync 信号。到此,我们也知道了 Vsync 信号是通过 HWComposer 产生的了。

7.3 HWComposer 的初始化

但是我们还是没有弄清楚 Vsync 的硬件信号是如何变成软件信号的。

但是我们这里看到的都是硬件的 Vsync 信号,它有是如何变成 EventThread 中的 Event 的呢。

我们回到 SurfaceFlinger 的 init 函数中,在 init 函数中有这样两行代码

SurfaceFlinger::init(){
    // 通过 Factory 创建 HWComposer,并将创建的 HWComposer 设置到 CompositionEngine
    // 这里的 getBE 获取到的就是 SurfaceFlingerBE 对象,它是 SF 中的一个内部类,封装了和 HWComposer 相关的逻辑
    mCompositionEngine->setHwComposer(getFactory().createHWComposer(getBE().mHwcServiceName));
    // 给 HWComposer 注册回调,回调函数就是 this(SurfaceFlinger 自己)
    mCompositionEngine->getHwComposer().registerCallback(this, getBE().mComposerSequenceId);
}

getFactory().createHWComposer(getBE().mHwcServiceName) 就是创建 HWComposer。传递进去的就是 HWComposer 的服务名称。

7.3.1 HWComposer 的创建

首先将传递进来的字符串作为 serviceName 创建了一个 HWComposer

std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override {
    return std::make_unique<android::impl::HWComposer>(
            std::make_unique<Hwc2::impl::Composer>(serviceName));
}

7.3.2 HWComposer::registerCallback

void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
                                  int32_t sequenceId) {
    // 这个 mHwcDevice 是 HWC2::Device
    mHwcDevice->registerCallback(callback, sequenceId);
}

在创建完之后,SurfaceFlinger 又将自己作为回调对象传递到了 HWComposer 中,对应的序列id是通过 SurfaceFlingerBE 获取来的。也就是显示硬件的序列号。

7.3.3 Device::registerCallback

void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) {
    if (mRegisteredCallback) {
        ALOGW("Callback already registered. Ignored extra registration "
                "attempt.");
        return;
    }
    mRegisteredCallback = true;
    // 创建了一个回调的连接桥,封装了 callback(即Sf)和对应的序列id
    sp<ComposerCallbackBridge> callbackBridge(
            new ComposerCallbackBridge(callback, sequenceId));
    // 将 callbackBridge 设置到 mComposer 中
    // 这个 mComposer 定义在 HWC2.h 中,是 android::Hwc2::Composer
    // 它的实现在 ComposerHal.cpp
    mComposer->registerCallback(callbackBridge);
}

7.3.4 Composer::registerCallback

void Composer::registerCallback(const sp<IComposerCallback>& callback)
{       
    // 这里有一个 mClient,它其实就是硬件抽象层在 SurfaceFlinger 中的对象 IComposerClient
    auto ret = mClient->registerCallback(callback);
}

7.4 ComposerCallbackBridge

之前已经说了 ComposerCallbackBridge 是对硬件 Vsync 回调的封装,那么我们接下来就看它是如何进行封装的,首先还是看 ComposerCallbackBridge 的定义

class ComposerCallbackBridge : public Hwc2::IComposerCallback {
public:
    ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
            : mCallback(callback), mSequenceId(sequenceId) {}

    Return<void> onHotplug(Hwc2::Display display,
                           IComposerCallback::Connection conn) override
    {
        HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
        mCallback->onHotplugReceived(mSequenceId, display, connection);
        return Void();
    }

    Return<void> onRefresh(Hwc2::Display display) override
    {
        mCallback->onRefreshReceived(mSequenceId, display);
        return Void();
    }

    Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
    {
        mCallback->onVsyncReceived(mSequenceId, display, timestamp);
        return Void();
    }

private:
    ComposerCallback* mCallback;
    int32_t mSequenceId;
};

显然,当系统硬件发出 Vsync 信号时,会通过 ComposerCallbackBridge 调用到它的 mCallback 的 onVsyncReceived。而这个 mCallback 之前已经说了,就是 SurfaceFlinger 对象,所以最终系统硬件层的 Vsync 信号,会通过 SurfaceFlinger 的 onVsyncReceived 函数进行传递。

到这里我们已经知道硬件的 Vsync 信号是如何传递到 SurfaceFlinger 中的。那么还有一个问题,这个是如何变成软件信号的?

八 Vsync 模拟信号的产生

首先我们来看 SurfaceFlinger 接收到硬件 Vsync 信号后做的事情。

void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
                                     int64_t timestamp) {

    Mutex::Autolock lock(mStateLock);
    // 首先判断序列号是否匹配,如果不匹配则忽略
    if (sequenceId != getBE().mComposerSequenceId) {
        return;
    }

    // 然后通过 HwComposer 过滤不需要的 Vsync 信号,正常情况结果为 true
    if (!getHwComposer().onVsync(hwcDisplayId, timestamp)) {
        return;
    }

    // 是否是外部显示器的 Vsync。Android 虽然支持外部显示器,但是手机并没有外部显示器
    if (hwcDisplayId != getHwComposer().getInternalHwcDisplayId()) {
        return;
    }

    // 调用 Scheduler 的 addResyncSample
    bool periodChanged = false;
    mScheduler->addResyncSample(timestamp, &periodChanged);
    if (periodChanged) {
        // 如果刷新率改变了,还需要调用 onRefreshRateChangeDetected
        mVsyncModulator.onRefreshRateChangeDetected();
    }
}

8.1 Scheduler::addResyncSample

首先是调用调度器的 addResyncSample,然后在调度器里面,调用了 DispSync 的 addResyncSample

void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodChanged) {
    bool needsHwVsync = false;
    *periodChanged = false;
    {
        std::lock_guard<std::mutex> lock(mHWVsyncLock);
        if (mPrimaryHWVsyncEnabled) {
            // 调用 DispSync::addResyncSample [8.2]
            needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp, periodChanged);
        }
    }

    if (needsHwVsync) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}

8.2 DispSync::addResyncSample

addResyncSample 这个函数非常复杂,并且它其中调用到的 updateModelLocked 也是一个非常关键的函数。这两个函数运用到了一些数学知识,所以在理解这两个函数之前,我们先看其中定义的宏

// 最多记录多少个 Vsync 采样点,定义的是32个
enum { MAX_RESYNC_SAMPLES = 32 };
// 最少要多少个 Vsync 采样点才开始更新刷新时间,定义是6个
enum { MIN_RESYNC_SAMPLES_FOR_UPDATE = 6 };

enum { NUM_PRESENT_SAMPLES = 8 };
enum { MAX_RESYNC_SAMPLES_WITHOUT_PRESENT = 4 };
enum { ACCEPTABLE_ZERO_ERR_SAMPLES_COUNT = 64 };

理解了其中的宏,我们开始逐步分析 addResyncSample

bool DispSync::addResyncSample(nsecs_t timestamp, bool* periodChanged) {
    Mutex::Autolock lock(mMutex);

    // 刷新率是否改变了
    *periodChanged = false;
    // 首先用 mFirstResyncSample 加 mNumResyncSamples 然后对 MAX_RESYNC_SAMPLES 取余
    // 这里系统最多会记录32个采样点,mNumResyncSamples 代表采样点的数量,它是不断自增的
    // 而 mFirstResyncSample 则表示这32个采样点中,第一个采样点的序号是多少
    // 假设现在第一个采样点是idx是6,已经有32个采样点,那么这32个采样点的idx就应该是6-37号
    // 所以这里取余得到的结果,应该是最后一个采样点的序号加1,也就是下一个采样点的序号38,取余之后为6
    // 所以这里会将第一个采样点idx的时间戳覆盖
    const size_t idx = (mFirstResyncSample + mNumResyncSamples) % MAX_RESYNC_SAMPLES;
    // 将传递进来的 timestamp 设置为6号采样点的时间戳
    mResyncSamples[idx] = timestamp;
    // 如果 mNumResyncSamples 为0,说明是第一次收到 Vsync 信号
    if (mNumResyncSamples == 0) {
        mPhase = 0;
    } else if (mPendingPeriod > 0) {
        // mNumResyncSamples > 0, so priorIdx won't overflow
        // 这里是先减1再取余,也就是最后一个采样点的序号,即37号采样点
        const size_t priorIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
        // 拿到41号采样点的时间戳
        const nsecs_t lastTimestamp = mResyncSamples[priorIdx];
                // 用42号采样点的时间戳减去41号采样点的时间戳,也就是最后两个采样点的时间戳之差
        const nsecs_t observedVsync = std::abs(timestamp - lastTimestamp);
        // mPeriod 是一帧应该花费的时间,之前我们假定是60帧,它就是16.6667ms
        // mPendingPeriod 则是到下一个 Vsync 信号需要等待的时间
        // observedVsync 是两个采样点之间的时间差,理想情况,采样点的时间差应该和每帧的时间 mPeriod 相同
        // 但是因为 Vsync 信号是硬件发出的,处理硬件信号需要花费时间,所以实际运行时,这两个时间是不同的
        if (std::abs(observedVsync - mPendingPeriod) < std::abs(observedVsync - mPeriod)) {
            // 即 mPendingPeriod > mPeriod
            // 观察到的 Vsync 更接近挂起期间,因此重置模型并刷新挂起期间。
            resetLocked();
            mPeriod = mPendingPeriod;
            mPendingPeriod = 0;
            *periodChanged = true;
        }
    }
    // 用最新的时间戳更新参考时间
    mReferenceTime = timestamp;
    // 通知 Thread 收到 Vsync 信号
    // 调用 DispSync 中的 mThread 的 updateModel,即 DispSyncThread::updateModel
    mThread->updateModel(mPeriod, mPhase, mReferenceTime);

    // 更新 mNumResyncSamples 或 mFirstResyncSample 的值
    // 如果采样点的数量没有到32个,则 mNumResyncSamples 自增
    // 如果到达了32个,则需要将第一个采样点的序号后移一位,之前第一个采样点的idx是6,这样后移了一位之后,
    // 第一个采样点的idx就变成了7
    if (mNumResyncSamples < MAX_RESYNC_SAMPLES) {
        mNumResyncSamples++;
    } else {
        mFirstResyncSample = (mFirstResyncSample + 1) % MAX_RESYNC_SAMPLES;
    }

    // 重要函数,计算刷新时间
    updateModelLocked();

    if (mNumResyncSamplesSincePresent++ > MAX_RESYNC_SAMPLES_WITHOUT_PRESENT) {
        resetErrorLocked();
    }

    if (mIgnorePresentFences) {
        // 如果我们忽略了当前的fences,我们就无法知道我们是否与HW-vsync同步,所以我们只是请求打开HW-vsync事件。
        return true;
    }
    // Check against kErrorThreshold / 2 to add some hysteresis before having to resync again
    // 检查kErrorThreshold/2以在再次重新同步之前添加一些滞后
    bool modelLocked = mModelUpdated && mError < (kErrorThreshold / 2) && mPendingPeriod == 0;

    if (modelLocked) {
        mThread->lockModel();
    }
    return !modelLocked;
}

这里有两个比较重要的函数,一个是 DispSyncThread::updateModel,一个是 updateModelLocked。我们先看 updateModelLocked

8.3 DispSync::updateModelLocked

updateModelLocked 这个函数会对 Vsync 信号的时间进行校准,我们知道 Vsync 信号是由硬件发出的,那么从硬件发出的 Vsync 信号到软件接收到,这中间会花费一定的时间,如果我们希望软件的 Vsync 信号和硬件的同步,那么就需要知道从硬件信号到软件信号花费了多少时间。

我们假设一个 Vsync 周期是一个圆,这个圆的角的弧度是 2π,这个圆的周长是 mPeriod,即一个周期ms占用的角是2π弧度。

这里我们假设系统是每秒60帧,那么一帧的时间就是16.6667个单位时间ms,即16.6667个单位时间ms对应的弧度是2π。如上图所示。

那么现在我们要求一个单位时间ms对应的弧度是多少呢?也就是这个秒针转动了多少。这里假定为 scale,公式如下:

mPeriod(16.6667ms)2π=1msscale\frac{mPeriod(16.6667ms)}{2\pi} = \frac{1ms}{scale}

结果就是:

scale=2πmPeriod(16.6667ms){scale} = \frac{2\pi}{mPeriod(16.6667ms)}

即一个单位ms对应角的弧度是 2πmPeriod(16.6667ms)\frac{2\pi}{mPeriod(16.6667ms)}, 理解了这些,我们再来看下面的代码

double scale = 2.0 * M_PI / double(mPeriod); 首先计算一个单位时间ms对应在圆上的角,所占用的弧度 scale。

size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES; mFirstResyncSample 是第一个 Vsync 采样点的序列号,而用它加1,就是从第二个采样点开始

nsecs_t sample = mResyncSamples[idx] - mReferenceTime; mReferenceTime 是最新采样点的时间戳,每次收到 Vsync 信号时,都会更新 mReferenceTime。

mResyncSamples[idx] - mReferenceTime 得到的就是每个采样点到现在时间戳的具体时间 sample,它应该是 mPeriod * n ± k,其中n是正整数,k是常数

double samplePhase = double(sample % mPeriod) * scale; 用 sample 对 mPeriod 取余,就是得到这个±k,这个k是一个具体的时间,单位为ms,然后用它乘以 scale,就得到了这个k在这个周期圆上对应角的弧度 samplePhase。

然后再对这个弧度求三角函数,假设这个误差的弧度为α:

sampleAvgX += cos(samplePhase);

sampleAvgY += sin(samplePhase);

最后对这个弧度取三角函数,得到X和Y坐标,并且相加, sampleAvgX 就是这个弧度的终点的X坐标,sampleAvgY 就是Y坐标

sampleAvgX /= double(mNumResyncSamples - 1);

sampleAvgY /= double(mNumResyncSamples - 1);

然后循环所有采样点的集合,也就是32个,得到X坐标的和和Y坐标的和,然后用这个和除以 mNumResyncSamples - 1,因为我们是从第二个采样点开始循环的,所有少一个采样点,得到平均坐标 sampleAvgX 和 sampleAvgY。最后求出这32个采样点的平均误差mPhase

mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);

通过反三角函数 atan2 得到这个点角对应的弧度 α',然后除以 scale,scale 表示一个单位时间ms所占用角的弧度,用 α' 除以 scale ,得到 mPhase 就表示这个角对应的弧度 α' 占用了多少个单位时间ms。

这里得到的 mPhase 就是一个周期的误差的时间,也就是从硬件信号到软件信号的耗时。它是通过32个采样点计算得来的。

void DispSync::updateModelLocked() {
    // 只有当采样点的数量大于6个的时候,才会做校准操作,也就是样本数量太少则不校准
    if (mNumResyncSamples >= MIN_RESYNC_SAMPLES_FOR_UPDATE) {

        nsecs_t durationSum = 0;
        nsecs_t minDuration = INT64_MAX;
        nsecs_t maxDuration = 0;
        // 这里计算总时长,以及拿到最长和最短的硬件vsync间隔
        for (size_t i = 1; i < mNumResyncSamples; i++) {
            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
            // 这里看成 (idx - 1 + MAX_RESYNC_SAMPLES) % MAX_RESYNC_SAMPLES 更好理解
            // 即 idx 的前一个采样点
            size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
            // 两个采样点的时间差
            nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
            // 增加到总时间差里面
            durationSum += duration;
            minDuration = min(minDuration, duration);
            maxDuration = max(maxDuration, duration);
        }

        // 这里用总时间差,减去最大和最小时间差后,计算平均间隔 mPeriod
        // 也就是每两个采样点之间平均花费的时间
        durationSum -= minDuration + maxDuration;
        mPeriod = durationSum / (mNumResyncSamples - 3);

        // 采样点的X和Y坐标
        double sampleAvgX = 0;
        double sampleAvgY = 0;
        // 2π 除以 mPeriod
        double scale = 2.0 * M_PI / double(mPeriod);
        // 跳过第一个Vsync,因为第一个Vsync已经更新到DispSync中了。
        // mReferenceTime是第一个Vsync的时间
        for (size_t i = 1; i < mNumResyncSamples; i++) {
            size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
            nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
            double samplePhase = double(sample % mPeriod) * scale;
            sampleAvgX += cos(samplePhase);
            sampleAvgY += sin(samplePhase);
        }

        sampleAvgX /= double(mNumResyncSamples - 1);
        sampleAvgY /= double(mNumResyncSamples - 1);

        mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);

        if (mPhase < -(mPeriod / 2)) {
            mPhase += mPeriod;
        }

        // 如果需要跳帧,就将 Vsync 周期增加跳过的帧数
        mPeriod += mPeriod * mRefreshSkipCount;

        // mThread 是 DispSyncThread,更新sw model. 这个方法会唤醒 DispSync 中的 mThread 线程
        mThread->updateModel(mPeriod, mPhase, mReferenceTime);
        mModelUpdated = true;
    }
}

8.4 DispSync::updateModel

首先我们看这个 updateModel,updateModel 的主要工作就是给 Vsync 相关的几个时间变量赋值,并且唤醒软件 Vsync 线程。这个函数会经过多次调用,我们先看第一次调用的逻辑

void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {

    Mutex::Autolock lock(mMutex);

    // 设置 Vsync 软硬件时间的误差(也可以说是时间坐标轴的偏移)
    mPhase = phase;

    // 如果参考时间不相同,说明这次的 Vsync 校准后的回调还没有执行,
    // 第一次调用的时 mReferenceTime 和 referenceTime 是相同的
    if (mReferenceTime != referenceTime) {
        for (auto& eventListener : mEventListeners) {
            eventListener.mHasFired = false;
        }
    }

    mReferenceTime = referenceTime;
    if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) {
                ...
    }

    // 设置 Vsync 周期
    mPeriod = period;

    // 唤醒线程
    mCond.signal();
}

到了这里,终于唤醒了之前的 threadLoop 线程了,还记得5.1中的 threadLoop 吗,当时值看了它的第一部分,现在来看它的第二部分

8.5 threadLoop的第二部分

virtual bool threadLoop() {
    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
    while (true) {
        ...

        {   ...

            if (mPeriod == 0) {
                // 之前会在这里陷入等待
                err = mCond.wait(mMutex);
                if (err != NO_ERROR) {
                    return false;
                }
                continue;
            }

            // 计算下一次 Vsync 的时间,这里传入了一个系统时间 now
            targetTime = computeNextEventTimeLocked(now);

            bool isWakeup = false;

            // 如果还没有到下一次 Vsync 的时间,那么就等待,直到下一次 Vsync 的时间
            if (now < targetTime) {
                if (targetTime == INT64_MAX) {
                    err = mCond.wait(mMutex);
                } else {
                    err = mCond.waitRelative(mMutex, targetTime - now);
                }

                if (err == TIMED_OUT) {
                    isWakeup = true;
                } else if (err != NO_ERROR) {
                    return false;
                }
            }

            // 拿到当前的系统时间
            now = systemTime(SYSTEM_TIME_MONOTONIC);

            // 校准时间不要超过1.5ns
            static const nsecs_t kMaxWakeupLatency = us2ns(1500);

            // 如果是没有到下次 Vsync 时间,陷入等待后,超时唤醒的,那么就进行校准
            if (isWakeup) {
                mWakeupLatency = ((mWakeupLatency * 63) + (now - targetTime)) / 64;
                mWakeupLatency = min(mWakeupLatency, kMaxWakeupLatency);
            }

            // 回调
            callbackInvocations = gatherCallbackInvocationsLocked(now);
        }

        if (callbackInvocations.size() > 0) {
            fireCallbackInvocations(callbackInvocations);
        }
    }
    return false;
}

这个函数也比较长,再看这个函数之前,我们先看相关的类 DispSyncSource

8.5.1 DispSyncSource 的定义

前面已经说了 , 在调度器 Scheduler 中创建了一个 DispSync , 然后通过这个 DispSync 创建了一个 DispSyncSource , 这里看DispSyncSource 的定义,我们发现它实现了一个 Callback , 这个 Callback 就是 Vsync 的回调

[frameworks/native/services/surfaceflinger/Scheduler/DispSyncSource.h]
class DispSyncSource final : public VSyncSource, private DispSync::Callback

了解了 DispSyncSource 之后,我们继续看 threadLoop

threadLoop 这个函数较长,我们分开来看

  1. computeNextEventTimeLocked:计算下一次 Vsync 信号的时间
  2. gatherCallbackInvocationsLocked:这个方法获取之前设置在 DispSync 中的监听
  3. fireCallbackInvocations:这个方法用来分发 Vsync 到对应的监听

8.5.2 computeNextEventTimeLocked

nsecs_t computeNextEventTimeLocked(nsecs_t now) {

    nsecs_t nextEventTime = INT64_MAX;
    for (size_t i = 0; i < mEventListeners.size(); i++) {
        // 遍历这个 mEventListeners,mEventListeners 一般有两个,一个是 SF,一个是 App
        nsecs_t t = computeListenerNextEventTimeLocked(mEventListeners[i], now);
        // 如果算出的时间小于 nextEventTime,则将 nextEventTime 设置为算出的时间
        if (t < nextEventTime) {
            nextEventTime = t;
        }
    }
    // 遍历完所有的 mEventListeners之后,nextEventTime 就是所有 mEventListeners 计算出来的最小值
    // 也就是最接近当前时间的时间戳,这个就是下个 Vsync 信号的时间戳
    return nextEventTime;
}

8.5.3 computeListenerNextEventTimeLocked

nsecs_t computeListenerNextEventTimeLocked(const EventListener& listener, nsecs_t baseTime) {
    // mLastEventTime 就是最后一个 SW Vsync 的目标时间,mWakeupLatency 就是上次线程醒来的延时时间
    // 得到的 lastEventTime 就是最后一次 SW Vsync 的实际时间
    nsecs_t lastEventTime = listener.mLastEventTime + mWakeupLatency;
    // baseTime 就是外面传进来的当前系统时间,一般来说 baseTime 肯定是大于 lastEventTime的
    if (baseTime < lastEventTime) {
        baseTime = lastEventTime;
    }
    // baseTime 减去 mReferenceTime,即减去硬件 Vsync 采样点的参考时间
    // 因为我们要计算它相对于参考时间的还有多久
    baseTime -= mReferenceTime;
    // 计算实际偏移,记得我们一开始在创建 EventThread 的时候传递进来的偏移吗,对于 App 和 SF 传递的偏移是不一样的,
    // 当时我说是为了错开 App 和 SF 的工作时间,避免造成拥挤,现在这里就用到了这个传递进来的偏移
    // SW Vsync 的最终偏移就是 系统偏移mPhase + EventThread的偏移
    // SF 是 SF_VSYNC_EVENT_PHASE_OFFSET_NS
    // APP 是 VSYNC_EVENT_PHASE_OFFSET_NS
    nsecs_t phase = mPhase + listener.mPhase;

    // baseTime 再减去实际偏移的时间
    baseTime -= phase;

    // 经过了上面两次的相减,那么我们得到的 baseTime 就应该是最后一个硬件 Vsync 采样点
    if (baseTime < 0) {
        baseTime = -mPeriod;
    }

    // mPeriod 表示一个 Vsync 周期,baseTime / mPeriod 则表示现在到了第几个 Vsync 周期
    nsecs_t numPeriods = baseTime / mPeriod;

    // 计算下一个 Vsync 周期的时间,并且用它加上实际偏移的时间,就是 SW Vsync 的目标时间
    nsecs_t t = (numPeriods + 1) * mPeriod + phase;

    // 再将这个时间加上参考时间
    // 参考时间加上相对于参考时间的时间,就是下一个 SW Vsync 的目标时间
    t += mReferenceTime;

    // 如果这个 SW Vsync 的目标时间距离上一个 SW Vsync 的时间小于3/5个周期,那么跳过这个 SW Vsync,
    // 将它后移一个周期
    if (isCloseToPeriod(t - listener.mLastEventTime)) {
        t += mPeriod;
    }

    // 最后还要减去线程醒来的耗时
    t -= mWakeupLatency;

    return t;
}

bool isCloseToPeriod(nsecs_t duration) {
    // Ratio of 3/5 is arbitrary, but it must be greater than 1/2.
    return duration < (3 * mPeriod) / 5;
}

computeListenerNextEventTimeLocked 这个函数非常复杂,它主要就是为了计算下一次 Vsync 的时间,所以需要考虑硬件的时间差,app 和 sf 不同的时间差,还有系统的参考时间。

8.5.4 gatherCallbackInvocationsLocked

gatherCallbackInvocationsLocked 这个函数主要就是获取之前设置在 mEventListeners 中的 callback , 这个 Callback 就是DispSyncSource , 之前我们看 DispSyncSource 的定义的时候已经说过了 , 它实现了 Callback 的回调

[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) {
    std::vector<CallbackInvocation> callbackInvocations;
    ...
    for (auto& eventListener : mEventListeners) {
        if (t < now) {
            ...
            ci.mCallback = eventListener.mCallback;
            ci.mEventTime = t;

            callbackInvocations.push_back(ci);
            eventListener.mLastEventTime = t;
            eventListener.mLastCallbackTime = now;
            eventListener.mHasFired = true;
        }
    }
    return callbackInvocations;
}

8.5.5 fireCallbackInvocations

[frameworks/native/services/surfaceflinger/Scheduler/DispSync.cpp]
void fireCallbackInvocations(const std::vector<CallbackInvocation>& callbacks) {
    for (size_t i = 0; i < callbacks.size(); i++) {
        callbacks[i].mCallback->onDispSyncEvent(callbacks[i].mEventTime);
    }
}

fireCallbackInvocations 这个函数则是调用之前获取到 callbacks 的 onDispSyncEvent , 其实就是调用的 DispSyncSource 的onDispSyncEvent

8.6 DispSyncSource::onDispSyncEvent

onDispSyncEvent 中的 callback 就是之前在 EventThread 构造函数中设置的 mCallback , 所以它会调用到 EventThread 中的onVSyncEvent

[frameworks/native/services/surfaceflinger/Scheduler/DispSyncSource.cpp]
void DispSyncSource::onDispSyncEvent(nsecs_t when) {
    ...
    if (callback != nullptr) {
        callback->onVSyncEvent(when);
    }
}

8.7 EventThread::onVSyncEvent

[frameworks/native/services/surfaceflinger/Scheduler/EventThread.cpp]
void EventThread::onVSyncEvent(nsecs_t timestamp) {
    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count));
    mCondition.notify_all();
}

在onVSyncEvent中 , 我们发现它往 mPendingEvents 中添加了一个Event 。还记得[2.2]中的问题1吗,mPendingEvents 中的 Event 是怎么来的?

到这里,我们跟踪了系统硬件的 Vsync 信号,到 SW Vsync 模拟信号,然后 SF 中的 EventThread 处理 Vsync 的过程。

总结

关于 SurfaceFlinger 的 Vsync 机制,基本已经全部说明了。这里做一个概括。

  1. SurfaceFlinger 中 Vsync 信号相关的线程是如何初始化的 [见一]
  2. SurfaceFlinger 中 Vsync 信号的线程是如何连接信号源和回调监听的 [见二]
  3. SurfaceFlinger 中 MessageQueue 是如何处理 Vsync 信号的 [见三]
  4. Vsync 信号源的创建过程 [见四]
  5. Vsync 信号源的工作线程的工作原理 [见五]
  6. Vsync 相关控制是如何实现的 [见六和七]
  7. Vsync 模拟信号是如何产生的,是如何控制时间减小误差的 [见八]
  • 对于 Vsync,首先它是由系统硬件发出的信号(HW-VSYNC),在 SurfaceFlinger 初始化的时候,会注册对应的硬件回调去接收这个硬件信号。
  • 当 SurfaceFlinger 接收到这个硬件信号后,会将它传递到 DispSync 中,然后通过 DispSync 这个模型,分发出两个不同的软件模拟的 Vsync(SW-VSYNC)。
  • 在 DispSync 不断发出模拟 Vsync 的时候,必然会存在运行时间长了导致误差逐渐增大的情况。而且将 HW-VSYNC 转换成 SW-VSYNC 的过程中,因为要经历硬件到软件的传递等一系列的 CPU 工作,所以也存在一段时间的误差,所以在 VSYNC 信号转换的过程中,和在分发 SW-SYNC 的时候需要考虑这部分的时间误差。
  • 最后,SW-VSYNC 通过 MessageQueue 的消息机制分发到对应的 sf 和 app 中,然后对应的进程开始相应的工作。