以下分析基于Android R.
简述 上一章我们分析了SurfaceFlinger是如何根据Framework传入的帧率参数选择合适帧率的。
接来下我们详细看看SurfaceFlinger是如何通知硬件切换帧率的。
一. SurfaceFlinger接受帧率变化 接上一章,从 SurfaceFlinger::setDesiredActiveConfig 开始.
这里的ActiveConfigInfo就是SurfaceFlinger根据Framework传入的帧率范围以及各个Layer投票计算的最终帧率信息。
1 2 3 4 5 6 7 8 struct ActiveConfigInfo { HwcConfigIndexType configId; Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None; bool operator !=(const ActiveConfigInfo& other) const { return configId != other.configId || event != other.event; } };
1.1 SurfaceFlinger.setDesiredActiveConfig 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 void SurfaceFlinger::setDesiredActiveConfig (const ActiveConfigInfo& info) { ATRACE_CALL (); auto & refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId (info.configId); mVsyncPeriod = refreshRate.getVsyncPeriod (); ALOGV ("setDesiredActiveConfig(%s)" , refreshRate.getName ().c_str ()); std::lock_guard<std::mutex> lock (mActiveConfigLock) ; if (mDesiredActiveConfigChanged) { const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event; mDesiredActiveConfig = info; mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig; } else { const auto display = getDefaultDisplayDeviceLocked (); if (!display || display->getActiveConfig () == refreshRate.getConfigId ()) { return ; } mDesiredActiveConfigChanged = true ; mDesiredActiveConfig = info; repaintEverythingForHWC (); mScheduler->resyncToHardwareVsync (true , refreshRate.getVsyncPeriod ()); mVSyncModulator->onRefreshRateChangeInitiated (); mPhaseConfiguration->setRefreshRateFps (refreshRate.getFps ()); mVSyncModulator->setPhaseOffsets (mPhaseConfiguration->getCurrentOffsets ()); mScheduler->setConfigChangePending (true ); } if (mRefreshRateOverlay) { mRefreshRateOverlay->changeRefreshRate (refreshRate); } }
1.2 SurfaceFlinger.repaintEverythingForHWC 1 2 3 4 5 6 7 8 void SurfaceFlinger::repaintEverythingForHWC () { mRepaintEverything = true ; mPowerAdvisor.notifyDisplayUpdateImminent (); mEventQueue->invalidate (); }
1.3 Scheduler.resyncToHardwareVsync 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void Scheduler::resyncToHardwareVsync (bool makeAvailable, nsecs_t period) { { std::lock_guard<std::mutex> lock (mHWVsyncLock) ; if (makeAvailable) { mHWVsyncAvailable = makeAvailable; } else if (!mHWVsyncAvailable) { return ; } } if (period <= 0 ) { return ; } setVsyncPeriod (period); }
1.3.1 Scheduler.setVsyncPeriod 1 2 3 4 5 6 7 8 9 10 11 12 void Scheduler::setVsyncPeriod (nsecs_t period) { std::lock_guard<std::mutex> lock (mHWVsyncLock) ; mPrimaryDispSync->setPeriod (period); if (!mPrimaryHWVsyncEnabled) { mPrimaryDispSync->beginResync (); mEventControlThread->setVsyncEnabled (true ); mPrimaryHWVsyncEnabled = true ; } }
现在R上软件Vsync产生更改了架构,由Q上DispSync改成VSyncReactor, 不过原理不变就不分析了, 具体可以参考文章-SurfaceFlinger(2)–DispSync。
可以通过将属性debug.sf.vsync_reactor置为false后重启,切回Q上的DispSync机制
1.3.2 VSyncReactor.setPeriod 1 2 3 4 5 6 7 8 9 10 11 12 13 14 void VSyncReactor::setPeriod (nsecs_t period) { ATRACE_INT64 ("VSR-setPeriod" , period); std::lock_guard lk (mMutex) ; mLastHwVsync.reset (); if (!mSupportKernelIdleTimer && period == getPeriod ()) { endPeriodTransition (); } else { startPeriodTransition (period); } }
1.3.3 VSyncReactor.startPeriodTransition 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void VSyncReactor::startPeriodTransition (nsecs_t newPeriod) { mPeriodConfirmationInProgress = true ; mPeriodTransitioningTo = newPeriod; mMoreSamplesNeeded = true ; setIgnorePresentFencesInternal (true ); } void VSyncReactor::setIgnorePresentFencesInternal (bool ignoration) { mInternalIgnoreFences = ignoration; updateIgnorePresentFencesInternal (); } void VSyncReactor::updateIgnorePresentFencesInternal () { if (mExternalIgnoreFences || mInternalIgnoreFences) { mUnfiredFences.clear (); } }
1.4 VSyncModulator.onRefreshRateChangeInitiated 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 void VSyncModulator::onRefreshRateChangeInitiated () { if (mRefreshRateChangePending) { return ; } mRefreshRateChangePending = true ; updateOffsets (); } void VSyncModulator::updateOffsets () { std::lock_guard<std::mutex> lock (mMutex) ; updateOffsetsLocked (); } void VSyncModulator::updateOffsetsLocked () { const Offsets& offsets = getNextOffsets (); mPhaseOffsetControl.setPhaseOffset (mSfConnectionHandle, offsets.sf); mPhaseOffsetControl.setPhaseOffset (mAppConnectionHandle, offsets.app); mOffsets = offsets; if (!mTraceDetailedInfo) { return ; } const bool isEarly = &offsets == &mOffsetsConfig.early; const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl; const bool isLate = &offsets == &mOffsetsConfig.late; ATRACE_INT ("Vsync-EarlyOffsetsOn" , isEarly); ATRACE_INT ("Vsync-EarlyGLOffsetsOn" , isEarlyGl); ATRACE_INT ("Vsync-LateOffsetsOn" , isLate); }
1.4.1 VSyncModulator.getNextOffsets 1 2 3 4 5 6 7 8 9 10 11 const VSyncModulator::Offsets& VSyncModulator::getNextOffsets () const { if (mExplicitEarlyWakeup || mTransactionStart == Scheduler::TransactionStart::EarlyEnd || mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) { return mOffsetsConfig.early; } else if (mRemainingRenderEngineUsageCount > 0 ) { return mOffsetsConfig.earlyGl; } else { return mOffsetsConfig.late; } }
1.4.2 DispSyncSource.setPhaseOffset 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void DispSyncSource::setPhaseOffset (nsecs_t phaseOffset) { std::lock_guard lock (mVsyncMutex) ; const nsecs_t period = mDispSync->getPeriod (); const int numPeriods = phaseOffset / period; phaseOffset -= numPeriods * period; if (mPhaseOffset == phaseOffset) { return ; } mPhaseOffset = phaseOffset; if (!mEnabled) { return ; } status_t err = mDispSync->changePhaseOffset (static_cast <DispSync::Callback*>(this ), mPhaseOffset); if (err != NO_ERROR) { ALOGE ("error changing vsync offset: %s (%d)" , strerror (-err), err); } }
1.4.3 VSyncReactor.changePhaseOffset 1 2 3 4 5 6 7 8 9 status_t VSyncReactor::changePhaseOffset (DispSync::Callback* callback, nsecs_t phase) { std::lock_guard<std::mutex> lk (mMutex) ; auto const it = mCallbacks.find (callback); LOG_ALWAYS_FATAL_IF (it == mCallbacks.end (), "callback was %p not registered" , callback); it->second->start (phase); return NO_ERROR; }
1.4.4 VSyncReactor.CallbackRepeater.start 1 2 3 4 5 6 7 8 9 void start (nsecs_t offset) { std::lock_guard<std::mutex> lk (mMutex) ; mStopped = false ; mOffset = offset; auto const schedule_result = mRegistration.schedule (calculateWorkload (), mLastCallTime); LOG_ALWAYS_FATAL_IF ((schedule_result != ScheduleResult::Scheduled), "Error scheduling callback: rc %X" , schedule_result); }
1.4.5 VSyncCallbackRegistration.schedule 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 ScheduleResult VSyncCallbackRegistration::schedule (nsecs_t workDuration, nsecs_t earliestVsync) { if (!mValidToken) { return ScheduleResult::Error; } return mDispatch.get ().schedule (mToken, workDuration, earliestVsync); } ScheduleResult VSyncDispatchTimerQueue::schedule (CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) { auto result = ScheduleResult::Error; { std::lock_guard<decltype (mMutex) > lk (mMutex) ; auto it = mCallbacks.find (token); if (it == mCallbacks.end ()) { return result; } auto & callback = it->second; auto const now = mTimeKeeper->now (); auto const rearmImminent = now > mIntendedWakeupTime; if (CC_UNLIKELY (rearmImminent)) { callback->addPendingWorkloadUpdate (workDuration, earliestVsync); return ScheduleResult::Scheduled; } 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) { auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom (std::max (earliestVsync, now + workDuration)); bool const wouldSkipAVsyncTarget = mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance)); if (wouldSkipAVsyncTarget) { return ScheduleResult::Scheduled; } bool const alreadyDispatchedForVsync = mLastDispatchTime && ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime && (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime); if (alreadyDispatchedForVsync) { nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom (*mLastDispatchTime + mMinVsyncDistance); } auto const nextWakeupTime = nextVsyncTime - workDuration; mWorkDuration = workDuration; mEarliestVsync = earliestVsync; mArmedInfo = {nextWakeupTime, nextVsyncTime}; return ScheduleResult::Scheduled; }
1.5 VSyncModulator.setPhaseOffsets 1 2 3 4 5 6 void VSyncModulator::setPhaseOffsets (const OffsetsConfig& config) { std::lock_guard<std::mutex> lock (mMutex) ; mOffsetsConfig = config; updateOffsetsLocked (); }
这里传入的OffsetsConfig是通过PhaseOffsets拿到的
1.5.1 PhaseOffsets.getCurrentOffsets 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Offsets getCurrentOffsets () const override { return getOffsetsForRefreshRate (mRefreshRateFps); }PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate (float fps) const { const auto iter = std::find_if (mOffsets.begin (), mOffsets.end (), [&fps](const std::pair<float , Offsets>& candidateFps) { return fpsEqualsWithMargin (fps, candidateFps.first); }); if (iter != mOffsets.end ()) { return iter->second; } ALOGW ("Can't find offset for %.2f fps" , fps); return getPhaseOffsets (fps, static_cast <nsecs_t >(1e9 f / fps)); } PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets (float fps, nsecs_t vsyncPeriod) const { if (fps > 65.0f ) { return getHighFpsOffsets (vsyncPeriod); } else { return getDefaultOffsets (vsyncPeriod); } }
哦吼,到这里setDesiredActiveConfig的流程也算是差不多分析完了,主要做的事情也就下面这些:
触发HWC刷新而不重置空闲计时器。
软件Vsync产生模块记录更新的Vsync周期到mPeriodTransitioningTo中,且开始接收硬件vsync,这可以检测到硬件刷新率切换。
调用onRefreshRateChangeCompleted, 通知更新偏移量
保存即将更新的Fps到mPhaseConfiguration中
再次根据即将更新的Fps拿到的固定偏移量更新偏移量
二. 硬件切换帧率 上面的流程跑完后,实际上硬件帧率在哪儿切换还是没有看到,包括mPeriodTransitioningTo是怎么更新到实际软件Vsync中的呢?
注意到在步骤#1.2中也就是repaintEverythingForHWC会请求下一帧的Vsync,很自然的想法就是实际帧率切换应该是在下一帧到来的时候才开始的。
我们知道SurfaceFlinger接受到Vsync信号后,会调用onMessageInvalidate(Q上是onMessageReceived)方法开始更新、合成Layer。
回顾这个方法,很快就可以找到实际vsync切换在这一块代码中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 if (mSetActiveConfigPending) { if (framePending) { mEventQueue->invalidate (); return ; } mSetActiveConfigPending = false ; ON_MAIN_THREAD (setActiveConfigInternal ()); } { Mutex::Autolock _l(mStateLock); mScheduler->chooseRefreshRateForContent (); } ON_MAIN_THREAD (performSetActiveConfig ());
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 void SurfaceFlinger::performSetActiveConfig () { ATRACE_CALL (); ALOGV ("performSetActiveConfig" ); const auto desiredActiveConfig = getDesiredActiveConfig (); if (!desiredActiveConfig) { return ; } auto & refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId (desiredActiveConfig->configId); ALOGV ("performSetActiveConfig changing active config to %d(%s)" , refreshRate.getConfigId ().value (), refreshRate.getName ().c_str ()); const auto display = getDefaultDisplayDeviceLocked (); if (!display || display->getActiveConfig () == desiredActiveConfig->configId) { desiredActiveConfigChangeDone (); return ; } if (!isDisplayConfigAllowed (desiredActiveConfig->configId)) { desiredActiveConfigChangeDone (); return ; } mUpcomingActiveConfig = *desiredActiveConfig; const auto displayId = display->getId (); LOG_ALWAYS_FATAL_IF (!displayId); ATRACE_INT ("ActiveConfigFPS_HWC" , refreshRate.getFps ()); hal::VsyncPeriodChangeConstraints constraints; constraints.desiredTimeNanos = systemTime (); constraints.seamlessRequired = false ; hal::VsyncPeriodChangeTimeline outTimeline; auto status = getHwComposer ().setActiveConfigWithConstraints (*displayId, mUpcomingActiveConfig.configId.value (), constraints, &outTimeline); if (status != NO_ERROR) { ALOGW ("setActiveConfigWithConstraints failed: %d" , status); return ; } mScheduler->onNewVsyncPeriodChangeTimeline (outTimeline); mSetActiveConfigPending = true ; }
这里做一些合法性判断,最重要的是告诉HWC去更新帧率了。
2.1.1 HWComposer.setActiveConfigWithConstraints 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 status_t HWComposer::setActiveConfigWithConstraints ( DisplayId displayId, size_t configId, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline* outTimeline) { RETURN_IF_INVALID_DISPLAY (displayId, BAD_INDEX); auto & displayData = mDisplayData[displayId]; if (displayData.configMap.count (configId) == 0 ) { LOG_DISPLAY_ERROR (displayId, ("Invalid config " + std::to_string (configId)).c_str ()); return BAD_INDEX; } auto error = displayData.hwcDisplay->setActiveConfigWithConstraints (displayData.configMap[configId], constraints, outTimeline); RETURN_IF_HWC_ERROR (error, displayId, UNKNOWN_ERROR); return NO_ERROR; }
2.1.2 HWC2::impl::Display.setActiveConfigWithConstraints 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Error Display::setActiveConfigWithConstraints ( const std::shared_ptr<const HWC2::Display::Config>& config, const VsyncPeriodChangeConstraints& constraints, VsyncPeriodChangeTimeline* outTimeline) { ALOGV ("[%" PRIu64 "] setActiveConfigWithConstraints" , mId); if (config->getDisplayId () != mId) { ALOGE ("setActiveConfigWithConstraints received config %u for the wrong display %" PRIu64 " (expected %" PRIu64 ")" , config->getId (), config->getDisplayId (), mId); return Error::BAD_CONFIG; } if (isVsyncPeriodSwitchSupported ()) { Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints; hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos; hwc2Constraints.seamlessRequired = constraints.seamlessRequired; Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {}; auto intError = mComposer.setActiveConfigWithConstraints (mId, config->getId (), hwc2Constraints, &vsyncPeriodChangeTimeline); outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos; outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired; outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos; return static_cast <Error>(intError); } }
2.1.3 ComposerHal.setActiveConfigWithConstraints 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 V2_4::Error Composer::setActiveConfigWithConstraints ( Display display, Config config, const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints, VsyncPeriodChangeTimeline* outTimeline) { using Error = V2_4::Error; if (!mClient_2_4) { return Error::UNSUPPORTED; } Error error = kDefaultError_2_4; mClient_2_4->setActiveConfigWithConstraints (display, config, vsyncPeriodChangeConstraints, [&](const auto & tmpError, const auto & tmpTimeline) { error = tmpError; if (error != Error::NONE) { return ; } *outTimeline = tmpTimeline; }); return error; }
到这里HWC切换帧率已经完成了。
2.2 SurfaceFlinger.setActiveConfigInternal 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 void SurfaceFlinger::setActiveConfigInternal () { ATRACE_CALL (); const auto display = getDefaultDisplayDeviceLocked (); if (!display) { return ; } auto & oldRefreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId (display->getActiveConfig ()); std::lock_guard<std::mutex> lock (mActiveConfigLock) ; mRefreshRateConfigs->setCurrentConfigId (mUpcomingActiveConfig.configId); mRefreshRateStats->setConfigMode (mUpcomingActiveConfig.configId); display->setActiveConfig (mUpcomingActiveConfig.configId); auto & refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId (mUpcomingActiveConfig.configId); if (refreshRate.getVsyncPeriod () != oldRefreshRate.getVsyncPeriod ()) { mTimeStats->incrementRefreshRateSwitches (); } mPhaseConfiguration->setRefreshRateFps (refreshRate.getFps ()); mVSyncModulator->setPhaseOffsets (mPhaseConfiguration->getCurrentOffsets ()); ATRACE_INT ("ActiveConfigFPS" , refreshRate.getFps ()); if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) { const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId (mUpcomingActiveConfig.configId) .getVsyncPeriod (); mScheduler->onPrimaryDisplayConfigChanged (mAppConnectionHandle, display->getId ()->value, mUpcomingActiveConfig.configId, vsyncPeriod); } }
到此帧率切换的过程,差不多就告一段落,当然这里面还有很对细节的部分。
比如硬件Vsync是怎么影响到VsyncRecator产生软件Vsync的,或者软件Vsync和硬件Vsync是怎么校准的。
不过我们先总结一下:
SurfaceFlinger收到setDesiredDisplayConfigSpecs更新帧率配置后,根据传入的帧率配置以及当前Layer选择一个最佳帧率
将这个最佳帧率信息存储在mDesiredActiveConfig中,然后请求下一帧Vsync,顺便更新一下偏移量
下一帧Vsync到来后,首先根据Layer再次计算一下最佳帧率,然后通知HWC更新帧率,在等待下一帧
第二个Vsync到来后,实际此时硬件HWC的Vsync已经更新了,现在就是同步更新SurfaceFlinger中各个变量中的状态,然后通知给AppEventThread更新
所以一个完整的帧率切换至少包含2个Vsync周期,不过这两个Vsync周期并不相同哦