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提供了以下功能:
参数:
FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_FIXED_SOURCE
FRAME_RATE_COMPATIBILITY_DEFAULT
ANATIVEWINDOW_FRAME_RATE_COMPATIBILITY_DEFAULT
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 {}
SurfaceControl.java
中有个设置帧率选择优先级的接口。通知SF窗口的优先级。 然后,SF在决定屏幕的刷新率时,使用此信息来决定哪个窗口的所需渲染速率应具有优先级。
默认情况,所有的窗口优先级都是最低。
这种涉及到优先级的接口,由WMS统一管理最好,否则很容易被应用滥用。
Android R的WMS中已经做了相关策略:RefreshRatePolicy
优先级计算方法:
另外,为了适配更多的情况,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
数组里的黑名单。
上层的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
对应关系:
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没有指明需要的帧率时,通过对历史数据的计算,使用启发式的方式估算当前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(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;
}
std::optional 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(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 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};
}
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);
}
}
}
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(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(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 configId;
// LayerHistory中
// using Summary = std::vector;
/*
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 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
这个函数在计算显示刷新率时,对各种场景进行了优先级划分,由高到低为:
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& 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> 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;
// 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(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(layerPeriod) /
static_cast(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(layerPeriod) /
static_cast(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(score * 100));
if (score > max * (1 + EPSILON)) {
max = score;
bestRefreshRate = refreshRate;
}
}
return bestRefreshRate;
}
对上述逻辑做一下总结:
LayerVoteType
)LayerVoteType
)LayerVoteType
的图层数量,先决策一轮刷新率https://developer.android.com/guide/topics/media/frame-rate
我的简书文章地址:
Android R Variable Refresh Rate 研究