Android UI架构(十三)

您所在的位置:网站首页 如何切换刷新率和帧率 Android UI架构(十三)

Android UI架构(十三)

2024-07-11 04:03| 来源: 网络整理| 查看: 265

文章目录 参考资料简述一. SurfaceFlinger接受帧率变化1.1 SurfaceFlinger.setDesiredActiveConfig1.2 SurfaceFlinger.repaintEverythingForHWC1.3 Scheduler.resyncToHardwareVsync1.3.1 Scheduler.setVsyncPeriod1.3.2 VSyncReactor.setPeriod1.3.3 VSyncReactor.startPeriodTransition 1.4 VSyncModulator.onRefreshRateChangeInitiated1.4.1 VSyncModulator.getNextOffsets1.4.2 DispSyncSource.setPhaseOffset1.4.3 VSyncReactor.changePhaseOffset1.4.4 VSyncReactor.CallbackRepeater.start1.4.5 VSyncCallbackRegistration.schedule 1.5 VSyncModulator.setPhaseOffsets1.5.1 PhaseOffsets.getCurrentOffsets 二. 硬件切换帧率2.1 SurfaceFlinger.performSetActiveConfig2.1.1 HWComposer.setActiveConfigWithConstraints2.1.2 HWC2::impl::Display.setActiveConfigWithConstraints2.1.3 ComposerHal.setActiveConfigWithConstraints 2.2 SurfaceFlinger.setActiveConfigInternal

参考资料

以下分析基于Android R.

简述

上一章我们分析了SurfaceFlinger是如何根据Framework传入的帧率参数选择合适帧率的。

接来下我们详细看看SurfaceFlinger是如何通知硬件切换帧率的。

一. SurfaceFlinger接受帧率变化

接上一章,从 SurfaceFlinger::setDesiredActiveConfig 开始.

这里的ActiveConfigInfo就是SurfaceFlinger根据Framework传入的帧率范围以及各个Layer投票计算的最终帧率信息。

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 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 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; // 1.2 触发HWC刷新而不重置空闲计时器。 repaintEverythingForHWC(); // 1.3 现在开始接收vsync样本,这可以检测到硬件周期切换。 mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod()); // 1.4 调用onRefreshRateChangeCompleted, 通知更新偏移量 mVSyncModulator->onRefreshRateChangeInitiated(); // 保存即将更新的Fps mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps()); // 1.5 再次更新偏移量, 不过这一次是根据即将更新的Fps拿到的固定偏移量 mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets()); mScheduler->setConfigChangePending(true); } if (mRefreshRateOverlay) { mRefreshRateOverlay->changeRefreshRate(refreshRate); } } 1.2 SurfaceFlinger.repaintEverythingForHWC void SurfaceFlinger::repaintEverythingForHWC() { // 标记全部重绘制 mRepaintEverything = true; // 通知Power模组, Display将更新,OEM厂商自行实现这个标准接口 mPowerAdvisor.notifyDisplayUpdateImminent(); // EventThread请求下一帧Vsync mEventQueue->invalidate(); } 1.3 Scheduler.resyncToHardwareVsync void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) { { std::lock_guard lock(mHWVsyncLock); if (makeAvailable) { mHWVsyncAvailable = makeAvailable; } else if (!mHWVsyncAvailable) { // 硬件Vsync被禁止,直接返回 return; } } if (period setPeriod(period); if (!mPrimaryHWVsyncEnabled) { // 硬件Vsync关闭的情况下, 直接更改软件vsync周期,这里我们加上是开启的 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 void VSyncReactor::setPeriod(nsecs_t period) { ATRACE_INT64("VSR-setPeriod", period); std::lock_guard lk(mMutex); mLastHwVsync.reset(); // mSupportKernelIdleTimer的是由属性控制的,Google源码中默认是true的 // PRODUCT_DEFAULT_PROPERTY_OVERRIDES += ro.surface_flinger.support_kernel_idle_timer=true if (!mSupportKernelIdleTimer && period == getPeriod()) { endPeriodTransition(); } else { // 开始更新 startPeriodTransition(period); } } 1.3.3 VSyncReactor.startPeriodTransition void VSyncReactor::startPeriodTransition(nsecs_t newPeriod) { // 标记各个变量,并记录待更新的Fps对应一帧的刷新时长 mPeriodConfirmationInProgress = true; mPeriodTransitioningTo = newPeriod; mMoreSamplesNeeded = true; // 忽略当前Fence,其实就是清空mUnfiredFences中的fence setIgnorePresentFencesInternal(true); } void VSyncReactor::setIgnorePresentFencesInternal(bool ignoration) { mInternalIgnoreFences = ignoration; updateIgnorePresentFencesInternal(); } void VSyncReactor::updateIgnorePresentFencesInternal() { if (mExternalIgnoreFences || mInternalIgnoreFences) { mUnfiredFences.clear(); } } 1.4 VSyncModulator.onRefreshRateChangeInitiated void VSyncModulator::onRefreshRateChangeInitiated() { if (mRefreshRateChangePending) { return; } mRefreshRateChangePending = true; updateOffsets(); } void VSyncModulator::updateOffsets() { std::lock_guard lock(mMutex); updateOffsetsLocked(); } void VSyncModulator::updateOffsetsLocked() { // 1.4.1 选择偏移量 const Offsets& offsets = getNextOffsets(); // 1.4.2 更新对应的偏移量,这个mPhaseOffsetControl其实就是Scheduler mPhaseOffsetControl.setPhaseOffset(mSfConnectionHandle, offsets.sf); mPhaseOffsetControl.setPhaseOffset(mAppConnectionHandle, offsets.app); // 更新偏移量 mOffsets = offsets; // 这个trace的debug开关是由属性: debug.sf.vsync_trace_detailed_info 0/1 决定的 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 const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const { // 如果正在进行刷新率更改,或者最近开始了一个事务,则使用early偏移量。 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 void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { std::lock_guard lock(mVsyncMutex); const nsecs_t period = mDispSync->getPeriod(); // 正常来讲偏移量在 [-period, period) 之间 const int numPeriods = phaseOffset / period; phaseOffset -= numPeriods * period; if (mPhaseOffset == phaseOffset) { return; } mPhaseOffset = phaseOffset; // 尚未使能,就不需要通知给各个listener if (!mEnabled) { return; } // 1.4.3 DispSyncSource是继承了DispSync::Callback的 status_t err = mDispSync->changePhaseOffset(static_cast(this), mPhaseOffset); if (err != NO_ERROR) { ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err); } } 1.4.3 VSyncReactor.changePhaseOffset status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) { std::lock_guard lk(mMutex); auto const it = mCallbacks.find(callback); LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback); // 调用start,更新VSyncDispatchTimerQueue中相关信息 it->second->start(phase); return NO_ERROR; } 1.4.4 VSyncReactor.CallbackRepeater.start void start(nsecs_t offset) { std::lock_guard 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 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 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) second; } // Unknown refresh rate. This might happen if we get a hotplug event for an external display. // In this case just construct the offset. ALOGW("Can't find offset for %.2f fps", fps); return getPhaseOffsets(fps, static_cast(1e9f / 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切换在这一块代码中:

// 首先注意到这个参数默认是false的,也就是说 // 调用setDesiredActiveConfig方法后的第一帧是无法进入该分支的 // 我们先跳过这段代码 if (mSetActiveConfigPending) { if (framePending) { mEventQueue->invalidate(); return; } // 从HWC收到了当前的fence,假设它成功地更新了配置,因此更新SF各个状态 mSetActiveConfigPending = false; // 2.2 更新SurfaceFlinger的状态,此时HWC是已经更新了帧率 ON_MAIN_THREAD(setActiveConfigInternal()); } // ...... { Mutex::Autolock _l(mStateLock); // 因为Layer更新刷新率, 重新来选择刷新率 // 这里涉及到Layer的VoteType、权限等记录,有兴趣自行研究 mScheduler->chooseRefreshRateForContent(); } // 2.1 更新当前帧率设置 ON_MAIN_THREAD(performSetActiveConfig()); // ...... 2.1 SurfaceFlinger.performSetActiveConfig void SurfaceFlinger::performSetActiveConfig() { ATRACE_CALL(); ALOGV("performSetActiveConfig"); // 判断mDesiredActiveConfigChanged是否为true,获取变量mDesiredActiveConfig // 否则返回nullopt, 说明不需要帧率切换 const auto desiredActiveConfig = getDesiredActiveConfig(); if (!desiredActiveConfig) { // 如果不存在需要切换的帧率配置(mDesiredActiveConfig),直接返回 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()); // TODO(b/142753666) use constrains hal::VsyncPeriodChangeConstraints constraints; constraints.desiredTimeNanos = systemTime(); constraints.seamlessRequired = false; // 2.1.1 通知HWC更新帧率 hal::VsyncPeriodChangeTimeline outTimeline; auto status = getHwComposer().setActiveConfigWithConstraints(*displayId, mUpcomingActiveConfig.configId.value(), constraints, &outTimeline); if (status != NO_ERROR) { // setActiveConfigWithConstraints may fail if a hotplug event is just about // to be sent. We just log the error in this case. ALOGW("setActiveConfigWithConstraints failed: %d", status); return; } mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline); // 如果需要,Scheduler将向HWC提交一个空帧,回到onMessageInvalidate中处理 // 也就是在下一帧会处理,距离调用setDesiredActiveConfig就是第二个帧了。 mSetActiveConfigPending = true; }

这里做一些合法性判断,最重要的是告诉HWC去更新帧率了。

2.1.1 HWComposer.setActiveConfigWithConstraints 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; } // hwcDisplay是HWC2::impl::Display,用来描述硬件显示设备的 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 Error Display::setActiveConfigWithConstraints( const std::shared_ptr& 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; } // 是否支持Vsync Period切换 // 我们假设支持,其实不支持的话无非是换成调用setActiveConfig if (isVsyncPeriodSwitchSupported()) { Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints; hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos; hwc2Constraints.seamlessRequired = constraints.seamlessRequired; Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {}; // 2.2.2 通知HWComposer切换帧率 auto intError = mComposer.setActiveConfigWithConstraints(mId, config->getId(), hwc2Constraints, &vsyncPeriodChangeTimeline); outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos; outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired; outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos; return static_cast(intError); } // ...... } 2.1.3 ComposerHal.setActiveConfigWithConstraints 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; // 转到composer service处理. 也就是给硬件厂商实现 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 void SurfaceFlinger::setActiveConfigInternal() { ATRACE_CALL(); const auto display = getDefaultDisplayDeviceLocked(); if (!display) { return; } auto& oldRefreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()); std::lock_guard lock(mActiveConfigLock); // 更新配置为最新的帧率信息 mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId); mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId); // 将新的帧率保存在DisplayDevice中 display->setActiveConfig(mUpcomingActiveConfig.configId); auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId); if (refreshRate.getVsyncPeriod() != oldRefreshRate.getVsyncPeriod()) { // 前后帧率不一致,记录此次帧率切换,就是次数(refreshRateSwitches)+1 mTimeStats->incrementRefreshRateSwitches(); } // 偏移量管理类也要更新FPS信息 mPhaseConfiguration->setRefreshRateFps(refreshRate.getFps()); // HWC更新帧率了,偏移量再次更新 mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets()); ATRACE_INT("ActiveConfigFPS", refreshRate.getFps()); // 这里的event就是:Scheduler::ConfigEvent::Changed if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) { const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId) .getVsyncPeriod(); // 更新AppEventThread中的Vsync间隔信息 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周期并不相同哦



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3