以下分析基于Android R.
简述
上一章我们分析了App是如何通过更改一个小小的WindowManager的LayoutParam的属性,来影响Framework决策帧率变化的。
接来下我们详细看看SurfaceFlinger是如何根据Framework传入的帧率参数选择合适帧率的。
一. SurfaceFlinger接受帧率变化
接上一章,从 SurfaceFlinger::setAllowedDisplayConfigs 开始. 但是Android R上入口函数些许变化:
由SurfaceControl.setAllowedDisplayConfigs(getDisplayTokenLocked(), allowedPhysIndexes)
=》SurfaceControl.setDesiredDisplayConfigSpecs(displayToken, configSpecs);
这里的configSpecs是DesiredDisplayConfigSpecs类型
1.1 SurfaceControl.setDesiredDisplayConfigSpecs
1 | public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken, |
1.1.1 android_view_SurfaceControl.nativeSetDesiredDisplayConfigSpecs
1 | static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, jobject tokenObj, |
1.1.2 SurfaceComposerClient.setDesiredDisplayConfigSpecs
1 | status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, |
1.2 SurfaceFlinger.setDesiredDisplayConfigSpecs
1 | status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, |
1.2.1 SurfaceFlinger.schedule
1 | template <typename F, typename T> |
1.3 SurfaceFlinger.setDesiredDisplayConfigSpecsInternal
1 | status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( |
1.3.1 RefreshRateConfigs.setDisplayManagerPolicy
1 | status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { |
1.3.2 RefreshRateConfigs.constructAvailableRefreshRates
1 | void RefreshRateConfigs::constructAvailableRefreshRates() { |
这里吐槽一下,写的很丑。直接说明,在 mRefreshRates中寻找符合要求的configId(modeId)放入对应的集合中。
这里说的符合要求是指:
- 宽高与所设置的Policy中的defaultConfig的宽高一致
- 其帧率在所设置的Policy的最小和最大帧率之中
最终将结果保存在变量:mPrimaryRefreshRates以及mAppRequestRefreshRates中。
1.3.3 Scheduler.getPreferredConfigId
1 | std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() { |
这个地方原以为mFeatures.configId一般是存在value的,其实并不是。
1.3.4 Scheduler.calculateRefreshRateConfigIndexType
1 | // consideredSignals 的默认参数是nullptr的 |
1.3.5 RefreshRateConfigs.getBestRefreshRate
1 | const RefreshRate& RefreshRateConfigs::getBestRefreshRate( |
Emmmm…. 这一言难尽的代码。
总结一下,在上层计算传入两个刷新率范围后,这里主要是根据Layer投票以及一系列判断得到最终所需的刷新率。
说明一下几个概念:
- 显示主范围刷新率: DisplayModeDirector中投票算出的包含所有请求帧率的最小范围
- layerPeriod: 根据当前layer.desiredRefreshRate计算出一帧的时长
- displayPeriod: 当前AppReqeustRefereshRate中HwcConfig计算的一帧时长
整个投票过程简述:
- 首先计算所有不同LayerVoteType的数量
- 如果没有显式Layer,即ExplicitDefault和ExplicitExactOrMultiple类型,且存在触摸事件, 直接选择主范围刷新率最大帧率
- 没有touch事件且屏幕处于idle状态, 刷新率存在一定范围或者不存在显示请求刷新率的Layer时, 选择主范围刷新率最小帧率
- 没有Layer或者所有Layer都没有投票(NoVote)时, 选择最大帧率
- 存在Layer且所有Layer要么不投票,要么请求最小帧率时,选择最小帧率
- 当上述条件都不满足时,遍历所有Layer计算每个AppRequestRefreshRate的得分,找到最佳刷新率, 注意遍历时, 忽略不投票或者投票选择最小帧率的Layer
- 遍历所有AppRequestRefreshRate
- 只有ExplicitDefault或者ExplicitExactOrMultiple类型的Layer,且该Layer是有焦点的才允许投票超出刷新率请求范围的帧率,否则忽略该Layer
- 如果Layer是Max类型
- 用当前layer(app)请求的帧率除以最后一个layer(app)请求的帧率,得到的比值的平方乘以权重,计入当前AppRequestRefreshRate的分数
- 如果是ExplicitDefault类型的Layer
- 找到Layer将渲染的实际速率,首先假设layer.desiredRefreshRate计算的Period是渲染帧的最短时间
- 将该AppReqeustRefereshRate中的Display Period刷新时长依次翻倍,直到满足该Layer刷新的最低时长,也就是fps大小每次折半
- 此时layer分数为 layer所需的时长除以满足刷新要求的最长时长在乘以权重计入当前AppRequestRefreshRate的分数
- 如果是ExplicitExactOrMultiple或者Heuristic类型的Layer
- 首先计算需要多少个显示vSync来显示这个层的一个帧,即计算 layerPeriod/displayPeriod 得到商 quot 和余数 rem
- 如果是整数倍关系,当前AppRequestRefreshRate的分数直接加上该Layer的权重
- 当layer请求的fps比AppReqeustRefereshRate中的实际display的fps要大的时候,得分是layer period除以display period的商的十一分之一乘以layer的权重
- layer所需的刷新率低于的显示刷新率,但又不是整数倍关系时,用 Pl 表述 layer period,Pd表示display period
- diff0 = 2 * (Pl mod Pd) - Pd, Pl > Pd 且 K ∈ {1,2,3,…,9}
- 当diff0 > 0 时, diff = (Pl mod Pd) * 2^k - Pd * (2^k-1)
- 当diff0 < 0 时, diff = Pd - 2^k * (Pl mode Pd)
- 当diff小于800时(差值小于800us), 或者k>9结束, 得分是当前Layer的权重乘以1/(2K)
- 遍历所有AppRequestRefreshRate
- 如果存在请求最大帧率的layer就反向遍历,找到得分最大的帧率 bestRefreshRate
- 如果显示主刷新率没有范围,比如最小值和最大值都是120Hz时
- 如果没有layer参与评分,从显示主刷新范围选取最大值
- 否则返回计算得出的最佳刷新率
- 如果存在touch事件, 不存在ExplicitDefault的Layer且显示主范围刷新率最大值大于计算的刷新率时, 采用最大刷新率
- 以上条件均不满足时,返回计算的bestRefreshRate
好了,本次分析到此为止,接下来就是继续看SurfaceFlinger如何通知HWC硬件切换帧率了。