Andoid SurfaceFlinger(三) VSYNC的申请,生产,下发与校准

上文中不断提及一个概念,即app和surfaceFlinger直接使用的vsync(sf-vsync,app-vsync,appsf-vsync),并不是硬件vsync,而是在硬件vsync的基础上模拟出来一个x为vsync的序号,y为vsync时间点的一个一次函数(y = k*x + b)的软件vsync模型,surfaceflinger和app在申请所需的vsync时会通过此模型,再加上一些偏移, 计算出vsync时间,等到vsync时间到,下发一个vsync信号。

在这里插入图片描述

一. VSYNC的申请

上一篇中提及到,vsync申请过程中会调用到DispSyncSource.start(),随后调用到mRegistration.schedule(),传入的参数依次是海中vsync的workDuration,readyDuration,earliestVsync,前两者的概念上文中已经解释过,不再赘述,而earliestVsync代表者当前种类的vsync的最近一次的下发时间。

services/surfaceflinger/Scheduler/DispSyncSource.cpp

void start(std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) {
        std::lock_guard lock(mMutex);
        mStarted = true;
        mWorkDuration = workDuration;
        mReadyDuration = readyDuration;
		
		// 把三个时间参数封装到结构体中
        auto const scheduleResult =
                mRegistration.schedule({.workDuration = mWorkDuration.count(), 	
                                        .readyDuration = mReadyDuration.count(),
                                        .earliestVsync = mLastCallTime.count()});  	// earliestVsync指该类型的一次vsync时间
        LOG_ALWAYS_FATAL_IF((!scheduleResult.has_value()), "Error scheduling callback");
    }

二. VSYNC的生产

接上文随后调用到VSyncDispatchTimerQueue::schedule(),参数token代表的是那种类型的vsync,scheduleTiming中保存着上文提到的三个时间点,随后根据token找到对应的vsync类型的callback.

ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token,
                                                 ScheduleTiming scheduleTiming) {
    ScheduleResult result;
    {
        std::lock_guard lock(mMutex);
		// 找到token对应的callback
        auto it = mCallbacks.find(token);
        if (it == mCallbacks.end()) {
            return result;
        }
        auto& callback = it->second;
        auto const now = mTimeKeeper->now();
		.....
		// 根据workduiration,readyduration和now计算出vsync时间。
        result = callback->schedule(scheduleTiming, mTracker, now);
        if (!result.has_value()) {
            return result;
        }
        if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
        	// 根据计算出来vsync时间,设置定时
            rearmTimerSkippingUpdateFor(now, it);
        }
    }

    return result;
}

如下:

  1. 根据timing和now计算出理论上这一帧的上屏时间,以理解为这一帧正真在屏幕上显示时的HW_VSYNC时间,等于当前的时间点 + 该种vsync对应的workduration + 该种vsync对应的readyduration。
  2. 计算出vsync时间 = 理论上屏时间 - workDuration - readyDuration。
  3. 把理论上屏的时间 ,vsync时间, nextReadyTime 封装到 VSyncDispatchTimerQueueEntry.mArmedInfo。
services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp

ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTiming timing,
                                                      VSyncTracker& tracker, nsecs_t now) {
     // 计算理论上屏的时间,可以理解为正真在屏幕上显示时的HW_VSYNC时间
    auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom(
            std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration));
    // vsync时间 = 理论上屏时间 - workDuration - readyDuration
    auto nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration;
	.....
	// 计算出 nextReadyTime = 理论上屏时间 - readyDuration
    auto const nextReadyTime = nextVsyncTime - timing.readyDuration;
    mScheduleTiming = timing;
    // 把 理论上屏的时间 ,vsync时间, nextReadyTime 封装到 VSyncDispatchTimerQueueEntry.mArmedInfo。
    mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime};
    return getExpectedCallbackTime(nextVsyncTime, timing);
}

结构体ArmingInfo如下

struct ArmingInfo {
        nsecs_t mActualWakeupTime;  // vsync时间 
        nsecs_t mActualVsyncTime; 	// 理论上屏的时间
        nsecs_t mActualReadyTime;   // nextReadyTime
    };

关键,根据软件vsync模型计算理论上屏时间:
概述以一下:

  1. 取出当前刷新率下 软件vsync模型的斜率和截距。
  2. 基于当前时间,传入软件vsync模型计算出理论上屏的时间
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFromLocked(nsecs_t timePoint) const {
	// 取出当前刷新率下 软件vsync模型的斜率和截距。
    auto const [slope, intercept] = getVSyncPredictionModelLocked();
	// 如果mTimestamps为空,则说明现在正可能打开了硬件VSYNC,进行校准,则使用截距为0,斜率为mIdealPeriod
	// 的软件VSYNC模型计算出该次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;
    }

    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();
    };

    ALOGV("%s", printer().c_str());
    LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
                        printer().c_str());
	// 返回理论上屏时间
    return prediction;
}

至此 app-vsync/appsf-vsync的申请计算流程,
在这里插入图片描述

三. VSYNC的下发

接下来等待之前设置的vsync时间到,待定时的时间到了,开始进行回调

如下,遍历所有的callback,类型为VSyncDispatchTimerQueueEntry,callback->wakeupTime为上文设置给VSyncDispatchTimerQueueEntry的mArmedInfo,如果该VSyncDispatchTimerQueueEntry没有申请vsync,则对应的mArmedInfo为空。
如下:

  1. 遍历所有VSyncDispatchTimerQueueEntry,找出wakeupTime不为空的VSyncDispatchTimerQueueEntry,这里的wakeupTime就是上文中提到的vsync时间。

  2. 随后找出所有VSyncDispatchTimerQueueEntry中vsync时间最小的值min,随后用最小值min和mIntendedWakeupTime和最比较,mIntendedWakeupTime指的是当前模型中下一次vsync的时间,当前没有请求vsync时,mIntendedWakeupTime是一个无限大的值,故当刚申请app-vsync
    时,min < mIntendedWakeupTime。

  3. 设置定时,min中保存着下一次vsync时间,

services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp

void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor(
        nsecs_t now, CallbackMap::iterator const& skipUpdateIt) {
    // min,用于保存所有VSyncDispatchTimerQueueEntry的vsync时间中最小的那个。
    std::optional<nsecs_t> min;
    std::optional<nsecs_t> targetVsync;
    std::optional<std::string_view> nextWakeupName;
    for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
        auto& callback = it->second;
        if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) {
            continue;
        }

        if (it != skipUpdateIt) {
            callback->update(mTracker, now);
        }
        // callback->wakeupTime() = 上文中计算出的vsync时间 
        // 找出所有VSyncDispatchTimerQueueEntry中vsync时间最小的那个
        auto const wakeupTime = *callback->wakeupTime();
        if (!min || *min > wakeupTime) {
            nextWakeupName = callback->name();
            min = wakeupTime;
            //callback->targetVsync()= VSyncDispatchTimerQueueEntry.mArmedInfo的mActualVsyncTime 
            //即该帧的理论上屏幕时间
            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());
        }
        // 设置定时,min中保存着下一次vsync时间,
        setTimer(*min, now);
    } else {
        ATRACE_NAME("cancel timer");
        cancelTimer();
    }
}

随后调用到VSyncDispatchTimerQueue::setTimer(),

  1. 把mIntendedWakeupTime更新为最新的vsync时间。
  2. 通过mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),mIntendedWakeupTime);,在计时
    线程设置一个定时,该定时的唤醒时间就是上文提到的最新的vsync时间,当定时的时间到达时会执行传入的回调VSyncDispatchTimerQueue::timerCallback。
services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp

void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) {
    mIntendedWakeupTime = targetTime;
    mTimeKeeper->alarmAt(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
                         mIntendedWakeupTime);
    mLastTimerSchedule = mTimeKeeper->now();
}

时间到达后,回调VSyncDispatchTimerQueue::timerCallback,vsync信号至此产生,接下来将把该信号下发给申请vsync的app或者surfaceflinger。

  1. 遍历所有的VSyncDispatchTimerQueueEntry,找出mArmedInfo->mActualWakeupTime不为空的,表明刚才改VSyncDispatchTimerQueueEntry有申请过VSYNC,随后判断该VSyncDispatchTimerQueueEntry的wakeupTime(即vsync时间)是否小于mIntendedWakeupTime ,小于则说明该触发改VSyncDispatchTimerQueueEntry的vsync了,随后嗲用callback->executing();清除该VSyncDispatchTimerQueueEntry在申请vsync时设置的定时信息。随后把VSyncDispatchTimerQueueEntry和vsync事件等封装到结构体Invocation中,最后把Invocation添加到列表中,为后续分发做准备。
  2. 遍历invocations,调用invocation.callback->callback,开始执行回调,向VSyncDispatchTimerQueueEntry分发vsync。将会回调到该种vsync对应的DispSyncSource。
services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp

void VSyncDispatchTimerQueue::timerCallback() {
    struct Invocation {
        std::shared_ptr<VSyncDispatchTimerQueueEntry> callback;
        nsecs_t vsyncTimestamp;  	// 理论上屏时间(可以理解为硬件vsync时间)
        nsecs_t wakeupTimestamp;    //	计算出的软件vsync时间
        nsecs_t deadlineTimestamp;  // 
    };
    std::vector<Invocation> invocations;
    {
        std::lock_guard lock(mMutex);
        auto const now = mTimeKeeper->now();
        mLastTimerCallback = now;
        // 便利所有的callback。类型为VSyncDispatchTimerQueueEntry
        for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
            auto& callback = it->second;
            //  上文可知callback->wakeupTime() = mArmedInfo->mActualWakeupTime,即软件vsync时间
            auto const wakeupTime = callback->wakeupTime();
            // wakeupTime为空代表callback没有申请vsync,跳过
            if (!wakeupTime) {
                continue;
            }
			// 
            auto const readyTime = callback->readyTime();

            auto const lagAllowance = std::max(now - mIntendedWakeupTime, static_cast<nsecs_t>(0));
            // 如果当前VSyncDispatchTimerQueueEntry的vsync时间,就把该回调放入到invocations
            if (*wakeupTime < mIntendedWakeupTime + mTimerSlack + lagAllowance) {
                callback->executing();
                // mLastDispatchTime = mArmedInfo->mActualVsyncTime; 即实际上屏时间
                invocations.emplace_back(Invocation{callback, *callback->lastExecutedVsyncTarget(),
                                                    *wakeupTime, *readyTime});
            }
        }

        mIntendedWakeupTime = kInvalidTime;
        rearmTimer(mTimeKeeper->now());
    }

    for (auto const& invocation : invocations) {
    	// 调用每个  invocation.callback->callback
    	// vsyncTimestamp:该次vsync的理论上屏时间(可以以理解为未来的硬件vsync时间),invocation.wakeupTimestamp:该次软件vsyn时间
    	// invocation.deadlineTimestamp: 该次vsync的vsync事件 - readyDuration
        invocation.callback->callback(invocation.vsyncTimestamp, invocation.wakeupTimestamp,
                                      invocation.deadlineTimestamp);
    }
}


nsecs_t VSyncDispatchTimerQueueEntry::executing() {
	// 将mLastDispatchTime 设置为 这一帧的理论上屏时间
    mLastDispatchTime = mArmedInfo->mActualVsyncTime;
    disarm();
    return *mLastDispatchTime;
}

void VSyncDispatchTimerQueueEntry::disarm() {
    // 重置arminfo
    mArmedInfo.reset();
}

随后会回调到DispSyncSource中:

  1. 修改mValue的值,这里在systrace的体现是一vsync个脉冲信号。
  2. 继续调用callback->onVSyncEvent执行回调,类型为app-sync和appsf-vsync的vsync将会回调到对应的EventThread,sf-vsync将会回调到MessageQueue
native\services\surfaceflinger\Scheduler\DispSyncSource.cpp
// vsyncTime:实际上屏时间(未来的硬件vsync时间)   targetWakeupTime: 软件vsync时间
void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime,
                                     nsecs_t readyTime) {
    VSyncSource::Callback* callback;
    {
        std::lock_guard lock(mCallbackMutex);
        callback = mCallback;
    }

    if (mTraceVsync) {
    	// 在trace上体现出脉冲信号
        mValue = (mValue + 1) % 2;
    }
	// 执行回调,将会回调到EventThread或MessageQueue
    if (callback != nullptr) {
        callback->onVSyncEvent(targetWakeupTime, {vsyncTime, readyTime});
    }
}

以EventThread为例:
将会回调到onVSyncEvent,至此就和上文完成了衔接,不再分析。

void EventThread::onVSyncEvent(nsecs_t timestamp, VSyncSource::VSyncData vsyncData) {
    std::lock_guard<std::mutex> lock(mMutex);

    LOG_FATAL_IF(!mVSyncState);
    mPendingEvents.push_back(makeVSync(mVSyncState->displayId, timestamp, ++mVSyncState->count,
                                       vsyncData.expectedPresentationTime,
                                       vsyncData.deadlineTimestamp));
    mCondition.notify_all();
}

以上过程可以用如下图总结:
在这里插入图片描述

四. VSYNC的校准

上文中提到过,为了避免频繁的打开硬件VSYNC来产生VSYNC信号,故在SurfaceFlinger中除了还维持了一个软件VSYNC信号模型,当app申请app-vsync信号时,不会直接采用硬件VSYNC,而是通过软件VSYNC信号模型,只需要向模型输入需要VSYNC的时间,模型变计算出合适的软件VSYNC时间,随后等到计算出的软件VSYNC时间到了,向应用下发一个app-vsync信号。但是,用模拟出来的VSYNC信号模型随着使用难免产生误差,当误差过大时便打开硬件VSYNC对VSYNC信号模型进行校准。

在这里插入图片描述

1. 打开硬件vsync准备校准

在这里插入图片描述

1.1 app和EventThread建立连接的过程准备校准

如下app和EventThread建立连接的过程中,传入了一个Lamb函数回调。

native\services\surfaceflinger\Scheduler\Scheduler.cpp

sp<EventThreadConnection> Scheduler::createConnectionInternal(
        EventThread* eventThread, ISurfaceComposer::EventRegistrationFlags eventRegistration) {
     // 这里 传入一个lambda函数
    return eventThread->createEventConnection([&] { resync(); }, eventRegistration);
}

随后当建立连接后调用connection->resyncCallback()调用上文的 resync()

native\services\surfaceflinger\Scheduler\EventThread.cpp

void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
    if (connection->resyncCallback) {
        connection->resyncCallback();
    }
	.....
}

如下,并不是每次app建立连接EventThread是都会触发校准,只有上一次校准的时刻大于kIgnoreDelay 才会触发。

native\services\surfaceflinger\Scheduler\Scheduler.cpp

void Scheduler::resync() {
    static constexpr nsecs_t kIgnoreDelay = ms2ns(750);

    const nsecs_t now = systemTime();
    const nsecs_t last = mLastResyncTime.exchange(now);
	// 只有当前的时间比上一次校准时间大于700ms才会,以避免频繁的校准
    if (now - last > kIgnoreDelay) { // kIgnoreDelay = 700ms
        const auto refreshRate = [&] {
            std::scoped_lock lock(mRefreshRateConfigsLock);
            return mRefreshRateConfigs->getActiveMode()->getFps();
        }();
        resyncToHardwareVsync(false, refreshRate);
    }
}
void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate) {
    {
    // 获取当前刷新率对应的周期,并调用setVsyncPeriod
    setVsyncPeriod(refreshRate.getPeriodNsecs());
}

首先把期望的周期设置给HWC,随后打开硬件VSYNC开关

native\services\surfaceflinger\Scheduler\Scheduler.cpp

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

    std::lock_guard<std::mutex> lock(mHWVsyncLock);
    // 把VSYNC周期设置给HWC
    mVsyncSchedule->getController().startPeriodTransition(period);

    if (!mPrimaryHWVsyncEnabled) {
        mVsyncSchedule->getTracker().resetModel();
        // 打开硬件VSYNC开关
        mSchedulerCallback.setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}

跨进程调用到HWC,以打开HW_VSYNC

native\services\surfaceflinger\SurfaceFlinger.cpp

void SurfaceFlinger::setVsyncEnabled(bool enabled) {
    ATRACE_CALL();

    // On main thread to avoid race conditions with display power state.
    static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) {
        mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;

        if (const auto display = getDefaultDisplayDeviceLocked();
            display && display->isPoweredOn()) {
            setHWCVsyncEnabled(display->getPhysicalId(), mHWCVsyncPendingState);
        }
    }));
}

2.2 改变帧率,开始准备校准

void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
    ATRACE_CALL();

    if (!info.mode) {
        ALOGW("requested display mode is null");
        return;
    }
    auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId());
    if (!display) {
        ALOGW("%s: display is no longer valid", __func__);
        return;
    }

    if (display->setDesiredActiveMode(info)) {
        scheduleComposite(FrameHint::kNone);

        // Start receiving vsync samples now, so that we can detect a period
        // switch.
        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);

        updatePhaseConfiguration(info.mode->getFps());
        mScheduler->setModeChangePending(true);
    }
}
void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate) {
    {
        std::lock_guard<std::mutex> lock(mHWVsyncLock);
        if (makeAvailable) {
            mHWVsyncAvailable = makeAvailable;
        } else if (!mHWVsyncAvailable) {
            // Hardware vsync is not currently available, so abort the resync
            // attempt for now
            return;
        }
    }

    setVsyncPeriod(refreshRate.getPeriodNsecs());
}
void SurfaceFlinger::setDesiredActiveMode(const ActiveModeInfo& info) {
    ATRACE_CALL();

    if (!info.mode) {
        ALOGW("requested display mode is null");
        return;
    }
    auto display = getDisplayDeviceLocked(info.mode->getPhysicalDisplayId());
    if (!display) {
        ALOGW("%s: display is no longer valid", __func__);
        return;
    }

    if (display->setDesiredActiveMode(info)) {
        scheduleComposite(FrameHint::kNone);

        // Start receiving vsync samples now, so that we can detect a period
        // switch.
        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);

        updatePhaseConfiguration(info.mode->getFps());
        mScheduler->setModeChangePending(true);
    }
}
void Scheduler::resyncToHardwareVsync(bool makeAvailable, Fps refreshRate) {
    {
        std::lock_guard<std::mutex> lock(mHWVsyncLock);
        if (makeAvailable) {
            mHWVsyncAvailable = makeAvailable;
        } else if (!mHWVsyncAvailable) {
            // Hardware vsync is not currently available, so abort the resync
            // attempt for now
            return;
        }
    }

    setVsyncPeriod(refreshRate.getPeriodNsecs());
}
void Scheduler::setVsyncPeriod(nsecs_t period) {
    if (period <= 0) return;

    std::lock_guard<std::mutex> lock(mHWVsyncLock);
    mVsyncSchedule->getController().startPeriodTransition(period);

    if (!mPrimaryHWVsyncEnabled) {
        mVsyncSchedule->getTracker().resetModel();
        mSchedulerCallback.setVsyncEnabled(true);
        mPrimaryHWVsyncEnabled = true;
    }
}

2.收到硬件vsync,开始进行校准

当打开VSYNC开关后,随后SurfaceFlinger这边就收到了onComposerHalVsync回调,HWC就是通过该回调把硬件VSYNC信号传递给了SurfaceFlinger

native\services\surfaceflinger\SurfaceFlinger.cpp

void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp,
                                        std::optional<hal::VsyncPeriodNanos> vsyncPeriod) {
    .....
    mScheduler->addResyncSample(timestamp, vsyncPeriod, &periodFlushed);
    ....

}

通过HWC的回调添加时间戳,
将会调用到VSyncReactor向VSyncPredictor添加硬件vsync时间戳,如果过程中VSyncPredictor已经有了足够多的时间戳数据,则根据时间戳数据拟合出软件vsync模型,如果不够的话则返回true表明需要更多的时间戳。

  1. 向用到VSyncReactor向VSyncPredictor添加硬件vsync时间戳,并尝试拟合软件vsync模型
  2. 根据needsHwVsync判断拟合软件vsync是否需要更多的硬件vsync,如果需要更多的vsync则继续打开硬件vsync开关,否则关闭硬件vsync。
services/surfaceflinger/Scheduler/Scheduler.cpp

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) {
        	// needsHwVsync 代表是否需要更多的硬件VSYNC信号
            needsHwVsync =
                    mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
                                                                        periodFlushed);
        }
    }
	
    if (needsHwVsync) {
    	// 如果需要更多的硬件VSYNC信号,则继续打开硬件VSYNC开关
        enableHardwareVsync();
    } else {
    	// 如果不需要更多的硬件VSYNC信号,则继续关闭硬件VSYNC开关
        disableHardwareVsync(false);
    }
}

接下来着重分析根据时间戳拟合软件vsync的过程:

  1. 判断时间戳的合法性
  2. 向mTracker传入时间戳,并判断是否需要更多的时间戳。
native\services\surfaceflinger\Scheduler\VSyncReactor.cpp

bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
                                       bool* periodFlushed) {
    if (periodConfirmed(timestamp, hwcVsyncPeriod)) {
        ATRACE_NAME("VSR: period confirmed");
        ....
        // 把硬件VSYNC的时间戳添加给VSyncTracker的子类:VSyncPredictor
        mTracker.addVsyncTimestamp(timestamp);
        endPeriodTransition();
        // 判断拟合出软件VSYNC模型是否需要更多的硬件VSYNC信号来拟合
        mMoreSamplesNeeded = mTracker.needsMoreSamples();
        .....
    return mMoreSamplesNeeded;
}

拟合模型

  1. 检查时间戳的有效性,同时检查时间戳的数量是够达到要求(6个),不满足则返回fasle,以告诉调用者需要更多的时间戳。
  2. 处理时间戳数据,构造出自变量数据集(由时间戳集合的每个元素减去时间戳集合最小值得到),构造出自变量数据集(根据当前vsync周期和自变量数据集得到的),自变量数据集中的数据和因变量数据集中的数据意义对应,代表着平面直角坐标系上的一个个点。
  3. 根据两个数据集,通过最小二乘法拟合出一条直线,得到直线的斜率和截距,这个拟合出来的直线就是软件vsync的模型
  4. 检查啮合出来直线的斜率(代表着软件vsync的周期)和当前刷新率下的理想周期的差值是否在20%之内,是的话则保存拟合出来的vsync模型,否则清空时间戳集合,返回fasle,重新收集数据拟合。
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
    std::lock_guard lock(mMutex);
	// 接续检查timestamp是否有效,无效则返回fasle 告诉调用者需要更多的时间戳
    if (!validate(timestamp)) {
        // VSR could elect to ignore the incongruent timestamp or resetModel(). If ts is ignored,
        // don't insert this ts into mTimestamps ringbuffer. If we are still
        // in the learning phase we should just clear all timestamps and start
        // over.
        if (mTimestamps.size() < kMinimumSamplesForPrediction) {
            // Add the timestamp to mTimestamps before clearing it so we could
            // update mKnownTimestamp based on the new timestamp.
            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;
    } 
	// 时间戳将加入到mTimestamps中,最多能添加20个时间戳   kHistorySize = 20
    if (mTimestamps.size() != kHistorySize) {
        mTimestamps.push_back(timestamp);
        mLastTimestampIndex = next(mLastTimestampIndex);
    } else {
        mLastTimestampIndex = next(mLastTimestampIndex);
        mTimestamps[mLastTimestampIndex] = timestamp;
    }

    const size_t numSamples = mTimestamps.size();
    // kMinimumSamplesForPrediction 用于集合软件vsync模型的时间戳最少需要6个
    // ,数量不够返回fasle,告诉调用者,需要更多的时间戳
    if (numSamples < kMinimumSamplesForPrediction) {
        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)
    //
    std::vector<nsecs_t> vsyncTS(numSamples);
    std::vector<nsecs_t> ordinals(numSamples);

    // Normalizing to the oldest timestamp cuts down on error in calculating the intercept.
    const auto oldestTS = *std::min_element(mTimestamps.begin(), mTimestamps.end());
    auto it = mRateMap.find(mIdealPeriod);
    auto const currentPeriod = it->second.slope;

    // The mean of the ordinals must be precise for the intercept calculation, so scale them up for
    // fixed-point arithmetic.
    constexpr int64_t kScalingFactor = 1000;
	// 构造出数据集 y:硬件vysnc的时间点   x:vsync的序列号
    nsecs_t meanTS = 0;
    nsecs_t meanOrdinal = 0;

    for (size_t i = 0; i < numSamples; i++) {
        traceInt64If("VSP-ts", mTimestamps[i]);
		// 数据集 y = mTimestamps - mTimestamps最小值
        const auto timestamp = mTimestamps[i] - oldestTS;
        vsyncTS[i] = timestamp;
        meanTS += timestamp;
		// 数据集x是根据当前vsync周期和数据集y得到的
        const auto ordinal = (vsyncTS[i] + currentPeriod / 2) / currentPeriod * kScalingFactor;
        ordinals[i] = ordinal;
        meanOrdinal += ordinal;
    }

    meanTS /= numSamples;
    meanOrdinal /= numSamples;

    for (size_t i = 0; i < numSamples; i++) {
        vsyncTS[i] -= meanTS;
        ordinals[i] -= meanOrdinal;
    }

    nsecs_t top = 0;
    nsecs_t bottom = 0;
    for (size_t i = 0; i < numSamples; i++) {
        top += vsyncTS[i] * ordinals[i];
        bottom += ordinals[i] * ordinals[i];
    }

    if (CC_UNLIKELY(bottom == 0)) {
        it->second = {mIdealPeriod, 0};
        clearTimestamps();
        return false;
    }
	// 	拟合出软件vsync模型斜率和周期
    nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;  // 斜率
    nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);  // 截距
	// 计算出vsync模型斜率(=周期) 和 当前频率下理想周期的偏差
    auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;  // 目前设置帧率下的理想周期
    // 如果偏差大于20% 则这次拟合无效,清空clearTimestamps,返回fasle,告诉调用者需要更多的时间戳
    if (percent >= kOutlierTolerancePercent) {
        it->second = {mIdealPeriod, 0};
        clearTimestamps();
        return false;
    }

    traceInt64If("VSP-period", anticipatedPeriod);
    traceInt64If("VSP-intercept", intercept);

    it->second = {anticipatedPeriod, intercept};

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

验证一下拟合过程:
如下打印出了拟合过车内各种的数据集

2024-03-12 13:35:04.813 addVsyncTimestamp vsyncTSResult: [0,17041000,33642000,50507000,67263000,83706000,], ordinalsResult: [0,1000,2000,3000,4000,5000,] kMinimumSamplesForPrediction: 6, kHistorySize: 20 mIdealPeriod:16666667,

拟合结束后打印的软件vsync模型斜率 和截距:

2024-03-12 13:35:04.815 586-694 VSyncPredictor surfaceflinger E addVsyncTimestamp anticipatedPeriod: 16744600, intercept: 165000 kMaxPercent:100, kOutlierTolerancePercent:20

随后把数据刚才的数据集用python的sklearn进行拟合,并绘图(红线为拟合的直线,蓝点为数据集的坐标点)
在这里插入图片描述
打印通过python的sklearn拟合的结果,可见和vsync拟合过程中得到的结果一致
在这里插入图片描述

python的代码如下:

import numpy as np  # 引入numpy模块,用于数组处理
import matplotlib.pyplot as plt  # 引入图表绘制模块
from sklearn.linear_model import LinearRegression  # 用于回归分析

x = np.array([0, 1000, 2000, 3000, 4000, 5000])  # vsync id的数据集
y = np.array([0, 17041000, 33642000, 50507000, 67263000, 83706000])  # vsync时间戳的数据集

plt.plot(x, y, 'ro')  # 在图标上绘制 x,y  数据集的坐标点

model = LinearRegression()  # 创建一个回归分析对象
model.fit(x.reshape(-1, 1), y.reshape(-1, 1))  # 对x和y进行拟合

k = model.coef_[0]  # 获取拟合结果的斜率
b = model.intercept_  # 获取拟合结果的截距

#  打印拟合结果:
print("斜率:" , k)
print("截距:" , b)

#  构造拟合结果  一元一次方程的数据集
x1 = np.linspace(-100, 6000, 50)
y1 = x1 * k + b

plt.plot(x1, y1)  # 绘制拟合结果的直线
plt.show()   # 展示图表

3. 通过PresentFence进行校准

除了上文所说的当app连接到EventTread时,刷新率发生改变时会触发打开硬件vsync来对软件vsync进行校准外,当一帧画面在在屏幕上
完成显示后也会触发校准,校准的时间戳来源是HWC传递给surfaceflinger的PresentFence。SurfaceFlinger在每一帧交换HWC的时候,同时都会从HWC那里得到这一帧的PresentFence,PresentFence的SignalTime可以理解为屏幕硬件vsync的时间。

surfaceflinger合成后期,会从HWC获取到PresentFence,随后开始利用PresentFence的开始对软件vsync模型进行校准。

void SurfaceFlinger::postComposition() {
	....
	if (display && display->isInternal() && display->getPowerMode() == hal::PowerMode::ON &&
        mPreviousPresentFences[0].fenceTime->isValid()) {
        mScheduler->addPresentFence(mPreviousPresentFences[0].fenceTime);  // 把fence的时间信息添加到 vsync  进行 校准
    }
    .....
}

将会调用到VSyncReactor向VSyncPredictor添加硬件FenceTime,如果过程中VSyncPredictor已经有了足够多的时间戳数据,则根据时间戳数据拟合出软件vsync模型,如果不够的话则返回true表明需要更多的时间戳。

native\services\surfaceflinger\Scheduler\Scheduler.cpp

void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) {
    if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) {
        enableHardwareVsync();
    } else {
        disableHardwareVsync(false);
    }
}

使用PresentFence的signalTime进行软件vsync校准:

  1. 检查fence的的状态
  2. 首先处理mUnfiredFences中的fence,mUnfiredFences放的是之前传进该函数,但是还未signal的Fence,如果mUnfiredFences的fence本轮已经Signal,则调用addVsyncTimestamp使用该fence的SignalTime对软件vsync进行校准。
  3. 处理该函数本次传入的fence,如果该fence已经Signal则调用addVsyncTimestamp使用该fence的SignalTime对软件vsync进行校准,否则把该fence添加到mUnfiredFences等下一轮处理。

addVsyncTimestamp的流程上文已经分析过,不再分析。

native\services\surfaceflinger\Scheduler\VSyncReactor.cpp

// return true 意味着要打开硬件HWC进行采集
bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) {
	// fence为空,返回
    if (!fence) {
        return false;
    }
	
	// 获取该fence的signalTime
    nsecs_t const signalTime = fence->getCachedSignalTime();
    // fence的signalTime无效 返回
    if (signalTime == Fence::SIGNAL_TIME_INVALID) {
        return true;
    }

    std::lock_guard lock(mMutex);
    if (mExternalIgnoreFences || mInternalIgnoreFences) {
        return true;
    }

    bool timestampAccepted = true;
    // mUnfiredFences 中收集者之前传递进来但是未signal的fence
    // 遍历mUnfiredFences,找出当前已经signal的fence,如果有效,就调用addVsyncTimestamp
    // 使用fence的signalTime进行校准
    for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
        auto const time = (*it)->getCachedSignalTime();
        if (time == Fence::SIGNAL_TIME_PENDING) {
            it++;
        } else if (time == Fence::SIGNAL_TIME_INVALID) {
            it = mUnfiredFences.erase(it);
        } else {
            timestampAccepted &= mTracker.addVsyncTimestamp(time);

            it = mUnfiredFences.erase(it);
        }
    }
	// 处理当前传进来的fence,如果该fence的signal状态为Fence::SIGNAL_TIME_PENDING,意味着
	// 该fence还没被signal,则把该fence添加进mUnfiredFences,待下一轮处理
    if (signalTime == Fence::SIGNAL_TIME_PENDING) {
        if (mPendingLimit == mUnfiredFences.size()) {
            mUnfiredFences.erase(mUnfiredFences.begin());
        }
        mUnfiredFences.push_back(std::move(fence));
    } else {
    	// 如果该fence已经signal,则使用该fence的signalTime进行校准。
        timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
    }

    if (!timestampAccepted) {
        mMoreSamplesNeeded = true;
        setIgnorePresentFencesInternal(true);
        mPeriodConfirmationInProgress = true;
    }

    return mMoreSamplesNeeded;
}

结束

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值