上文中不断提及一个概念,即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;
}
如下:
- 根据timing和now计算出理论上这一帧的上屏时间,以理解为这一帧正真在屏幕上显示时的HW_VSYNC时间,等于当前的时间点 + 该种vsync对应的workduration + 该种vsync对应的readyduration。
- 计算出vsync时间 = 理论上屏时间 - workDuration - readyDuration。
- 把理论上屏的时间 ,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模型计算理论上屏时间:
概述以一下:
- 取出当前刷新率下 软件vsync模型的斜率和截距。
- 基于当前时间,传入软件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为空。
如下:
-
遍历所有VSyncDispatchTimerQueueEntry,找出wakeupTime不为空的VSyncDispatchTimerQueueEntry,这里的wakeupTime就是上文中提到的vsync时间。
-
随后找出所有VSyncDispatchTimerQueueEntry中vsync时间最小的值min,随后用最小值min和mIntendedWakeupTime和最比较,mIntendedWakeupTime指的是当前模型中下一次vsync的时间,当前没有请求vsync时,mIntendedWakeupTime是一个无限大的值,故当刚申请app-vsync
时,min < mIntendedWakeupTime。 -
设置定时,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(),
- 把mIntendedWakeupTime更新为最新的vsync时间。
- 通过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。
- 遍历所有的VSyncDispatchTimerQueueEntry,找出mArmedInfo->mActualWakeupTime不为空的,表明刚才改VSyncDispatchTimerQueueEntry有申请过VSYNC,随后判断该VSyncDispatchTimerQueueEntry的wakeupTime(即vsync时间)是否小于mIntendedWakeupTime ,小于则说明该触发改VSyncDispatchTimerQueueEntry的vsync了,随后嗲用callback->executing();清除该VSyncDispatchTimerQueueEntry在申请vsync时设置的定时信息。随后把VSyncDispatchTimerQueueEntry和vsync事件等封装到结构体Invocation中,最后把Invocation添加到列表中,为后续分发做准备。
- 遍历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中:
- 修改mValue的值,这里在systrace的体现是一vsync个脉冲信号。
- 继续调用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表明需要更多的时间戳。
- 向用到VSyncReactor向VSyncPredictor添加硬件vsync时间戳,并尝试拟合软件vsync模型
- 根据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的过程:
- 判断时间戳的合法性
- 向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;
}
拟合模型
- 检查时间戳的有效性,同时检查时间戳的数量是够达到要求(6个),不满足则返回fasle,以告诉调用者需要更多的时间戳。
- 处理时间戳数据,构造出自变量数据集(由时间戳集合的每个元素减去时间戳集合最小值得到),构造出自变量数据集(根据当前vsync周期和自变量数据集得到的),自变量数据集中的数据和因变量数据集中的数据意义对应,代表着平面直角坐标系上的一个个点。
- 根据两个数据集,通过最小二乘法拟合出一条直线,得到直线的斜率和截距,这个拟合出来的直线就是软件vsync的模型。
- 检查啮合出来直线的斜率(代表着软件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校准:
- 检查fence的的状态
- 首先处理mUnfiredFences中的fence,mUnfiredFences放的是之前传进该函数,但是还未signal的Fence,如果mUnfiredFences的fence本轮已经Signal,则调用addVsyncTimestamp使用该fence的SignalTime对软件vsync进行校准。
- 处理该函数本次传入的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;
}
结束