Variable Refresh Rate (VRR) 簡(jiǎn)介
Android R上Google增加了Display Feature:VRR0.5
早在Google之前,Android的OEM廠商就已經(jīng)開始研發(fā)可變幀率(Variable Refresh Rate),直到一加7 pro的發(fā)布將整個(gè)行業(yè)帶入90HZ高刷的時(shí)代。高刷新必然帶來巨大的功耗增量,同時(shí)視頻游戲等應(yīng)用因?yàn)槠椿蛘哌m配等問題并沒有體現(xiàn)出高幀應(yīng)有的價(jià)值。OEM廠商針對(duì)這些情況做了大量可變幀率策略和對(duì)應(yīng)用的反向適配(MEMC視頻運(yùn)動(dòng)插幀等)。但是不同廠商策略相差很大,應(yīng)用又不配合,一直沒有一個(gè)形成統(tǒng)一完整的高幀生態(tài)。
通過查看Android代碼我們發(fā)現(xiàn),從Q開始Google就已經(jīng)開始著手對(duì)可變幀率場(chǎng)景進(jìn)行劃分,試圖提供一種統(tǒng)一通用的可變幀率機(jī)制。Android R上又一次的重構(gòu)使相關(guān)功能初具雛形 - VRR0.5。
Variable Refresh Rate (VRR) 0.5提供了以下功能:
- 允許該應(yīng)用指定圖層的所需要的幀
- 幫助決定顯示刷新率
- SDK和NDK中提供接口
接口介紹與WMS中的策略
New public APIs
- Surface.setFrameRate()
- SurfaceControl.Transaction.setFrameRate()
- ANativeWindow_setFrameRate()
- ASurfaceTransaction_setFrameRate()
參數(shù):
- 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
接口的實(shí)現(xiàn) & WMS中的策略
setFrameRate
Android R上已經(jīng)考慮到了類似視頻這種應(yīng)用,如果系統(tǒng)不適應(yīng)此類應(yīng)用刷新率,會(huì)導(dǎo)致糟糕的用戶體驗(yàn)(重復(fù)幀)。
所以,Android R上提供了針對(duì)單個(gè)圖層設(shè)置幀率的接口。

Surface.java & SurfaceControl.java 已經(jīng)提供了setFrameRate方法。

參數(shù)frameRate = 0時(shí),代表使用系統(tǒng)默認(rèn)幀率。compatibility 參數(shù)會(huì)影響到系統(tǒng)對(duì)幀率的決策。
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"FRAME_RATE_COMPATIBILITY_"},
value = {FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE})
public @interface FrameRateCompatibility {}
setFrameRateSelectionPriority - 幀率優(yōu)先級(jí)策略
SurfaceControl.java 中有個(gè)設(shè)置幀率選擇優(yōu)先級(jí)的接口。通知SF窗口的優(yōu)先級(jí)。 然后,SF在決定屏幕的刷新率時(shí),使用此信息來決定哪個(gè)窗口的所需渲染速率應(yīng)具有優(yōu)先級(jí)。
默認(rèn)情況,所有的窗口優(yōu)先級(jí)都是最低。

這種涉及到優(yōu)先級(jí)的接口,由WMS統(tǒng)一管理最好,否則很容易被應(yīng)用濫用。
Android R的WMS中已經(jīng)做了相關(guān)策略:RefreshRatePolicy

優(yōu)先級(jí)計(jì)算方法:
- 0默認(rèn)是最高優(yōu)先級(jí),-1代表沒有設(shè)置優(yōu)先級(jí)
- 有焦點(diǎn)(focus)的窗口,如果投票給想要的mode ID,有最高的優(yōu)先級(jí)
- 有焦點(diǎn)但是沒有指定mode的窗口是默認(rèn)優(yōu)先級(jí)
- 沒有窗口,但是有指定mode窗口,優(yōu)先級(jí)最低。
- 這個(gè)針對(duì)分屏類場(chǎng)景使用的。比如,如果分屏下有兩個(gè)窗口,一個(gè)有焦點(diǎn)不在于當(dāng)前系統(tǒng)幀率,一個(gè)沒有焦點(diǎn)制定了mode,這種情況下,我們可以將mode設(shè)置成指定mode的窗口。
另外,為了適配更多的情況,WMS還增加了兩個(gè)類名單。
// 非高幀應(yīng)用包名
private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
// 黑名單,在黑名單之內(nèi)的包名限制使用高幀
private final HighRefreshRateBlacklist mHighRefreshRateBlacklist;
高幀黑名單
HighRefreshRateBlacklist 類負(fù)責(zé)管理高幀黑名單,提供更新和判斷邏輯。
HighRefreshRateBlacklist(Resources r, DeviceConfigInterface deviceConfig) {
mDefaultBlacklist = r.getStringArray(R.array.config_highRefreshRateBlacklist);
... ...
}
讀取config_highRefreshRateBlacklist數(shù)組里的黑名單。

SurfaceFlinger中的實(shí)現(xiàn)邏輯
Layer.setFrameRateSelectionPriority && Layer.setFrameRate
上層的setFrameRateSelectionPriority 對(duì)應(yīng)的同樣是底層Layer的 setFrameRateSelectionPriority 。

上層的setFrameRate 對(duì)應(yīng)的同樣是底層Layer的 setFrameRate 。

Layer.cpp
struct State {
... ...
// 對(duì)應(yīng)setFrameRateSelectionPriority設(shè)置的參數(shù)
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
// 對(duì)應(yīng)setFrameRate設(shè)置的參數(shù)
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 對(duì)應(yīng)關(guān)系:
- 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發(fā)現(xiàn)frameRateSelectionPriority 這個(gè)參數(shù)SF并沒有使用,Google還沒有把這個(gè)功能開發(fā)完成。
// TODO(b/144307188): This needs to be plugged into layer summary as
// an additional parameter.
ALOGV("Layer has priority: %d", strong->getFrameRateSelectionPriority());
Layer歷史數(shù)據(jù)統(tǒng)計(jì) - 用于啟發(fā)式(Heuristic)計(jì)算
當(dāng)Layer沒有指明需要的幀率時(shí),通過對(duì)歷史數(shù)據(jù)的計(jì)算,使用啟發(fā)式的方式估算當(dāng)前l(fā)ayer的幀率
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;
}
計(jì)算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);
}
// 計(jì)算每幀平均時(shí)間
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.
// 去除突發(fā)幀的情況,保證捕獲的所有幀都均勻分布
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才會(huì)計(jì)算可能的刷新率
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中,對(duì)窗口進(jìn)行了劃分:
- wallpaper - 壁紙圖層跑在最小幀率
- status bar - Doesn't care about the refresh rate
- 其他圖層 - 啟發(fā)式估算幀率
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計(jì)算幀率時(shí),發(fā)現(xiàn)是指定幀率,非啟發(fā)式,就直接返回指定幀率 - 如果是(
FRAME_RATE_COMPATIBILITY_DEFAULT),就是用圖層指定的幀率 - 如果圖層放棄投票(
NoVote),對(duì)刷新率沒有任何要求,并且在確定顯示刷新率時(shí)不應(yīng)考慮該層(類似statusbar/R角/全屏手勢(shì))。那么它的幀率根據(jù)其它圖層而定
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());
}
計(jì)算圖層權(quán)重
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;
}
// 上層設(shè)置的優(yōu)先級(jí)并沒有使用,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);
// 棄權(quán)的圖層不考慮
// Skip NoVote layer as those don't have any requirements
if (type == LayerHistory::LayerVoteType::NoVote) {
continue;
}
// 計(jì)算圖層在屏幕中的位置
// 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);
// 計(jì)算圖層的面積區(qū)域
const float layerArea = transformed.getWidth() * transformed.getHeight();
// 圖層區(qū)域/顯示區(qū)域 = 圖層在顯示區(qū)域中的占比 = 權(quán)重
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;
}
決定內(nèi)容刷新率
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
// 根據(jù)內(nèi)容選擇幀率
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;
// 幀率計(jì)算邏輯
newConfigId = calculateRefreshRateConfigIndexType();
if (mFeatures.configId == newConfigId) {
return;
}
mFeatures.configId = newConfigId;
auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
}
calculateRefreshRateConfigIndexType 這個(gè)函數(shù)在計(jì)算顯示刷新率時(shí),對(duì)各種場(chǎng)景進(jìn)行了優(yōu)先級(jí)劃分,由高到低為:
- Power 事件
- Touch 事件
- Content是否刷新(是否處于Idle狀態(tài))
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;
// 該函數(shù)中最重要的調(diào)用
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 對(duì)應(yīng)的就是 HWC_POWER_MODE_NORMAL

getRefreshRateForContentV2 是排除掉更高優(yōu)先級(jí)場(chǎng)景后,計(jì)算最終顯示刷新率的函數(shù)。
其中 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,沒有內(nèi)容檢查,
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.
// 對(duì)應(yīng)上層 FRAME_RATE_COMPATIBILITY_DEFAULT
// Layer指定了幀率
// Android原生對(duì)這類圖層的策略是,如果存在這種圖層,得到touch事件也不會(huì)把幀率提高到最大
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) {
// 設(shè)置scores中幀率對(duì)應(yīng)的所有得分為 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是最大的幀率
// 這里通過當(dāng)前幀率與最大幀率相除,得出的值 < 1
// 最大幀率與最大幀率相除,得出的值 = 1
// 加上平方之后,小于最大幀率的其他幀率,得分遠(yuǎn)遠(yuǎn)小于最大幀率
// 通過這種方式,把最高分給到最高幀率
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);
// 該幀率對(duì)于這個(gè)Layer得分 × 權(quán)重
scores[i].second += weight * layerScore;
continue;
}
// 注意這里計(jì)算的是周期,不是幀率,跟幀率大小成反比 ?。。。? const auto displayPeriod = scores[i].first->vsyncPeriod;
// desiredRefreshRate 是LayerRequirement結(jié)構(gòu)體中的
// 前文提到,using Summary = std::vector<RefreshRateConfigs::LayerRequirement>;
// Summary中的refreshRate來自LayerInfoV2的getRefreshRate()函數(shù)
// 該函數(shù)返回的是{mLayerVote.type, mLayerVote.fps};
// mLayerVote中的fps是以下方式設(shè)置
// info->setLayerVote(voteType, frameRate.rate);
// FrameRate中的rate是上層setFrameRate接口傳下來的
// 所以 layer.desiredRefreshRate == frameRate.rate
const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
// 對(duì)應(yīng)上層 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要求的周期比當(dāng)前周期的大,那把當(dāng)前刷新周期一直增加
auto actualLayerPeriod = displayPeriod;
int multiplier = 1;
while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
multiplier++;
actualLayerPeriod = displayPeriod * multiplier;
}
// layerPeriod / actualLayerPeriod這個(gè)值有兩種可能
// 1. actualLayerPeriod < layerPeriod
// && actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION > layerPeriod
// 那么layerPeriod / actualLayerPeriod > 1,該幀率對(duì)于這個(gè)Layer得分 = 1
// 2. actualLayerPeriod > layerPeriod
// 那么layerPeriod / actualLayerPeriod < 1,該幀率對(duì)于這個(gè)Layer得分 < 1
// 可以看出來,第一種情況得分上比較占便宜。這是因?yàn)?,如果圖層刷新頻率大于顯示頻率,
// 會(huì)出現(xiàn)等待的情況,如果圖層刷新幀率小于顯示幀率,二者又很接近,那么每一幀都能即使得到顯示,并且也沒有很多重復(fù)幀的情況
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指定刷新率或者通過啟發(fā)式估算出來的Layer內(nèi)容刷新率的情況
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這個(gè)函數(shù)范圍的是layerPeriod / displayPeriod的
// 商(Quotient)和余數(shù)(remainder),其中包含四舍五入(800us)的情況
const auto [displayFramesQuot, displayFramesRem] =
getDisplayFrames(layerPeriod, displayPeriod);
static constexpr size_t MAX_FRAMES_TO_FIT =
10; // Stop calculating when score < 0.1
// 如果余數(shù) == 0,圖層所需的刷新率是顯示刷新率的整數(shù)倍數(shù)
// 認(rèn)為這種情況是匹配的,該幀率對(duì)于這個(gè)Layer得分 = 1
if (displayFramesRem == 0) {
// Layer desired refresh rate matches the display rate.
return 1.0f;
}
// 如果商 == 0,說明圖層所需幀率遠(yuǎn)遠(yuǎn)大于當(dāng)前提供的顯示幀率
// 這種情況相差越大,得分越低
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所需幀率遠(yuǎn)遠(yuǎn)小于顯示幀率
// 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;
}
總結(jié):
對(duì)上述邏輯做一下總結(jié):
- SDK & NDK中暴露了允許Applications根據(jù)自身場(chǎng)景對(duì)刷新幀率進(jìn)行投票的接口
- WMS對(duì)焦點(diǎn),分屏等情況對(duì)幀率優(yōu)先級(jí)的劃分,同時(shí)提供了黑名單機(jī)制
- SurfaceFlinger根據(jù)得到的信息對(duì)顯示幀率做最終決策
- 如果Applications或者Framework沒有設(shè)置刷新率,并且配置中打開了內(nèi)容刷新率統(tǒng)計(jì)邏輯,那么會(huì)根據(jù)圖層刷新歷史,對(duì)圖層內(nèi)容刷新率做啟發(fā)式估算,并更新投票類型(
LayerVoteType) - 如果Applications或者Framework設(shè)置了刷新率,以APP設(shè)置的為準(zhǔn),并更新投票類型(
LayerVoteType) - 遍歷每個(gè)Layer,計(jì)算Layer在顯示區(qū)域里面的占比,用作權(quán)重
- 考慮power、touch等特殊場(chǎng)景,這些場(chǎng)景優(yōu)先級(jí)更高
- 當(dāng)沒有特殊場(chǎng)景時(shí),根據(jù)不同
LayerVoteType的圖層數(shù)量,先決策一輪刷新率 - 如果上述都沒有決策成功,會(huì)遍歷所有Layer,遍歷所有支持的顯示幀率。根據(jù)Layer的要求,針對(duì)每個(gè)顯示幀率給出得分
- 最終得分最多的是最有可能滿足所有場(chǎng)景的幀率
- 如果Applications或者Framework沒有設(shè)置刷新率,并且配置中打開了內(nèi)容刷新率統(tǒng)計(jì)邏輯,那么會(huì)根據(jù)圖層刷新歷史,對(duì)圖層內(nèi)容刷新率做啟發(fā)式估算,并更新投票類型(
參考:
https://developer.android.com/guide/topics/media/frame-rate
我的csdn文章地址:
Android R Variable Refresh Rate 研究