显示框架之深入Vsync原理

本文旨在研究这个话题-- vsync是如何有序控制sf合成和app绘制的节奏?
应用需要等VSYNC-app脉冲来进行绘制,绘制完后又需要等VSYNC-sf脉冲在surfaceflinger里面进行合成,VSYNC-app和VSYNC-sf的触发是这篇文章的重点,先来回顾下绘制->合成的整个链路,大致如下:{Pid: UI Thread}Choreographer#doFrame -> (Input、animation、traversal)-> draw -> {Pid: Renderthread} DrawFrames -> syncFrameState -> flush commands -> queueBuffer -> acquireNextBufferLocked -> {Pid: SF} setTransactionState -> queueTransaction -> setTransactionFlags。
Call到surfaceflinger的setTransactionFlags设置有更新的transaction,来表示应用要更新帧,先看下这个Func:

void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
                                         const sp<IBinder>& applyToken, FrameHint frameHint) {
    modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken);

    if (const bool scheduled = mTransactionFlags.fetch_or(mask) & mask; !scheduled) {
        scheduleCommit(frameHint);
    }
}

modulateVsync: 主要是更新CallbackRepeater类里面mWorkDuration 和 mReadyDuration这两个值,这两个值参与了vsync的计算。该func流程是根据schedule的类型决定VsyncConfigType 是选early,earlyGPU,还是late,然后取对应的duration赋值给mWorkDuration、mReadyDuration。以60hz为例,系统设置的early、GL early和late如下,那如果VsyncConfigType 选择为late,就将app duration 赋值给mWorkDuration,SF duration赋值给mReadyDuration

           app phase:      1000000 ns                 SF phase:      1000000 ns
           app duration:  16666666 ns                 SF duration:  15666666 ns
     early app phase:      1000000 ns           early SF phase:      1000000 ns
     early app duration:  16666666 ns           early SF duration:  15666666 ns
  GL early app phase:      1000000 ns        GL early SF phase:      1000000 ns
  GL early app duration:  16666666 ns        GL early SF duration:  15666666 ns
       HWC min duration:         0 ns
      present offset:         0 ns             VSYNC period:  16666666 ns
      
      
void SurfaceFlinger::setVsyncConfig(const VsyncModulator::VsyncConfig& config,
                                    nsecs_t vsyncPeriod) {
    mScheduler->setDuration(mAppConnectionHandle,
                            /*workDuration=*/config.appWorkDuration,
                            /*readyDuration=*/config.sfWorkDuration);
    mScheduler->setDuration(mSfConnectionHandle,
                            /*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
                            /*readyDuration=*/config.sfWorkDuration);
    mScheduler->setDuration(config.sfWorkDuration);
}

void DispSyncSource::setDuration(std::chrono::nanoseconds workDuration,
                                 std::chrono::nanoseconds readyDuration) {
    std::lock_guard lock(mVsyncMutex);
    mWorkDuration = workDuration;
    mReadyDuration = readyDuration;

    // If we're not enabled, we don't need to mess with the listeners
    if (!mEnabled) {
        return;
    }

    mCallbackRepeater->start(mWorkDuration, mReadyDuration);
}

mTransactionFlags:SF类的一个全局变量,通过setTransactionFlags 来增加flag,通过clearTransactionFlags来去掉对应的flag,flag类型如下:

enum {
    eTransactionNeeded = 0x01, 
    eTraversalNeeded = 0x02,  // 1和2表示这帧有layer状态的变化,比如:layerstack,size,alpha等
    eDisplayTransactionNeeded = 0x04, // 表示有display状态的变化,比如:DisplaySize,DestoryDisplay
    eTransformHintUpdateNeeded = 0x08,
    eTransactionFlushNeeded = 0x10, //表示需要合成这些变化的状态
    eTransactionMask = 0x1f,
};

回到setTransactionFlags,若mTransactionFlags没有
eTransactionFlushNeeded flag,则将mTransactionFlags加上eTransactionFlushNeeded,然后执行scheduleCommit,如图1所示。若mTransactionFlags本身有了eTransactionFlushNeeded则不会执行scheduleCommit,这种情况一般是这帧有mPendingTransactionQueues或者mTransactionQueue不为空,如图2所示。


图1

图2

看下scheduleCommit,走到MessageQueue::scheduleFrame里面,如下:

void MessageQueue::scheduleFrame() {
    ATRACE_CALL();

    {
        std::lock_guard lock(mInjector.mutex);
        if (CC_UNLIKELY(mInjector.connection)) {
            ALOGD("%s while injecting VSYNC", __FUNCTION__);
            mInjector.connection->requestNextVsync();
            return;
        }
    }

    std::lock_guard lock(mVsync.mutex);
    mVsync.scheduledFrameTime =
            mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(),
                                           .readyDuration = 0,
                                           .earliestVsync = mVsync.lastCallbackTime.count()});
}

主要来看下schedule 这个func,schedule是计算vsync时间戳的入口,如下:

ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) {
    if (!mValidToken) {
        return std::nullopt;
    }
    return mDispatch.get().schedule(mToken, scheduleTiming);
}

ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                 ScheduleTiming scheduleTiming) {
    ScheduleResult result;
    {
        std::lock_guard lock(mMutex);

        auto it = mCallbacks.find(token);
        if (it == mCallbacks.end()) {
            return result;
        auto& callback = it->second;
        auto const now = mTimeKeeper->now();

        /* If the timer thread will run soon, we'll apply this work update via the callback
         * timer recalculation to avoid cancelling a callback that is about to fire. */
        auto const rearmImminent = now > mIntendedWakeupTime;
        if (CC_UNLIKELY(rearmImminent)) {
            callback->addPendingWorkloadUpdate(scheduleTiming);
            return getExpectedCallbackTime(mTracker, now, scheduleTiming);
        }

        result = callback->schedule(scheduleTiming, mTracker, now);
        if (!result.has_value()) {
            return result;
        }
        if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
            rearmTimerSkippingUpdateFor(now, it);
        }
    }

    return result;
}

这里有个mToken来表示具体哪种类型的回调,在初始化时注册了3种类型的回调,分别为sf, app, appSf

// 注册sf的回调, callback为MessageQueue::vsyncCallback
void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
                             frametimeline::TokenManager& tokenManager,
                             std::chrono::nanoseconds workDuration) {
    setDuration(workDuration);
    mVsync.tokenManager = &tokenManager;
    mVsync.registration = std::make_unique<
            scheduler::VSyncCallbackRegistration>(dispatch,
                                                  std::bind(&MessageQueue::vsyncCallback, this,
                                                            std::placeholders::_1,
                                                            std::placeholders::_2,
                                                            std::placeholders::_3),
                                                  "sf");
}

/* 注册app和appSf的回调,由createConnection发起,mName分别为app和appSf,callback为
DispSyncSource::onVsyncCallback */
CallbackRepeater(VSyncDispatch& dispatch, VSyncDispatch::Callback cb, const char* name,
                     std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
                     std::chrono::nanoseconds notBefore)
          : mName(name),
            mCallback(cb),
            mRegistration(dispatch,
                          std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
                                    std::placeholders::_2, std::placeholders::_3),
                          mName),
            mStarted(false),
            mWorkDuration(workDuration),
            mReadyDuration(readyDuration),
            mLastCallTime(notBefore) {}

//分别将callbackName和callback带进来执行 registerCallback        
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
                                                     VSyncDispatch::Callback callback,
                                                     std::string callbackName)
      : mDispatch(dispatch),
        mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))),
        mValidToken(true) {}

//sf, app, appSf分别创建了VSyncDispatchTimerQueueEntry,mCallbacks size为3
VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback(
        Callback callback, std::string callbackName) {
    std::lock_guard lock(mMutex);
    return CallbackToken{
            mCallbacks
                    .emplace(++mCallbackToken,
                             std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName),
                                                                            std::move(callback),
                                                                            mMinVsyncDistance))
                    .first->first};
}

回到schedule里面,此时mToken表示sf的callback,mIntendedWakeupTime表示这帧预期唤醒时间,now 小于 mIntendedWakeupTime,所以走到了VSyncDispatchTimerQueueEntry::schedule,如下:

ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
                                                      VSyncTracker& tracker, nsecs_t now) {
    auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
    auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;

    bool const wouldSkipAVsyncTarget =
            mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
    bool const wouldSkipAWakeup =
            mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
            
    if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
        return getExpectedCallbackTime(nextVsyncTime, timing);
    }

    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
    if (alreadyDispatchedForVsync) {
        nextVsyncTime =
                tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
        nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
    }

    auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
    mScheduleTiming = timing;
    mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
    return getExpectedCallbackTime(nextVsyncTime, timing);
}

这个Func的一堆时间戳看的头晕眼花,可以尝试把这些时间戳打在trace上一帧帧查看。真正计算vsync时间戳的Func是nextAnticipatedVSyncTimeFrom,在讲这个函数前需要介绍下vsync计算模型,了解了这个模型,后面再看到nextAnticipatedVSyncTimeFrom就可以只用看输入的时间戳以及对应的输出。

Vsync计算模型

从切帧的角度来看这个模型,当系统发生切帧时,会通过resyncToHardwareVsync->setVsyncPeriod->
setVsyncEnabled 来打开HW Vsync校准SW Vsync到预期的周期,如下

void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
...
    if (display->setDesiredActiveMode(info)) {
        // 让sf强行合成一帧
        scheduleComposite(FrameHint::kNone);

        // Start receiving vsync samples now, so that we can detect a period
        // switch.
        // 开HW Vsync和设置mPeriodTransitioningTo
        mScheduler->resyncToHardwareVsync(true, info.mode->getFps());
        // As we called to set period, we will call to onRefreshRateChangeCompleted once
        // VsyncController model is locked.
        modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated);

        // 更新mode对应的duration、phase
        updatePhaseConfiguration(info.mode->getFps());
        mScheduler->setModeChangePending(true);
    }
    ...
  }
  
 void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate, bool force_resync) {
    {
        ...
    setVsyncPeriod(refreshRate.getPeriodNsecs(), force_resync);
}

void Scheduler::setVsyncPeriod(nsecs_t period, bool force_resync) {
    if (period <= 0) return;

    std::lock_guard<std::mutex> lock(mHWVsyncLock);
    
    mVsyncSchedule->getController().startPeriodTransition(period);
     /* mPrimaryHWVsyncEnabled 代表HW Vsync 是否enable,若关闭或者强行打开则使能,
        告诉display驱动需要校准*/
    if (!mPrimaryHWVsyncEnabled || force_resync) {
        mVsyncSchedule->getTracker().resetModel();
        mSchedulerCallback.setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}

void VSyncReactor::startPeriodTransition(nsecs_t period) {
    ATRACE_INT64("VSR-setPeriod", period);
    std::lock_guard lock(mMutex);
    mLastHwVsync.reset();

    if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) {
      // 如果模型里面的周期与要切换的周期一致,则不需要更多的采样,下一步就是关闭HW Vsync
        endPeriodTransition();
        setIgnorePresentFencesInternal(false);
        mMoreSamplesNeeded = false;
    } else {
        startPeriodTransitionInternal(period);
    }
}

//不一致则会把把period设给mPeriodTransitioningTo,然后需要采样
void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) {
    ATRACE_CALL();
    mPeriodConfirmationInProgress = true;
    mPeriodTransitioningTo = newPeriod;
    mMoreSamplesNeeded = true;
    setIgnorePresentFencesInternal(true);
}

切帧时会设一次scheduleComposite让sf强行合成一次,这一次合成的目的是在setActiveModeInHwcIfNeeded 时把预期的mode传给HWC,这样让display驱动切到预期的mode后就能往上报时间戳来校准SW Vsync。试想如果不强行合成一帧,如果下一帧没有应用transaction的变化,sf不合成,就无法将正确的mode传递给驱动。

void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
  ...
  // 将预期的mode传递给驱动
   const auto status = FTL_FAKE_GUARD(kMainThreadContext,
                                    display->initiateModeChange(*desiredActiveMode,
                                                               constraints, &outTimeline));
  ...
  }

驱动切帧时会上报时间戳给到surfaceflinger进行校准,如下:

void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                        std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
  ... 
    //驱动会传timestamp和vsyncPeriod过来校准
    mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
  ...
  }
  
 void Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                                bool* periodFlushed) {
    bool needsHwVsync = false;
    *periodFlushed = false;
    { // Scope for the lock
        std::lock_guard<std::mutex> lock(mHWVsyncLock);
        if (mPrimaryHWVsyncEnabled) {
        // 加入HW Vsync Timestamp进行校准
            needsHwVsync =
                    mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
                                                                        periodFlushed);
        }
    }

    if (needsHwVsync) {
        // 如果还需要采样,则继续打开HW Vsync
        enableHardwareVsync();
    } else {
        // 如果不需要采样,则关闭
        disableHardwareVsync(false);
    }
}
  
 bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                                       bool* periodFlushed) {
    assert(periodFlushed);

    std::lock_guard lock(mMutex);
    /* 首先判断驱动的周期和设的mPeriodTransitioningTo是否在误差范围内,如果在误差范围内则表示
     驱动已经切到指定的周期 */
    if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
        ATRACE_NAME("VSR: period confirmed");
        if (mPeriodTransitioningTo) {
            // 设置mIdealPeriod为预期的周期
            mTracker.setPeriod(*mPeriodTransitioningTo);
            *periodFlushed = true;
        }

        if (mLastHwVsync) {
            mTracker.addVsyncTimestamp(*mLastHwVsync);
        }
        // addVsyncTimestamp 是vsync模型的核心,将驱动传来的时间戳加进来校准
        mTracker.addVsyncTimestamp(timestamp);
        /* 将 mPeriodConfirmationInProgress设为false,表示已经确认驱动的周期是正确的,接下来
        不会再走periodConfirmed了 */
        endPeriodTransition();
        // 如果收集到的mTimestamps size小于6,则继续进行采样
        mMoreSamplesNeeded = mTracker.needsMoreSamples();
    } else if (mPeriodConfirmationInProgress) {
        ATRACE_NAME("VSR: still confirming period");
        mLastHwVsync = timestamp;
        mMoreSamplesNeeded = true;
        *periodFlushed = false;
    } else {
        ATRACE_NAME("VSR: adding sample");
        *periodFlushed = false;
        // 接下来当不在进行periodConfirmed 时会走”adding sample“ 继续添加时间戳
        mTracker.addVsyncTimestamp(timestamp);
         // 如果收集到的mTimestamps size小于6,则继续进行采样
        mMoreSamplesNeeded = mTracker.needsMoreSamples();
    }

    if (!mMoreSamplesNeeded) {
        setIgnorePresentFencesInternal(false);
    }
    return mMoreSamplesNeeded;
}

可以看到addHwVsyncTimestamp 是先判断驱动的周期是否成功切换为预期值,如果成功,则走"adding sample" 来添加时间戳,如果失败,则需要继续走periodConfirmed判断驱动的周期。
Vsync模型的核心在addVsyncTimestamp体现,Google也是很友好的写了注释来解释这个模型,其实就是简单的一元线性回归,用来确定2个变量之间存在定量关系的统计方法,将两个变量用一条直线近似表示。

bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {    std::lock_guard lock(mMutex);

     /* validate 来判断驱动前后两帧的时间戳差值与mIdealPeriod 的误差。
       正常情况下驱动上传的前后两帧时间戳的差应该与 mIdealPeriod相差不大,如果误差较大,则可能驱动
       产生的时间戳有问题 */
    if (!validate(timestamp)) {
       
       if (mTimestamps.size() < kMinimumSamplesForPrediction) {
           
            mTimestamps.push_back(timestamp);
            clearTimestamps();

        } else if (!mTimestamps.empty()) {
            mKnownTimestamp =
                    std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
        } else {
            mKnownTimestamp = timestamp;
        }
        return false;
    }

    // 如果时间戳有效,则push到mTimestamps
    if (mTimestamps.size() != kHistorySize) {
        mTimestamps.push_back(timestamp);
        mLastTimestampIndex = next(mLastTimestampIndex);
    } else {
        mLastTimestampIndex = next(mLastTimestampIndex);
        mTimestamps[mLastTimestampIndex] = timestamp;
    }

    const size_t numSamples = mTimestamps.size();
    if (numSamples < kMinimumSamplesForPrediction) {
        //如果有效的时间戳小于6,则需要继续增加样本,提前return
        mRateMap[mIdealPeriod] = {mIdealPeriod, 0};
        return true;
    }
    
    // This is a 'simple linear regression' calculation of Y over X, with Y being the
    // vsync timestamps, and X being the ordinal of vsync count.
    // The calculated slope is the vsync period.
    // Formula for reference:
    // Sigma_i: means sum over all timestamps.
    // mean(variable): statistical mean of variable.
    // X: snapped ordinal of the timestamp
    // Y: vsync timestamp
    //
    //         Sigma_i( (X_i - mean(X)) * (Y_i - mean(Y) )
    // slope = -------------------------------------------
    //         Sigma_i ( X_i - mean(X) ) ^ 2
    //
    // intercept = mean(Y) - slope * mean(X)
    //
    /* 当mTimestamps满6个时,使用一元线性回归,将采集到的mTimestamps时间戳当成Y,序号当作X
        计算slope 和 intercept 来满足 Y = slope * X + intercept 的线性关系 
        slope 为直线的斜率即 vsync周期, intercept 为截距 */
    it->second = {anticipatedPeriod, intercept};

    ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
          anticipatedPeriod, intercept);
    return true;
}

可以看到addVsyncTimestamp先通过validate判断驱动前后两帧的时间戳差值与mIdealPeriod 的误差,若为有效的时间戳则加入到mTimestamps里面,收集满6个则采用一元线性回归拟合时间戳与序列号成直线关系,为后面预估vsync时间戳做准备。按照Google的注释,我们用Matlab画个图,我们先收集6个有效的时间戳,如下图:








Matlab程序如下:

x=[1,2,3,4,5,6];
y=[2778939.392000, 2778947.684000, 2778955.976000, 2778964.268000, 2778972.560000, 2778980.853000];
figure
plot(x,y,'r*') %作散点图(制定横纵坐标)
xlabel('x(VSYNC Counter)','fontsize',12)
ylabel('y(TimeStamp)','fontsize',12)
set(gca,'linewidth',2)
%采用最小二乘拟合
Lxx=sum((x-mean(x)).^2)
Lxy=sum((x-mean(x)).*(y-mean(y)));
b1=Lxy/Lxx;
b0=mean(y)-b1*mean(x);
y1=b1*x+b0;
hold on
plot(x,y1,'linewidth',2);
m2=LinearModel.fit(x,y)%函数进行线性回归

最后我们拟合的直线如下图所示,计算出来的斜率为8.29,赋给slope,表示SW vsync 周期



总结一下,Vsync模型先检查驱动的周期以及驱动相邻释放的时间戳的差值是否都在误差范围内,如果都在误差范围内则采样6次有效的时间戳来进行一元线性回归,计算出直线的斜率和截距为后面预估Vsync唤醒时间做好准备。
为啥需要预估,不一直使用驱动上报的时间戳?因为采样6次后会disableHWVsync,剩下的都靠这条直线预估,如果一直打开HWVsync校准,会有功耗问题,相信做过显示服务的同学曾经都被功耗组提过单,说静态桌面下测量电流比对比机高几十ma,部分原因就是HW Vsync不断在上报采样。
掌握了这个模型,我们再来看系统如何根据现在的时间预估下一帧的时间。

nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
    //首先得到模型计算的slope和intercept值
    auto const [slope, intercept] = getVSyncPredictionModelLocked();

    // 当打开HW Vsync时会先清掉之前的时间戳
    if (mTimestamps.empty()) {
        traceInt64If("VSP-mode", 1);
        auto const knownTimestamp = mKnownTimestamp ? *mKnownTimestamp : timePoint;
        auto const numPeriodsOut = ((timePoint - knownTimestamp) / mIdealPeriod) + 1;
        return knownTimestamp + numPeriodsOut * mIdealPeriod;
    }
   /*
      其核心思想是根据传进来的时间计算得到大于传进来的值且距离最近的slope倍数的值
      可能有点拗口,举个例子就知道了,我们简化这些时间戳,比如:
      x=[1,2,3,4,5,6];
      y=[32,48,64,80,96,112];
      当我们传入70时,根据 y = ax+b, b=32, x=(70-32)/16 + 1 = 3 得到 y=3*16+32= 80
      当我们传入100时,根据 y = ax+b, b = 32,x=(100-32)/16 + 1 = 5 得到 y = 5*16 +32 = 112
      也就是说我们输出的值要是slope的倍数,而且是大于传进来的值且距离最近的slope倍数的值
   */
    auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());

    // See b/145667109, the ordinal calculation must take into account the intercept.
    auto const zeroPoint = oldest + intercept;
    auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
    auto const prediction = (ordinalRequest * slope) + intercept + oldest;

    traceInt64If("VSP-mode", 0);
    traceInt64If("VSP-timePoint", timePoint);
    traceInt64If("VSP-prediction", prediction);

    auto const printer = [&, slope = slope, intercept = intercept] {
        std::stringstream str;
        str << "prediction made from: " << timePoint << "prediction: " << prediction << " (+"
            << prediction - timePoint << ") slope: " << slope << " intercept: " << intercept
            << "oldestTS: " << oldest << " ordinal: " << ordinalRequest;
        return str.str();
    };
    LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
                        printer().c_str());
        return prediction;
}

到这里我们就知道了系统是如何预估时间戳了,这也保证了VSYNC-XXX的脉冲间隔都是周期的倍数,后面可以把模型想象成黑盒子,只用管输入和输出就行。
回到VSyncDispatchTimerQueueEntry::schedule里面,这个mToken类型是sf。

ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
                                                      VSyncTracker& tracker, nsecs_t now) {
 /*根据前文所述,把nextAnticipatedVSyncTimeFrom想象成黑盒,输入为now + timing.workDuration + timing.readyDuration
   以60hz为例:
   输出得到nextVsyncTime值,mToken类型是sf,readyDuration = 0
   nextVsyncTime的含义是这帧上屏时间(HW Vsync时间戳)
   nextWakeupTime = nextVsyncTime - workDuration, 含义是预估的消费这一帧SW vsync唤醒的时间
   理解这两个变量很重要
   也表明了,这一帧从sf合成到驱动上屏需要经过1个workDuration时间。 
 */
    auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
    auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;

    bool const wouldSkipAVsyncTarget =
            mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
    bool const wouldSkipAWakeup =
            mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance)));
    if (wouldSkipAVsyncTarget && wouldSkipAWakeup) {
        return getExpectedCallbackTime(nextVsyncTime, timing);
    }

    bool const alreadyDispatchedForVsync = mLastDispatchTime &&
            ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
             (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
    if (alreadyDispatchedForVsync) {
        nextVsyncTime =
                tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
        nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
    }

    auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
    mScheduleTiming = timing;
    //这里更新mArmedInfo的mActualWakeupTime、mActualVsyncTime、mActualReadyTime
    mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
    return getExpectedCallbackTime(nextVsyncTime, timing);
}

这里重在理解参与计算时间戳的含义,不然会一头雾水,这里再强调一遍:
nextVsyncTime的含义是上屏的时间,也就是模拟出了HW Vsync时间,驱动根据HW Vsync来将帧上屏。
nextWakeupTime的含义是预估的消费这一帧SW vsync唤醒的时间,也就是模拟出了SW Vsync时间。
计算完后回到 VSyncDispatchTimerQueue::schedule里面:

ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                 ScheduleTiming scheduleTiming) {
...
/* 如果mActualWakeupTime < mIntendedWakeupTime - (0.5ms) 则走rearmTimerSkippingUpdateFor
    更新唤醒时间
*/
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
            rearmTimerSkippingUpdateFor(now, it);
        }
    }

    return result;
}

mIntendedWakeupTime前面讲过,表示VSYNC-XXX唤醒时间,当这里计算出来的mActualWakeupTime 大于这个mIntendedWakeupTime时,需要以近的时间为主,就不会更新唤醒时间了。到这里scheduleFrame的逻辑就结束了。总结一下就是:根据当前时间依靠Vsync模型计算出预估的HW Vsync和 SW Vsync唤醒时间,如果已经有mIntendedWakeupTime 且唤醒时间大于mIntendedWakeupTime 时就结束,如果小于,则更新唤醒时间。
我们知道vsync是依据定时器实现在指定的时间唤醒,这个就是TimerDispatch 线程,根据epoll机制来监听TimerFd,当定时时间到就会唤醒来执行对应的callback,这部分代码在Timer.cpp里面,感兴趣可以研究下,有了这部分基础,我们看下是哪里设的定时时间。
当我们不清楚代码是怎么跑,状态量很多分不清楚时,就多加点log和trace来看,从现象反向理解原理。以60hz为例,发现走CallbackRepeater的callback时会通过schedule来重新设定唤醒时间。如下图所示:



理解这部分原理需要联系上下3帧来看,当上一帧设定的定时时间到时,会唤醒callback,走如下逻辑:

void VSyncDispatchTimerQueue::timerCallback() {
    struct Invocation {
        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
        nsecs_t vsyncTimestamp;
        nsecs_t wakeupTimestamp;
        nsecs_t deadlineTimestamp;
    };
    std::vector<Invocation> invocations;
    {
        std::lock_guard lock(mMutex);
        auto const now = mTimeKeeper->now();
        mLastTimerCallback = now;
        // 这里mCallbacks size为3,分别为sf,app,appSf,所以会循环3次
        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
            auto& callback = it->second;
            auto const wakeupTime = callback->wakeupTime();
            if (!wakeupTime) {
                /* 当mArmedInfo 为空时则不会把callback放到invocations里面,后面就不会
                执行对应的callback函数 */
                continue;
            }

            auto const readyTime = callback->readyTime();

            auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
            /* 
            当 mArmedInfo的mActualWakeupTime 小于 mIntendedWakeupTime + mTimerSlack + lagAllowance
            则会把mArmedInfo reset掉,然后加入该类型的callback
            */
            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
                callback->executing();
                invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
                                                    *wakeupTime, *readyTime});
            }
        }
        // 将mIntendedWakeupTime  设置成一个很大的无效时间
        mIntendedWakeupTime = kInvalidTime;
        /* 再走一遍rearmTimerSkippingUpdateFor逻辑,如果前面都走了executing(),则cancelTimer
           取消定时 */
        rearmTimer(mTimeKeeper->now());
    }

    for (auto const& invocation : invocations) {
        // 分别执行对应的callback函数
        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
                                      invocation.deadlineTimestamp);
    }
}

/* reset mArmedInfo*/
nsecs_t VSyncDispatchTimerQueueEntry::executing() {
    mLastDispatchTime = mArmedInfo->mActualVsyncTime;
    disarm();
    return *mLastDispatchTime;
}
void VSyncDispatchTimerQueueEntry::disarm() {
    mArmedInfo.reset();
}

/* 取消定时 */
void VSyncDispatchTimerQueue::cancelTimer() {
    mIntendedWakeupTime = kInvalidTime;
    mTimeKeeper->alarmCancel();
}

void Timer::alarmCancel() {
    std::lock_guard lock(mMutex);

    struct itimerspec old_timer;
    struct itimerspec new_timer {
        .it_interval = {.tv_sec = 0, .tv_nsec = 0},
        .it_value = {
                .tv_sec = 0,
                .tv_nsec = 0,
        },
    };

    if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
        ALOGW("Failed to disarm timerfd");
    }
}

sf对应的callback为MessageQueue::vsyncCallback

void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime) {
    // Trace VSYNC-sf
    // 触发一次脉冲,VSYNC-SF 值发生变化
    mVsync.value = (mVsync.value + 1) % 2;

    {
        std::lock_guard lock(mVsync.mutex);
        mVsync.lastCallbackTime = std::chrono::nanoseconds(vsyncTime);
        mVsync.scheduledFrameTime.reset();
    }

    mTargetWakeupTime = targetWakeupTime;

    const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions(
            {targetWakeupTime, readyTime, vsyncTime});

    // 触发SF合成工作开始
    mHandler->dispatchFrame(vsyncId, vsyncTime);
}

app和appSf对应的callback在CallbackRepeater,如下:

void callback(nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) {
        {
            std::lock_guard lock(mMutex);
            mLastCallTime = std::chrono::nanoseconds(vsyncTime);
        }
        // mCallback 为 DispSyncSource::onVsyncCallback
        mCallback(vsyncTime, wakeupTime, readyTime);

        {
            std::lock_guard lock(mMutex);
            if (!mStarted) {
                return;
            }
            //计算下一次的唤醒时间和HW Vsync时间
            auto const scheduleResult =
                    mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                            .readyDuration = mReadyDuration.count(),
                                            .earliestVsync = vsyncTime});
            LOG_ALWAYS_FATAL_IF(!scheduleResult.has_value(), "Error rescheduling callback");
        }
        ...
    }
    
    void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
                                     nsecs_t readyTime) {
    VSyncSource::Callback* callback;
    {
        std::lock_guard lock(mCallbackMutex);
        callback = mCallback;
    }
    // 触发一次脉冲,VSYNC-app 或 VSYNC-appSf 值发生变化
    if (mTraceVsync) {
        mValue = (mValue + 1) % 2;
    }
    if (callback != nullptr) {
        // 执行EventThread::onVSyncEvent产生一个Event
        callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime});
    }
}

总结一下:当上一帧设置定时,定时时间到,然后在回调函数触发VSYNC-sf,VSYNC-app脉冲,sf开始合成工作,app开始绘制渲染工作。接着,app通过schedule重新计算下一帧的唤醒时间和HW Vsync时间,又回到了schedule,不同的是此时mToken为app和一些变量的不同,再看下这个Func:

ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                 ScheduleTiming scheduleTiming) {
    ScheduleResult result;
    {
        std::lock_guard lock(mMutex);

        auto it = mCallbacks.find(token);
        if (it == mCallbacks.end()) {
            return result;
        auto& callback = it->second;
        auto const now = mTimeKeeper->now();

        /* If the timer thread will run soon, we'll apply this work update via the callback
         * timer recalculation to avoid cancelling a callback that is about to fire. */
        auto const rearmImminent = now > mIntendedWakeupTime;
        if (CC_UNLIKELY(rearmImminent)) {
            callback->addPendingWorkloadUpdate(scheduleTiming);
            return getExpectedCallbackTime(mTracker, now, scheduleTiming);
        }
  
        // 前面讲过,这次要更新app的mArmedInfo
        result = callback->schedule(scheduleTiming, mTracker, now);
        if (!result.has_value()) {
            return result;
        }
        /* 还记得当token为sf时,这个条件不满足,但此时因为执行完回调的关系把mIntendedWakeupTime 
        设成一个非常大的无效数,所以满足了该条件, 需要重新设定唤醒时间 
        */
        if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
            rearmTimerSkippingUpdateFor(now, it);
        }
    }

    return result;
}

void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
    std::optional<nsecs_t> min;
    std::optional<nsecs_t> targetVsync;
    std::optional<std::string_view> nextWakeupName;
    // mCallbacks为3,循环3次,对应app,sf,appsf
    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
        auto& callback = it->second;
        /* 如果对应的mArmedInfo为空,则不会给定时器设置,这里sf和appsf都为空,因为
         之前回调时清除了所有的mArmedInfo, 而app又通过 VSyncDispatchTimerQueueEntry::schedule
         重新设置了mArmedInfo,所以只有app能设定时时间 */
        if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
            continue;
        }

        // 120hz会走update,因为sf和app vsync有相位差,这个后面再分析
        if (it != skipUpdateIt) {
            callback->update(mTracker, now);
        }
        // 获取app VSyncDispatchTimerQueueEntry::schedule计算的唤醒时间
        auto const wakeupTime = *callback->wakeupTime();
        if (!min || *min > wakeupTime) {
            nextWakeupName = callback->name();
            min = wakeupTime;
            targetVsync = callback->targetVsync();
        }
    }

    if (min && min < mIntendedWakeupTime) {
        if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
            ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
                              "us; VSYNC in ", ns2us(*targetVsync - now), "us");
            ATRACE_NAME(trace.c_str());
        }
        // 将唤醒时间发送给定时器,终于找到了设置定时时间的地方
        setTimer(*min, now);
    } else {
        cancelTimer();
    }
}

void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
     // 更新mIntendedWakeupTime,无效变有效值
    mIntendedWakeupTime = targetTime;
    // 发送给定时器
    mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
                         mIntendedWakeupTime);
    mLastTimerSchedule = mTimeKeeper->now();
}
// 设置定时时间,记时开始,后面到mIntendedWakeupTime后再次唤醒
void Timer::alarmAt(std::function<void()> callback, nsecs_t time) {
    std::lock_guard lock(mMutex);
    using namespace std::literals;
    static constexpr int ns_per_s =
            std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();

    mCallback = std::move(callback);
    mExpectingCallback = true;

    struct itimerspec old_timer;
    struct itimerspec new_timer {
        .it_interval = {.tv_sec = 0, .tv_nsec = 0},
        .it_value = {.tv_sec = static_cast<long>(time / ns_per_s),
                     .tv_nsec = static_cast<long>(time % ns_per_s)},
    };

    if (timerfd_settime(mTimerFd, TFD_TIMER_ABSTIME, &new_timer, &old_timer)) {
        ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
    }
}

总结一下:上一帧定时,当前帧被唤醒后又开始在app schedule里面设置定时,到下一帧唤醒又开始设置定时,循环反复。还记得前面我们说的sf schedule没有设置定时,到这里就清楚了,因为app schedule是在回调时设置的定时,而sf schedule 需要等应用queueBuffer时才走,这个时间晚于app的schedule,计算出来的wakeupTime略大于mIntendedWakeupTime,所以按照app的schedule计算的唤醒时间来,如图所示:



下面来讲下120hz,有些流程与60hz不同,首先120hz与60hz相比,跟HW Vsync的相位差不同,sf vsync相比HW vsync提前2ms,app vsync相比HW vsync滞后1ms,这也决定了sf和app是分开定时的。

           app phase:      1000000 ns                 SF phase:     -2000000 ns
           app duration:  13666666 ns                 SF duration:  10333333 ns
     early app phase:      1000000 ns           early SF phase:     -2000000 ns
     early app duration:  13666666 ns           early SF duration:  10333333 ns
  GL early app phase:      1000000 ns        GL early SF phase:     -2000000 ns
  GL early app duration:  13666666 ns        GL early SF duration:  10333333 ns
       HWC min duration:         0 ns
      present offset:         0 ns             VSYNC period:   8333333 ns

当触发app回调时,依然从timerCallback进行分析:

void VSyncDispatchTimerQueue::timerCallback() {
      ... 
        // 这里mCallbacks size为3,分别为sf,app,appSf,所以会循环3次
        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
            auto& callback = it->second;
            auto const wakeupTime = callback->wakeupTime();
            if (!wakeupTime) {
                continue;
            }

            auto const readyTime = callback->readyTime();

            auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
            /* 
              120hz时sf的wakeupTime > mIntendedWakeupTime,所以不会执行sf的callback
            */
            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
                callback->executing();
                invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
                                                    *wakeupTime, *readyTime});
            }
        }
        // 将mIntendedWakeupTime  设置成一个很大的无效时间
        mIntendedWakeupTime = kInvalidTime;
        /* 
          120hz sf的mArmedInfo 不会致空,会走到update里面
         */
        rearmTimer(mTimeKeeper->now());
    }

    for (auto const& invocation : invocations) {
        // 120hz 这里只执行app的callback
        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
                                      invocation.deadlineTimestamp);
    }
}


void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
    std::optional<nsecs_t> min;
    std::optional<nsecs_t> targetVsync;
    std::optional<std::string_view> nextWakeupName;
    // mCallbacks为3,循环3次,对应app,sf,appsf
    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
        auto& callback = it->second;
         // 120hz sf没有reset mArmedInfo,继续往下执行
        if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
            continue;
        }

        // 120hz sf会update,重新预估唤醒时间
        if (it != skipUpdateIt) {
            callback->update(mTracker, now);
        }
       
        auto const wakeupTime = *callback->wakeupTime();
        if (!min || *min > wakeupTime) {
            nextWakeupName = callback->name();
            min = wakeupTime;
            targetVsync = callback->targetVsync();
        }
    }

    if (min && min < mIntendedWakeupTime) {
        if (ATRACE_ENABLED() && nextWakeupName && targetVsync) {
            ftl::Concat trace(ftl::truncated<5>(*nextWakeupName), " alarm in ", ns2us(*min - now),
                              "us; VSYNC in ", ns2us(*targetVsync - now), "us");
            ATRACE_NAME(trace.c_str());
        }
        // 120hz 将sf唤醒时间发送给定时器
        setTimer(*min, now);
    } else {
        cancelTimer();
    }
}

/* update依然执行nextAnticipatedVSyncTimeFrom来计算唤醒时间,此时计算的时间是vsync-sf下一次唤醒
 时间 */
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
    if (!mArmedInfo && !mWorkloadUpdateInfo) {
        return;
    }

    if (mWorkloadUpdateInfo) {
        mScheduleTiming = *mWorkloadUpdateInfo;
        mWorkloadUpdateInfo.reset();
    }

    const auto earliestReadyBy = now + mScheduleTiming.workDuration + mScheduleTiming.readyDuration;
    const auto earliestVsync = std::max(earliestReadyBy, mScheduleTiming.earliestVsync);

    const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(earliestVsync);
    const auto nextReadyTime = nextVsyncTime - mScheduleTiming.readyDuration;
    const auto nextWakeupTime = nextReadyTime - mScheduleTiming.workDuration;
    
    mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
}

当sf回调时,状态与app时相反,timerCallback时app的wakeupTime > mIntendedWakeupTime,所以不会执行app的callback,app会走update计算下一次的VSYNC-app唤醒时间,然后设给定时器



总结一下:120hz sf和app是分开设置定时,app回调时sf的wakeupTime > mIntendedWakeupTime,故需要update sf的唤醒时间并设给定时器,sf回调时app的wakeupTime > mIntendedWakeupTime,故需要update app的唤醒时间并设给定时器,两者交替进行。如下图所示是走app回调,sf回调类似,就不上图了。



分析到这里,我们就能回答开篇提的问题 "vsync是如何有序控制sf合成和app绘制的节奏?"
即根据vsync模型预估出大于传入值且距离最近的周期的倍数来设置定时器唤醒sf和app的回调,从而能让sf和app以稳定的周期间隔进行合成和绘制。
相信大家都曾看到过这样的trace,sf有一帧的周期变成了32ms,为什么这里没有VSYNC-sf信号产生,可能有经验的同学都知道是因为应用queueBuffer晚了或丢了一帧,确实如此,那代码逻辑是怎样的呢?

还是得从timerCallback说起,因为从上一帧定时到这一帧唤醒期间,sf都没有更新mArmedInfo,还记得我们在上一帧执行callback时会清空mArmedInfo,所以如果不更新mArmedInfo则不会将sf的callback放到invocations里面,自然也就不会触发VSYNC-sf,而sf 更新mArmedInfo是通过scheduleFrame,所以应用如果没有queueBuffer则不会更新sf的mArmedInfo,所以也就不会触发VSYNC-sf。
另外一种情况大家也都遇到过,即app有一帧周期变长了,为什么这里没有VSYNC-app信号产生,有经验的同学也会想到因为应用没有requestVsync,那代码逻辑是怎么样的呢?



还是得从上一帧的timerCallback说起,当app回调时会走onVsyncCallback,继而会生成一个Vsync Event,并唤醒app的eventThread线程工作,代码如下:

void EventThread::threadMain(std::unique_lock<std::mutex>& lock) {
    DisplayEventConsumers consumers;

    while (mState != State::Quit) {
        std::optional<DisplayEventReceiver::Event> event;

        // Determine next event to dispatch.
        // 当makeVsync时mPendingEvents不为空,取出该event
        if (!mPendingEvents.empty()) {
            event = mPendingEvents.front();
            mPendingEvents.pop_front();
            
        ...
      
        bool vsyncRequested = false;

        // Find connections that should consume this event.
        auto it = mDisplayEventConnections.begin();
        while (it != mDisplayEventConnections.end()) {
            if (const auto connection = it->promote()) {
               // 是否有vsync请求
                vsyncRequested |= connection->vsyncRequest != VSyncRequest::None;
               // 如果有event和vsync请求,则会走后面dispatchEvent逻辑分发event给app
                if (event && shouldConsumeEvent(*event, connection)) {
                    consumers.push_back(connection);
                }

                ++it;
            } else {
                it = mDisplayEventConnections.erase(it);
            }
        }

        if (!consumers.empty()) {
           //分发Event给app,后续app就会做开篇说的doFrame等一套逻辑
            dispatchEvent(*event, consumers);
            consumers.clear();
        }

        State nextState;
        /*
        如果有vsync请求,则更新nextState,如果没有,则设成idle
        */
        if (mVSyncState && vsyncRequested) {
            nextState = mVSyncState->synthetic ? State::SyntheticVSync : State::VSync;
        } else {
            ALOGW_IF(!mVSyncState, "Ignoring VSYNC request while display is disconnected");
            nextState = State::Idle;
        }

        /*
          当nextState  状态变化时,会更新到mState
          1. 当之前的mState为State::VSync,nextState 为State::Idle,即走setVSyncEnabled(false)
             这种情况简单理解为前一次有vsync请求,这一次没有
          2. 当之前的mState为State::Idle, nextState 为State::VSync,即走setVSyncEnabled(true)
             这种情况简单理解为前一次没有vsync请求,这一次有了
        */
        if (mState != nextState) {
            if (mState == State::VSync) {
                mVSyncSource->setVSyncEnabled(false);
            } else if (nextState == State::VSync) {
                mVSyncSource->setVSyncEnabled(true);
            }

            mState = nextState;
        }

        if (event) {
         //如果有event则继续下一次循环
            continue;
        }

        // Wait for event or client registration/request.
        // 如果mState 为idle则等待唤醒,线程处于sleep状态
        if (mState == State::Idle) {
            mCondition.wait(lock);
        } else {
            ...
        }
   }
   
  void DispSyncSource::setVSyncEnabled(bool enable) {
    std::lock_guard lock(mVsyncMutex);
    //如果enable,即走app的schedule开始计算唤醒时间
    if (enable) {
        mCallbackRepeater->start(mWorkDuration, mReadyDuration);
    } else {
    //如果false,则走cancelTimer取消定时
        mCallbackRepeater->stop();
    }
    mEnabled = enable;
}
void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
        std::lock_guard lock(mMutex);
        mStarted = true;
        mWorkDuration = workDuration;
        mReadyDuration = readyDuration;
        //前一次没有vsync请求,这一次有了,则开始计算唤醒时间
        auto const scheduleResult =
                mRegistration.schedule({.workDuration = mWorkDuration.count(),
                                        .readyDuration = mReadyDuration.count(),
                                        .earliestVsync = mLastCallTime.count()});
        LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
    }

    void stop() {
        std::lock_guard lock(mMutex);
        LOG_ALWAYS_FATAL_IF(!mStarted, "DispSyncInterface misuse: callback already stopped");
        mStarted = false;
        // 前一次有vsync请求,这一次没有,则取消定时
        mRegistration.cancel();
    }

CancelResult VSyncDispatchTimerQueue::cancel(CallbackToken token) {
    std::lock_guard lock(mMutex);

    auto it = mCallbacks.find(token);
    if (it == mCallbacks.end()) {
        return CancelResult::Error;
    }
    auto& callback = it->second;

    auto const wakeupTime = callback->wakeupTime();
    if (wakeupTime) {
       //清除mArmedInfo
        callback->disarm();

        if (*wakeupTime == mIntendedWakeupTime) {
            mIntendedWakeupTime = kInvalidTime;
            //会走rearmTimerSkippingUpdateFor,前面已经讲过这个方法,走cancelTimer取消定时
            rearmTimer(mTimeKeeper->now());
        }
        return CancelResult::Cancelled;
    }
    return CancelResult::TooLate;
}

总结一下,当没有vsync请求时,app通过一系列逻辑(见注释)来取消定时,让TimerDispatch 处于 sleep状态,trace也能佐证,所以后面的VSYNC-sf和VSYNC-app都没产生


至此,我们对surfaceflinger内部的vsync机制有了清晰的认识。阅读完本篇文章,希望帮助大家理解如下几个原理:

  1. 通过HW Vsync采样来对SW Vsync校准的时机和流程
  2. SW Vsync计算模型
  3. 通过设定唤醒时间来触发VSYNC-xxx脉冲让app,sf进行工作
  4. app,sf唤醒时间的计算
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容