vsync的介绍和由来网上介绍的有很多,个人理解vsync是统一app、sf、lcm刷新的步调,就好像人走路,走的快和走的慢。网上介绍都是从宏观的角度分析vsync的原理,但作为底层工作者,还是需要从代码层弄懂它实际工作的原理。
vsync的基础介绍:https://blog.csdn.net/zhaizu/article/details/51882768
vsync分为硬件vsync和软件vsync,硬件vsync可以理解为屏幕的te信号,当hwc通过commit把数据提交给屏侧时,屏会在下个te信号把数据刷出来;软件vsync可以理解为在SurfaceFlinger内部通过一套计算模型模拟硬件vsync。为什么需要在SurfaceFlinger里面搞一套计算模型?试想下,如果没有,那SurfaceFlinger每一帧的刷新都需要接收从屏幕发过来的vsync,中间经过了HWBinder调用,多增加调用就多一份功耗,当然,时间戳也不一定准确,所以在SurfaceFlinger里面搞了一套软件vsync计算模型。
模型的输入为硬件vsync的时间戳:
文件:frameworks/native/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
std::lock_guard<std::mutex> lk(mMutex);
// 先校验硬件te时间戳的有效性,如果无效,则继续从屏幕那边采集
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 (!mTimestamps.empty()) {
mKnownTimestamp =
std::max(timestamp, *std::max_element(mTimestamps.begin(), mTimestamps.end()));
} else {
mKnownTimestamp = timestamp;
}
return false;
}
// 如果硬件te信号有效,则把时间戳放在mTimestamps 队列里面
if (mTimestamps.size() != kHistorySize) {
mTimestamps.push_back(timestamp);
mLastTimestampIndex = next(mLastTimestampIndex);
} else {
mLastTimestampIndex = next(mLastTimestampIndex);
mTimestamps[mLastTimestampIndex] = timestamp;
}
// 如果mTimestamps 的size小于6,则继续从屏幕那边采集
if (mTimestamps.size() < 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)
// 这部分代码是对输入的6个时间戳做一个线性回归,模拟出一条直线,这条直线的斜率就是vsync的周期,截距是intercept
...
// 模型的输出就是斜率和截距
it->second = {anticipatedPeriod, intercept};
ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp,
anticipatedPeriod, intercept);
return true;
}
文件:frameworks/native/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 =
mPrimaryDispSync->addResyncSample(timestamp, hwcVsyncPeriod, periodFlushed);
}
}
if (needsHwVsync) {
enableHardwareVsync();
} else {
// 如果不再需要HW vsync,则采样结束,关闭硬件Vsync
disableHardwareVsync(false);
}
}
void Scheduler::disableHardwareVsync(bool makeUnavailable) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
// 通知hwc,关掉硬件vsync
mEventControlThread->setVsyncEnabled(false);
mPrimaryDispSync->endResync();
// 将mPrimaryHWVsyncEnabled 设置为false, 这个是sf侧是否打开hw vsync的标志
mPrimaryHWVsyncEnabled = false;
}
if (makeUnavailable) {
mHWVsyncAvailable = false;
}
}
其实软件Vsync的计算模型就是简单的线性回归,采样6个硬件 te信号,来拟合出Surfaceflinger要跑的vsync周期和截距,这个截距的作用还不是很清楚。采样完毕后,将硬件Vsync关闭。
接下来看下,SurfaceFlinger是如何利用模型的输出值计算下一个vsync的时间戳。
从trace来看,TimerDispatch线程会执行vsync的callback来创建vsync event,app拿这个vsync event唤醒app的EventThread去给应用绘制,sf拿这个vsync event唤醒sf的EventThread去刷帧,之后再计算下一个vsync的时间戳,重复如此。代码从这里切入进来看:
文件:frameworks/native/services/surfaceflinger/Scheduler/VSyncReactor.cpp
void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
{
std::lock_guard<std::mutex> lk(mMutex);
mLastCallTime = vsynctime;
}
// mCallback 是DispSyncSource对象
mCallback->onDispSyncEvent(wakeupTime, vsynctime);
{
std::lock_guard<std::mutex> lk(mMutex);
if (mStopped) {
return;
}
// 计算下一个vsync时间戳
auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
"Error rescheduling callback: rc %X", schedule_result);
}
}
文件:frameworks/native/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
ScheduleResult VSyncDispatchTimerQueue::schedule(CallbackToken token, nsecs_t workDuration,
nsecs_t earliestVsync) {
...
// 这里的callback为VSyncDispatchTimerQueueEntry 对象
result = callback->schedule(workDuration, earliestVsync, mTracker, now);
if (result == ScheduleResult::CannotSchedule) {
return result;
}
if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
return result;
}
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
VSyncTracker& tracker, nsecs_t now) {
// 计算下一个vsync的时间戳
auto nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
...
// 下一个唤醒时间是下一个vsync时间-workDuration, workDuration = Vsync period - offset
auto const nextWakeupTime = nextVsyncTime - workDuration;
mWorkDuration = workDuration;
mEarliestVsync = earliestVsync;
// 更新mActualWakeupTime 和 mActualVsyncTime 值
mArmedInfo = {nextWakeupTime, nextVsyncTime};
return ScheduleResult::Scheduled;
}
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;
for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) {
...
auto const wakeupTime = *callback->wakeupTime();
if (!min || (min && *min > wakeupTime)) {
// 将唤醒时间设给min
nextWakeupName = callback->name();
min = wakeupTime;
targetVsync = callback->targetVsync();
}
}
if (min && (min < mIntendedWakeupTime)) {
if (targetVsync && nextWakeupName) {
mTraceBuffer.note(*nextWakeupName, *min - now, *targetVsync - now);
}
// 给定时器输入时间
setTimer(*min, now);
} else {
ATRACE_NAME("cancel timer");
cancelTimer();
}
}
void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t now) {
mIntendedWakeupTime = targetTime;
// 定时器定时的时间是 mActualWakeupTime - now
mTimeKeeper->alarmIn(std::bind(&VSyncDispatchTimerQueue::timerCallback, this),
targetTime - now);
mLastTimerSchedule = mTimeKeeper->now();
}
文件:frameworks/native/services/surfaceflinger/Scheduler/Timer.cpp
void Timer::alarmIn(std::function<void()> const& cb, nsecs_t fireIn) {
...
// 把callback带进来
mCallback = cb;
// 时间单位转换
struct itimerspec old_timer;
struct itimerspec new_timer {
.it_interval = {.tv_sec = 0, .tv_nsec = 0},
.it_value = {.tv_sec = static_cast<long>(fireIn / ns_per_s),
.tv_nsec = static_cast<long>(fireIn % ns_per_s)},
};
// 在Timer::reset 时创建了一个 mTimerFd,可以理解为创建了一个定时器,然后设置了定时的时间
if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
}
}
利用vsync模型值的函数在nextAnticipatedVSyncTimeFrom 逻辑里面,这个函数也是计算下一个vsync时间戳的最主要的函数,来看下
文件: frameworks/native/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const {
std::lock_guard<std::mutex> lk(mMutex);
//从vsync 计算模型获得值,slope表示计算出来的vsync period,intercept表示截距
auto const [slope, intercept] = getVSyncPredictionModel(lk);
...
// 从mTimestamps 时间戳拿最小的一个时间
auto const oldest = *std::min_element(mTimestamps.begin(), mTimestamps.end());
// See b/145667109, the ordinal calculation must take into account the intercept.
// 这套算式大致理解为 prediction 约等于 timepoint + slope, 为什么是约等于,因为取余运算是个陷阱
auto const zeroPoint = oldest + intercept;
auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
auto const prediction = (ordinalRequest * slope) + intercept + oldest;
...
return prediction;
}
对于这个运算,其实可以这么简单理解,now + workDuration + slope 约等于 nextVsyncTime , 而 wakeuptime = nextVsyncTime - workDuration,所以给定时器设置的时间就是 slope,也就是过一个vsync 周期,回调一次。