AndroidFrameWork切换帧率

2024-02-18 00:12
文章标签 切换 androidframework

本文主要是介绍AndroidFrameWork切换帧率,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

参考资料
简述
一. SurfaceFlinger接受帧率变化1.1 SurfaceFlinger.setDesiredActiveConfig1.2 SurfaceFlinger.repaintEverythingForHWC1.3 Scheduler.resyncToHardwareVsync1.3.1 Scheduler.setVsyncPeriod1.3.2 VSyncReactor.setPeriod1.3.3 VSyncReactor.startPeriodTransition1.4 VSyncModulator.onRefreshRateChangeInitiated1.4.1 VSyncModulator.getNextOffsets1.4.2 DispSyncSource.setPhaseOffset1.4.3 VSyncReactor.changePhaseOffset1.4.4 VSyncReactor.CallbackRepeater.start1.4.5 VSyncCallbackRegistration.schedule1.5 VSyncModulator.setPhaseOffsets1.5.1 PhaseOffsets.getCurrentOffsets
二. 硬件切换帧率2.1 SurfaceFlinger.performSetActiveConfig2.1.1 HWComposer.setActiveConfigWithConstraints2.1.2 HWC2::impl::Display.setActiveConfigWithConstraints2.1.3 ComposerHal.setActiveConfigWithConstraints2.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
2
3
4
5
6
7
8

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<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;// 1.2 触发HWC刷新而不重置空闲计时器。repaintEverythingForHWC();// 1.3 现在开始接收vsync样本,这可以检测到硬件周期切换。mScheduler->resyncToHardwareVsync(true, refreshRate.getVsyncPeriod());// 1.4 调用onRefreshRateChangeCompleted, 通知更新偏移量mVSyncModulator->onRefreshRateChangeInitiated();// 保存即将更新的FpsmPhaseConfiguration->setRefreshRateFps(refreshRate.getFps());// 1.5 再次更新偏移量, 不过这一次是根据即将更新的Fps拿到的固定偏移量mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());mScheduler->setConfigChangePending(true);
}if (mRefreshRateOverlay) {mRefreshRateOverlay->changeRefreshRate(refreshRate);
}

}

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

1.2 SurfaceFlinger.repaintEverythingForHWC

void SurfaceFlinger::repaintEverythingForHWC() {
// 标记全部重绘制
mRepaintEverything = true;
// 通知Power模组, Display将更新,OEM厂商自行实现这个标准接口
mPowerAdvisor.notifyDisplayUpdateImminent();
// EventThread请求下一帧Vsync
mEventQueue->invalidate();
}

1
2
3
4
5
6
7
8

1.3 Scheduler.resyncToHardwareVsync

void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) {
{
std::lock_guardstd::mutex lock(mHWVsyncLock);
if (makeAvailable) {
mHWVsyncAvailable = makeAvailable;
} else if (!mHWVsyncAvailable) {
// 硬件Vsync被禁止,直接返回
return;
}
}

if (period <= 0) {// 参数不合法return;
}
// 通知到软件Vsync产生模块,更新Vsync周期
setVsyncPeriod(period);

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

1.3.1 Scheduler.setVsyncPeriod

void Scheduler::setVsyncPeriod(nsecs_t period) {
std::lock_guardstd::mutex lock(mHWVsyncLock);
// 1.3.2 更新软件Vsync周期
mPrimaryDispSync->setPeriod(period);

if (!mPrimaryHWVsyncEnabled) {// 硬件Vsync关闭的情况下, 直接更改软件vsync周期,这里我们加上是开启的mPrimaryDispSync->beginResync();mEventControlThread->setVsyncEnabled(true);mPrimaryHWVsyncEnabled = true;
}

}

1
2
3
4
5
6
7
8
9
10
11
12

现在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
2
3
4
5
6
7
8
9
10
11
12
13
14

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

1.4 VSyncModulator.onRefreshRateChangeInitiated

void VSyncModulator::onRefreshRateChangeInitiated() {
if (mRefreshRateChangePending) {
return;
}
mRefreshRateChangePending = true;
updateOffsets();
}

void VSyncModulator::updateOffsets() {
std::lock_guardstd::mutex 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
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

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
2
3
4
5
6
7
8
9
10
11

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<DispSync::Callback*>(this), mPhaseOffset);
if (err != NO_ERROR) {ALOGE("error changing vsync offset: %s (%d)", strerror(-err), err);
}

}

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

1.4.3 VSyncReactor.changePhaseOffset

status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
std::lock_guardstd::mutex 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
2
3
4
5
6
7
8
9

1.4.4 VSyncReactor.CallbackRepeater.start

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
2
3
4
5
6
7
8
9

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<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
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

1.5 VSyncModulator.setPhaseOffsets

void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) {
std::lock_guardstd::mutex lock(mMutex);
mOffsetsConfig = config;
// 见 1.4 流程
updateOffsetsLocked();
}

1
2
3
4
5
6

这里传入的OffsetsConfig是通过PhaseOffsets拿到的
1.5.1 PhaseOffsets.getCurrentOffsets

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;
}// 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<nsecs_t>(1e9f / fps));

}

PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
// 这里根据帧率大小,分两种情况获取偏移量,具体就不看了,和参数配置相关
if (fps > 65.0f) {
return getHighFpsOffsets(vsyncPeriod);
} else {
return getDefaultOffsets(vsyncPeriod);
}
}

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

哦吼,到这里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());

// …

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

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;

}

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

这里做一些合法性判断,最重要的是告诉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;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

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<Error>(intError);
}// ......

}

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

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;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

到这里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<std::mutex> 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)+1mTimeStats->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);
}

}

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

到此帧率切换的过程,差不多就告一段落,当然这里面还有很对细节的部分。

比如硬件Vsync是怎么影响到VsyncRecator产生软件Vsync的,或者软件Vsync和硬件Vsync是怎么校准的。

不过我们先总结一下:

SurfaceFlinger收到setDesiredDisplayConfigSpecs更新帧率配置后,根据传入的帧率配置以及当前Layer选择一个最佳帧率
将这个最佳帧率信息存储在mDesiredActiveConfig中,然后请求下一帧Vsync,顺便更新一下偏移量
下一帧Vsync到来后,首先根据Layer再次计算一下最佳帧率,然后通知HWC更新帧率,在等待下一帧
第二个Vsync到来后,实际此时硬件HWC的Vsync已经更新了,现在就是同步更新SurfaceFlinger中各个变量中的状态,然后通知给AppEventThread更新

所以一个完整的帧率切换至少包含2个Vsync周期,不过这两个Vsync周期并不相同哦
————————————————
版权声明:本文为CSDN博主「SwallowJoe」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u014535072/article/details/108971660

这篇关于AndroidFrameWork切换帧率的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/719428

相关文章

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

Spring Boot实现多数据源连接和切换的解决方案

《SpringBoot实现多数据源连接和切换的解决方案》文章介绍了在SpringBoot中实现多数据源连接和切换的几种方案,并详细描述了一个使用AbstractRoutingDataSource的实... 目录前言一、多数据源配置与切换方案二、实现步骤总结前言在 Spring Boot 中实现多数据源连接

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u

解决Office Word不能切换中文输入

我们在使用WORD的时可能会经常碰到WORD中无法输入中文的情况。因为,虽然我们安装了搜狗输入法,但是到我们在WORD中使用搜狗的输入法的切换中英文的按键的时候会发现根本没有效果,无法将输入法切换成中文的。下面我就介绍一下如何在WORD中把搜狗输入法切换到中文。

15 组件的切换和对组件的data的使用

划重点 a 标签的使用事件修饰符组件的定义组件的切换:登录 / 注册 泡椒鱼头 :微辣 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-

71-java 导致线程上下文切换的原因

Java中导致线程上下文切换的原因通常包括: 线程时间片用完:当前线程的时间片用完,操作系统将其暂停,并切换到另一个线程。 线程被优先级更高的线程抢占:操作系统根据线程优先级决定运行哪个线程。 线程进入等待状态:如线程执行了sleep(),wait(),join()等操作,使线程进入等待状态或阻塞状态,释放CPU。 线程占用CPU时间过长:如果线程执行了大量的I/O操作,而不是CPU计算

[轻笔记]ubuntu shell脚本切换conda环境

source /home/yourhostname/anaconda3/etc/profile.d/conda.sh # 关键!!!conda activate env_name

ViewPager+fragment实现切换页面(一)

如今的很多应用中都是下面有一排按钮,点击可以切换页面,滑动也可以切换页面。下面就来简单的实现这个功能。 思路 首先肯定是会用到viewpager这个控件,为了能够向下兼容,最好用v4包下的viewpager,Activity要继承FragmentActivity 其次用一个集合来存储所有的fragment页面在设置viewpager的适配器时,把存储fragment页面的list集合传入ada

『功能项目』武器的切换实例【34】

本章项目成果展示 我们打开上一篇33战士的A键连击的项目, 本章要做的事情是按键盘E键切换职业时切换手中的武器 首先在资源商店下载免费的武器模型 创建一个空物体 命名为WeaponPos 将武器预制体拖拽至WeaponPos (注意调整空物体位置就可以后续文章会更换武器) 隐藏两把武器 运行项目 隐藏装备 在资源商店

2409wtl,切换视图

原文 介绍 我从一个基于SDI(单文档接口)WTL向导的应用开始,添加了一些从控件继承的窗口和一些对话框窗口(表单视图),然后才发现我必须,使SDI框架动态加载和卸载子窗口. 本文演示了两个可用来完成的技术:在SDI应用中的视图间动态切换.这是我使用的两个. 技术 1技术:第一个方法涉及按需析构和重建视图实例.这更简单,且在不介意析构和重建窗口对象时效果很好. 2:按需创建视图,然后用