Variable Refresh Rate (VRR) 简介
Android R上Google增加了Display Feature:VRR0.5
早在Google之前,Android的OEM厂商就已经开始研发可变帧率(Variable Refresh Rate),直到一加7 pro的发布将整个行业带入90HZ高刷的时代。高刷新必然带来巨大的功耗增量,同时视频游戏等应用因为片源或者适配等问题并没有体现出高帧应有的价值。OEM厂商针对这些情况做了大量可变帧率策略和对应用的反向适配(MEMC视频运动插帧等)。但是不同厂商策略相差很大,应用又不配合,一直没有一个形成统一完整的高帧生态。
通过查看Android代码我们发现,从Q开始Google就已经开始着手对可变帧率场景进行划分,试图提供一种统一通用的可变帧率机制。Android R上又一次的重构使相关功能初具雏形 - VRR0.5。
Variable Refresh Rate (VRR) 0.5提供了以下功能:
- 允许该应用指定图层的所需要的帧
- 帮助决定显示刷新率
- SDK和NDK中提供接口
接口介绍与WMS中的策略
New public APIs
- Surface.setFrameRate()
- SurfaceControl.Transaction.setFrameRate()
- ANativeWindow_setFrameRate()
- ASurfaceTransaction_setFrameRate()
参数:
- Video apps - 指定所需的帧率,适用于Video或者Game图层
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
- Non-video apps
FRAME_RATE_COMPATIBILITY_DEFAULT
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
接口的实现 & WMS中的策略
setFrameRate
Android R上已经考虑到了类似视频这种应用,如果系统不适应此类应用刷新率,会导致糟糕的用户体验(重复帧)。
所以,Android R上提供了针对单个图层设置帧率的接口。
Surface.java & SurfaceControl.java
已经提供了setFrameRate
方法。
参数frameRate
= 0时,代表使用系统默认帧率。compatibility
参数会影响到系统对帧率的决策。
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FRAME_RATE_COMPATIBILITY_"},
value = {FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE})
public @interface FrameRateCompatibility {}
setFrameRateSelectionPriority - 帧率优先级策略
SurfaceControl.java
中有个设置帧率选择优先级的接口。通知SF窗口的优先级。 然后,SF在决定屏幕的刷新率时,使用此信息来决定哪个窗口的所需渲染速率应具有优先级。
默认情况,所有的窗口优先级都是最低。
image-20200526173700180.png这种涉及到优先级的接口,由WMS统一管理最好,否则很容易被应用滥用。
Android R的WMS中已经做了相关策略:RefreshRatePolicy
优先级计算方法:
- 0默认是最高优先级,-1代表没有设置优先级
- 有焦点(focus)的窗口,如果投票给想要的mode ID,有最高的优先级
- 有焦点但是没有指定mode的窗口是默认优先级
- 没有窗口,但是有指定mode窗口,优先级最低。
- 这个针对分屏类场景使用的。比如,如果分屏下有两个窗口,一个有焦点不在于当前系统帧率,一个没有焦点制定了mode,这种情况下,我们可以将mode设置成指定mode的窗口。
另外,为了适配更多的情况,WMS还增加了两个类名单。
// 非高帧应用包名
private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
// 黑名单,在黑名单之内的包名限制使用高帧
private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
高帧黑名单
HighRefreshRateBlacklist
类负责管理高帧黑名单,提供更新和判断逻辑。
HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) {
mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist);
... ...
}
读取config_highRefreshRateBlacklist
数组里的黑名单。
SurfaceFlinger中的实现逻辑
Layer.setFrameRateSelectionPriority && Layer.setFrameRate
上层的setFrameRateSelectionPriority
对应的同样是底层Layer的 setFrameRateSelectionPriority
。
上层的setFrameRate
对应的同样是底层Layer的 setFrameRate
。
Layer.cpp
struct State {
... ...
// 对应setFrameRateSelectionPriority设置的参数
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
// 对应setFrameRate设置的参数
FrameRate frameRate;
// Indicates whether parents / children of this layer had set FrameRate
bool treeHasFrameRateVote;
};
// Encapsulates the frame rate and compatibility of the layer.
// This information will be used
// when the display refresh rate is determined.
struct FrameRate {
float rate;
FrameRateCompatibility type;
... ...
};
FrameRateCompatibility
// FrameRateCompatibility specifies how we should interpret the frame rate associated with
// the layer.
enum class FrameRateCompatibility {
Default, // Layer didn't specify any specific handling strategy
ExactOrMultiple, // Layer needs the exact frame rate (or a multiple of it) to present the
// content properly. Any other value will result in a pull down.
NoVote, // Layer doesn't have any requirements for the refresh rate and
// should not be considered when the display refresh rate is determined.
};
上层FrameRateCompatibility
跟SurfaceFlinger中的FrameRateCompatibility
对应关系:
- FRAME_RATE_COMPATIBILITY_DEFAULT == Default
- FRAME_RATE_COMPATIBILITY_FIXED_SOURCE == ExactOrMultiple
Layer::FrameRateCompatibility Layer::FrameRate::convertCompatibility(int8_t compatibility) {
switch (compatibility) {
case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT:
return FrameRateCompatibility::Default;
case ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE:
return FrameRateCompatibility::ExactOrMultiple;
default:
LOG_ALWAYS_FATAL("Invalid frame rate compatibility value %d", compatibility);
return FrameRateCompatibility::Default;
}
}
查看Android R code发现frameRateSelectionPriority
这个参数SF并没有使用,Google还没有把这个功能开发完成。
// TODO(b/144307188): This needs to be plugged into layer summary as
// an additional parameter.
ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
Layer历史数据统计 - 用于启发式(Heuristic)计算
当Layer没有指明需要的帧率时,通过对历史数据的计算,使用启发式的方式估算当前layer的帧率
void LayerHistoryV2::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
[layer](const auto& pair) { return pair.first == layer; });
LOG_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
const auto& info = it->second;
info->setLastPresentTime(presentTime, now);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
std::iter_swap(it, end);
mActiveLayersEnd++;
}
}
void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
mLastUpdatedTime = std::max(lastPresentTime, now);
FrameTimeData frameTime = {.presetTime = lastPresentTime, .queueTime = mLastUpdatedTime};
mFrameTimes.push_back(frameTime);
if (mFrameTimes.size() > HISTORY_SIZE) {
mFrameTimes.pop_front();
}
}
判断图层更新是否频繁
bool LayerInfoV2::isFrequent(nsecs_t now) const {
// Find the first valid frame time
auto it = mFrameTimes.begin();
for (; it != mFrameTimes.end(); ++it) {
if (isFrameTimeValid(*it)) {
break;
}
}
// If we know nothing about this layer we consider it as frequent as it might be the start
// of an animation.
if (std::distance(it, mFrameTimes.end()) < FREQUENT_LAYER_WINDOW_SIZE) {
return true;
}
// Find the first active frame
for (; it != mFrameTimes.end(); ++it) {
if (it->queueTime >= getActiveLayerThreshold(now)) {
break;
}
}
const auto numFrames = std::distance(it, mFrameTimes.end());
if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
return false;
}
// Layer is considered frequent if the average frame rate is higher than the threshold
const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
}
计算Layer刷新率
std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
static constexpr float MARGIN = 1.0f; // 1Hz
if (!hasEnoughDataForHeuristic()) {
ALOGV("Not enough data");
return std::nullopt;
}
// Calculate the refresh rate by finding the average delta between frames
nsecs_t totalPresentTimeDeltas = 0;
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
// If there are no presentation timestamp provided we can't calculate the refresh rate
if (it->presetTime == 0 || (it + 1)->presetTime == 0) {
return std::nullopt;
}
// 算出PresentTime Delta的总和
totalPresentTimeDeltas +=
std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
}
// 计算每帧平均时间
const float averageFrameTime =
static_cast<float>(totalPresentTimeDeltas) / (mFrameTimes.size() - 1);
// Now once we calculated the refresh rate we need to make sure that all the frames we captured
// are evenly distributed and we don't calculate the average across some burst of frames.
// 去除突发帧的情况,保证捕获的所有帧都均匀分布
for (auto it = mFrameTimes.begin(); it != mFrameTimes.end() - 1; ++it) {
const nsecs_t presentTimeDeltas =
std::max(((it + 1)->presetTime - it->presetTime), mHighRefreshRatePeriod);
if (std::abs(presentTimeDeltas - averageFrameTime) > 2 * averageFrameTime) {
return std::nullopt;
}
}
const auto refreshRate = 1e9f / averageFrameTime;
// 相差大于1HZ,更新帧率
if (std::abs(refreshRate - mLastReportedRefreshRate) > MARGIN) {
mLastReportedRefreshRate = refreshRate;
}
ALOGV("Refresh rate: %.2f", mLastReportedRefreshRate);
return mLastReportedRefreshRate;
}
std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
// 只有LayerVoteType == Heuristic才会计算可能的刷新率
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
return {mLayerVote.type, mLayerVote.fps};
}
if (!isFrequent(now)) {
return {LayerHistory::LayerVoteType::Min, 0};
}
auto refreshRate = calculateRefreshRateIfPossible();
if (refreshRate.has_value()) {
return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
}
return {LayerHistory::LayerVoteType::Max, 0};
}
屏幕刷新帧率决策逻辑
Android R中,对窗口进行了划分:
- wallpaper - 壁纸图层跑在最小帧率
- status bar - Doesn't care about the refresh rate
- 其他图层 - 启发式估算帧率
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
// If the content detection feature is off, all layers are registered at NoVote. We still
// keep the layer history, since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
if (!mUseContentDetection) {
mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::NoVote);
return;
}
// In V1 of content detection, all layers are registered as Heuristic (unless it's wallpaper).
if (!mUseContentDetectionV2) {
const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
? lowFps
: mRefreshRateConfigs.getMaxRefreshRate().fps;
mLayerHistory->registerLayer(layer, lowFps, highFps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
} else {
if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::Min);
} else if (layer->getWindowType() == InputWindowInfo::TYPE_STATUS_BAR) {
mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::NoVote);
} else {
mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
}
}
}
遍历Layer
- 如果图层指定了想要的刷新率(
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
),通过setLayerVote
通知LayerInfoV2
。在LayerInfoV2
计算帧率时,发现是指定帧率,非启发式,就直接返回指定帧率 - 如果是(
FRAME_RATE_COMPATIBILITY_DEFAULT
),就是用图层指定的帧率 - 如果图层放弃投票(
NoVote
),对刷新率没有任何要求,并且在确定显示刷新率时不应考虑该层(类似statusbar/R角/全屏手势)。那么它的帧率根据其它图层而定
void LayerHistoryV2::partitionLayers(nsecs_t now) {
const nsecs_t threshold = getActiveLayerThreshold(now);
// Collect expired and inactive layers after active layers.
size_t i = 0;
while (i < mActiveLayersEnd) {
auto& [weak, info] = mLayerInfos[i];
if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
i++;
// Set layer vote if set
const auto frameRate = layer->getFrameRateForLayerTree();
const auto voteType = [&]() {
switch (frameRate.type) {
case Layer::FrameRateCompatibility::Default:
return LayerVoteType::ExplicitDefault;
case Layer::FrameRateCompatibility::ExactOrMultiple:
return LayerVoteType::ExplicitExactOrMultiple;
case Layer::FrameRateCompatibility::NoVote:
return LayerVoteType::NoVote;
}
}();
if (frameRate.rate > 0 || voteType == LayerVoteType::NoVote) {
info->setLayerVote(voteType, frameRate.rate);
} else {
info->resetLayerVote();
}
continue;
}
if (CC_UNLIKELY(mTraceEnabled)) {
trace(weak, LayerHistory::LayerVoteType::NoVote, 0);
}
info->clearHistory();
std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
// Collect expired layers after inactive layers.
size_t end = mLayerInfos.size();
while (i < end) {
if (mLayerInfos[i].first.promote()) {
i++;
} else {
std::swap(mLayerInfos[i], mLayerInfos[--end]);
}
}
mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(end), mLayerInfos.end());
}
计算图层权重
LayerHistoryV2::Summary LayerHistoryV2::summarize(nsecs_t now) {
LayerHistory::Summary summary;
std::lock_guard lock(mLock);
partitionLayers(now);
for (const auto& [layer, info] : activeLayers()) {
const auto strong = layer.promote();
if (!strong) {
continue;
}
// 上层设置的优先级并没有使用,TODO
// TODO(b/144307188): This needs to be plugged into layer summary as
// an additional parameter.
ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
const bool recent = info->isRecentlyActive(now);
if (recent) {
const auto [type, refreshRate] = info->getRefreshRate(now);
// 弃权的图层不考虑
// Skip NoVote layer as those don't have any requirements
if (type == LayerHistory::LayerVoteType::NoVote) {
continue;
}
// 计算图层在屏幕中的位置
// Compute the layer's position on the screen
const Rect bounds = Rect(strong->getBounds());
const ui::Transform transform = strong->getTransform();
constexpr bool roundOutwards = true;
Rect transformed = transform.transform(bounds, roundOutwards);
// 计算图层的面积区域
const float layerArea = transformed.getWidth() * transformed.getHeight();
// 图层区域/显示区域 = 图层在显示区域中的占比 = 权重
float weight = mDisplayArea ? layerArea / mDisplayArea : 0.0f;
summary.push_back({strong->getName(), type, refreshRate, weight});
if (CC_UNLIKELY(mTraceEnabled)) {
trace(layer, type, static_cast<int>(std::round(refreshRate)));
}
} else if (CC_UNLIKELY(mTraceEnabled)) {
trace(layer, LayerHistory::LayerVoteType::NoVote, 0);
}
}
return summary;
}
决定内容刷新率
struct {
ContentDetectionState contentDetectionV1 = ContentDetectionState::Off;
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
std::optional<HwcConfigIndexType> configId;
// LayerHistory中
// using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
/*
struct LayerRequirement {
std::string name; // Layer's name. Used for debugging purposes.
LayerVoteType vote; // Layer vote type.
float desiredRefreshRate; // Layer's desired refresh rate, if applicable.
float weight; // Layer's weight in the range of [0, 1]. The higher the weight the more
};
*/
scheduler::LayerHistory::Summary contentRequirements;
bool isDisplayPowerStateNormal = true;
} mFeatures
// 根据内容选择帧率
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
ATRACE_CALL();
scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (mFeatures.contentRequirements == summary) {
return;
}
mFeatures.contentRequirements = summary;
mFeatures.contentDetectionV1 =
!summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
// 帧率计算逻辑
newConfigId = calculateRefreshRateConfigIndexType();
if (mFeatures.configId == newConfigId) {
return;
}
mFeatures.configId = newConfigId;
auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
}
calculateRefreshRateConfigIndexType
这个函数在计算显示刷新率时,对各种场景进行了优先级划分,由高到低为:
- Power 事件
- Touch 事件
- Content是否刷新(是否处于Idle状态)
HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
ATRACE_CALL();
// NOTE: If we remove the kernel idle timer, and use our internal idle timer, this
// code will have to be refactored. If Display Power is not in normal operation we want to be in
// performance mode. When coming back to normal mode, a grace period is given with
// DisplayPowerTimer.
if (mDisplayPowerTimer &&
(!mFeatures.isDisplayPowerStateNormal ||
mFeatures.displayPowerTimer == TimerState::Reset)) {
return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
if (!mUseContentDetectionV2) {
// As long as touch is active we want to be in performance mode.
if (mTouchTimer && mFeatures.touch == TouchState::Active) {
return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
}
// If timer has expired as it means there is no new content on the screen.
if (mIdleTimer && mFeatures.idleTimer == TimerState::Expired) {
return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
}
if (!mUseContentDetectionV2) {
// If content detection is off we choose performance as we don't know the content fps.
if (mFeatures.contentDetectionV1 == ContentDetectionState::Off) {
// NOTE: V1 always calls this, but this is not a default behavior for V2.
return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// Content detection is on, find the appropriate refresh rate with minimal error
return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements).configId;
}
bool touchConsidered;
// 该函数中最重要的调用
const auto& ret =
mRefreshRateConfigs
.getRefreshRateForContentV2(mFeatures.contentRequirements,
mTouchTimer &&
mFeatures.touch == TouchState::Active,
&touchConsidered)
.configId;
if (touchConsidered) {
// Clear layer history if refresh rate was selected based on touch to allow
// the hueristic to pick up with the new rate.
mLayerHistory->clear();
}
return ret;
}
isDisplayPowerStateNormal
对应的就是 HWC_POWER_MODE_NORMAL
getRefreshRateForContentV2
是排除掉更高优先级场景后,计算最终显示刷新率的函数。
其中 mAvailableRefreshRates
变量是排序过的。
const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
const std::vector<LayerRequirement>& layers, bool touchActive,
bool* touchConsidered) const {
ATRACE_CALL();
ALOGV("getRefreshRateForContent %zu layers", layers.size());
*touchConsidered = false;
std::lock_guard lock(mLock);
// If there are not layers, there is not content detection, so return the current
// refresh rate.
// 如果没有Layer,没有内容检查,
if (layers.empty()) {
*touchConsidered = touchActive;
return touchActive ? *mAvailableRefreshRates.back() : getCurrentRefreshRateByPolicyLocked();
}
int noVoteLayers = 0;
int minVoteLayers = 0;
int maxVoteLayers = 0;
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
float maxExplicitWeight = 0;
for (const auto& layer : layers) {
if (layer.vote == LayerVoteType::NoVote) {
noVoteLayers++;
} else if (layer.vote == LayerVoteType::Min) {
minVoteLayers++;
} else if (layer.vote == LayerVoteType::Max) {
maxVoteLayers++;
} else if (layer.vote == LayerVoteType::ExplicitDefault) {
explicitDefaultVoteLayers++;
maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
} else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
explicitExactOrMultipleVoteLayers++;
maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
}
}
// Consider the touch event if there are no ExplicitDefault layers.
// ExplicitDefault are mostly interactive (as opposed to ExplicitExactOrMultiple)
// and therefore if those posted an explicit vote we should not change it
// if get get a touch event.
// 对应上层 FRAME_RATE_COMPATIBILITY_DEFAULT
// Layer指定了帧率
// Android原生对这类图层的策略是,如果存在这种图层,得到touch事件也不会把帧率提高到最大
if (touchActive && explicitDefaultVoteLayers == 0) {
*touchConsidered = true;
// mAvailableRefreshRates.back() 最大帧率
return *mAvailableRefreshRates.back();
}
// Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) {
// mAvailableRefreshRates.front()最小帧率
return *mAvailableRefreshRates.front();
}
// Find the best refresh rate based on score
std::vector<std::pair<const RefreshRate*, float>> scores;
// 分配scores vector容器跟mAvailableRefreshRates一样大
scores.reserve(mAvailableRefreshRates.size());
for (const auto refreshRate : mAvailableRefreshRates) {
// 设置scores中帧率对应的所有得分为 0.0f
scores.emplace_back(refreshRate, 0.0f);
}
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (type: %d)", layer.name.c_str(), layer.vote);
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
continue;
}
auto weight = layer.weight;
for (auto i = 0u; i < scores.size(); i++) {
// 如果Layer想要最大帧率,把最高分给最高的帧率
// If the layer wants Max, give higher score to the higher refresh rate
if (layer.vote == LayerVoteType::Max) {
// scores.back().first->fps是最大的帧率
// 这里通过当前帧率与最大帧率相除,得出的值 < 1
// 最大帧率与最大帧率相除,得出的值 = 1
// 加上平方之后,小于最大帧率的其他帧率,得分远远小于最大帧率
// 通过这种方式,把最高分给到最高帧率
const auto ratio = scores[i].first->fps / scores.back().first->fps;
// use ratio^2 to get a lower score the more we get further from peak
const auto layerScore = ratio * ratio;
ALOGV("%s (Max, weight %.2f) gives %s score of %.2f", layer.name.c_str(), weight,
scores[i].first->name.c_str(), layerScore);
// 该帧率对于这个Layer得分 × 权重
scores[i].second += weight * layerScore;
continue;
}
// 注意这里计算的是周期,不是帧率,跟帧率大小成反比 !!!!
const auto displayPeriod = scores[i].first->vsyncPeriod;
// desiredRefreshRate 是LayerRequirement结构体中的
// 前文提到,using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
// Summary中的refreshRate来自LayerInfoV2的getRefreshRate()函数
// 该函数返回的是{mLayerVote.type, mLayerVote.fps};
// mLayerVote中的fps是以下方式设置
// info->setLayerVote(voteType, frameRate.rate);
// FrameRate中的rate是上层setFrameRate接口传下来的
// 所以 layer.desiredRefreshRate == frameRate.rate
const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
// 对应上层 FRAME_RATE_COMPATIBILITY_DEFAULT
if (layer.vote == LayerVoteType::ExplicitDefault) {
const auto layerScore = [&]() {
// Find the actual rate the layer will render, assuming
// that layerPeriod is the minimal time to render a frame
// 如果Layer要求的周期比当前周期的大,那把当前刷新周期一直增加
auto actualLayerPeriod = displayPeriod;
int multiplier = 1;
while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
multiplier++;
actualLayerPeriod = displayPeriod * multiplier;
}
// layerPeriod / actualLayerPeriod这个值有两种可能
// 1. actualLayerPeriod < layerPeriod
// && actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION > layerPeriod
// 那么layerPeriod / actualLayerPeriod > 1,该帧率对于这个Layer得分 = 1
// 2. actualLayerPeriod > layerPeriod
// 那么layerPeriod / actualLayerPeriod < 1,该帧率对于这个Layer得分 < 1
// 可以看出来,第一种情况得分上比较占便宜。这是因为,如果图层刷新频率大于显示频率,
// 会出现等待的情况,如果图层刷新帧率小于显示帧率,二者又很接近,那么每一帧都能即使得到显示,并且也没有很多重复帧的情况
return std::min(1.0f,
static_cast<float>(layerPeriod) /
static_cast<float>(actualLayerPeriod));
}();
ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
layerScore);
scores[i].second += weight * layerScore;
continue;
}
// Layer指定刷新率或者通过启发式估算出来的Layer内容刷新率的情况
if (layer.vote == LayerVoteType::ExplicitExactOrMultiple ||
layer.vote == LayerVoteType::Heuristic) {
const auto layerScore = [&]() {
// Calculate how many display vsyncs we need to present a single frame for this
// layer
// getDisplayFrames这个函数范围的是layerPeriod / displayPeriod的
// 商(Quotient)和余数(remainder),其中包含四舍五入(800us)的情况
const auto [displayFramesQuot, displayFramesRem] =
getDisplayFrames(layerPeriod, displayPeriod);
static constexpr size_t MAX_FRAMES_TO_FIT =
10; // Stop calculating when score < 0.1
// 如果余数 == 0,图层所需的刷新率是显示刷新率的整数倍数
// 认为这种情况是匹配的,该帧率对于这个Layer得分 = 1
if (displayFramesRem == 0) {
// Layer desired refresh rate matches the display rate.
return 1.0f;
}
// 如果商 == 0,说明图层所需帧率远远大于当前提供的显示帧率
// 这种情况相差越大,得分越低
if (displayFramesQuot == 0) {
// Layer desired refresh rate is higher the display rate.
return (static_cast<float>(layerPeriod) /
static_cast<float>(displayPeriod)) *
(1.0f / (MAX_FRAMES_TO_FIT + 1));
}
// 走到这里的情况是layerPeriod > displayPeriod
// 但是layerPeriod / displayPeriod不能整除
// Layer所需帧率远远小于显示帧率
// Layer desired refresh rate is lower the display rate. Check how well it fits
// the cadence
auto diff = std::abs(displayFramesRem - (displayPeriod - displayFramesRem));
int iter = 2;
while (diff > MARGIN_FOR_PERIOD_CALCULATION && iter < MAX_FRAMES_TO_FIT) {
diff = diff - (displayPeriod - diff);
iter++;
}
// 相差越小得分越高
return 1.0f / iter;
}();
ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f",
layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(),
layerScore);
scores[i].second += weight * layerScore;
continue;
}
}
}
// Now that we scored all the refresh rates we need to pick the one that got the highest score.
// In case of a tie we will pick the higher refresh rate if any of the layers wanted Max,
// or the lower otherwise.
const RefreshRate* bestRefreshRate = maxVoteLayers > 0
? getBestRefreshRate(scores.rbegin(), scores.rend())
: getBestRefreshRate(scores.begin(), scores.end());
return *bestRefreshRate;
}
getBestRefreshRate
取得分最高的为最适合的刷新率
const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
constexpr auto EPSILON = 0.001f;
const RefreshRate* bestRefreshRate = begin->first;
float max = begin->second;
for (auto i = begin; i != end; ++i) {
const auto [refreshRate, score] = *i;
ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
if (score > max * (1 + EPSILON)) {
max = score;
bestRefreshRate = refreshRate;
}
}
return bestRefreshRate;
}
总结:
对上述逻辑做一下总结:
- SDK & NDK中暴露了允许Applications根据自身场景对刷新帧率进行投票的接口
- WMS对焦点,分屏等情况对帧率优先级的划分,同时提供了黑名单机制
- SurfaceFlinger根据得到的信息对显示帧率做最终决策
- 如果Applications或者Framework没有设置刷新率,并且配置中打开了内容刷新率统计逻辑,那么会根据图层刷新历史,对图层内容刷新率做启发式估算,并更新投票类型(
LayerVoteType
) - 如果Applications或者Framework设置了刷新率,以APP设置的为准,并更新投票类型(
LayerVoteType
) - 遍历每个Layer,计算Layer在显示区域里面的占比,用作权重
- 考虑power、touch等特殊场景,这些场景优先级更高
- 当没有特殊场景时,根据不同
LayerVoteType
的图层数量,先决策一轮刷新率 - 如果上述都没有决策成功,会遍历所有Layer,遍历所有支持的显示帧率。根据Layer的要求,针对每个显示帧率给出得分
- 最终得分最多的是最有可能满足所有场景的帧率
- 如果Applications或者Framework没有设置刷新率,并且配置中打开了内容刷新率统计逻辑,那么会根据图层刷新历史,对图层内容刷新率做启发式估算,并更新投票类型(
参考:
https://developer.android.com/guide/topics/media/frame-rate
我的csdn文章地址:
Android R Variable Refresh Rate 研究
网友评论