SurfaceFlinger: more aggressive infrequent layer detection

Change the algorithm that chooses the refresh rate to treat layers as
infrequent unless proven otherwise. This change helps with multiple
switches during scenarios of blinking cursor where the detection of
infrequent layer is too long. The down side on this change is that
animations will be considered infrequent as well for the first few frames.
However the touch boost is a good mitigation of this.

Test: Typing in Messages and observe refresh rate
Test: Settings->About->up time and observe refresh rate
Bug: 155062712
Bug: 156654519
Change-Id: I317c69bd063df5d70f2d5705163cf61c1c9b1fff
This commit is contained in:
Ady Abraham 2020-05-15 11:51:48 -07:00
parent b209e7b56b
commit 1adbb72759
7 changed files with 77 additions and 96 deletions

View file

@ -90,7 +90,7 @@ LayerHistoryV2::~LayerHistoryV2() = default;
void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate, void LayerHistoryV2::registerLayer(Layer* layer, float /*lowRefreshRate*/, float highRefreshRate,
LayerVoteType type) { LayerVoteType type) {
const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate); const nsecs_t highRefreshRatePeriod = static_cast<nsecs_t>(1e9f / highRefreshRate);
auto info = std::make_unique<LayerInfoV2>(highRefreshRatePeriod, type); auto info = std::make_unique<LayerInfoV2>(layer->getName(), highRefreshRatePeriod, type);
std::lock_guard lock(mLock); std::lock_guard lock(mLock);
mLayerInfos.emplace_back(layer, std::move(info)); mLayerInfos.emplace_back(layer, std::move(info));
} }

View file

@ -27,8 +27,10 @@
namespace android::scheduler { namespace android::scheduler {
LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote) LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
: mHighRefreshRatePeriod(highRefreshRatePeriod), LayerHistory::LayerVoteType defaultVote)
: mName(name),
mHighRefreshRatePeriod(highRefreshRatePeriod),
mDefaultVote(defaultVote), mDefaultVote(defaultVote),
mLayerVote({defaultVote, 0.0f}) {} mLayerVote({defaultVote, 0.0f}) {}
@ -45,42 +47,23 @@ void LayerInfoV2::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now) {
} }
} }
bool LayerInfoV2::isFrameTimeValid(const FrameTimeData& frameTime) const {
return frameTime.queueTime >= std::chrono::duration_cast<std::chrono::nanoseconds>(
mFrameTimeValidSince.time_since_epoch())
.count();
}
bool LayerInfoV2::isFrequent(nsecs_t now) const { bool LayerInfoV2::isFrequent(nsecs_t now) const {
// Find the first valid frame time for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) {
auto it = mFrameTimes.begin(); if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) {
for (; it != mFrameTimes.end(); ++it) { ALOGV("%s infrequent (last frame is %.2fms ago", mName.c_str(),
if (isFrameTimeValid(*it)) { (now - mFrameTimes.back().queueTime) / 1e6f);
break; return false;
}
const auto numFrames = std::distance(mFrameTimes.crbegin(), it + 1);
if (numFrames >= FREQUENT_LAYER_WINDOW_SIZE) {
ALOGV("%s frequent (burst of %zu frames", mName.c_str(), numFrames);
return true;
} }
} }
// If we know nothing about this layer we consider it as frequent as it might be the start ALOGV("%s infrequent (not enough frames %zu)", mName.c_str(), mFrameTimes.size());
// of an animation. return false;
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;
} }
bool LayerInfoV2::hasEnoughDataForHeuristic() const { bool LayerInfoV2::hasEnoughDataForHeuristic() const {
@ -89,10 +72,6 @@ bool LayerInfoV2::hasEnoughDataForHeuristic() const {
return false; return false;
} }
if (!isFrameTimeValid(mFrameTimes.front())) {
return false;
}
if (mFrameTimes.size() < HISTORY_SIZE && if (mFrameTimes.size() < HISTORY_SIZE &&
mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) { mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
return false; return false;
@ -167,18 +146,22 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) { std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) { if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
return {mLayerVote.type, mLayerVote.fps}; return {mLayerVote.type, mLayerVote.fps};
} }
if (!isFrequent(now)) { if (!isFrequent(now)) {
ALOGV("%s is infrequent", mName.c_str());
return {LayerHistory::LayerVoteType::Min, 0}; return {LayerHistory::LayerVoteType::Min, 0};
} }
auto refreshRate = calculateRefreshRateIfPossible(); auto refreshRate = calculateRefreshRateIfPossible();
if (refreshRate.has_value()) { if (refreshRate.has_value()) {
ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()}; return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
} }
ALOGV("%s Max (can't resolve refresh rate", mName.c_str());
return {LayerHistory::LayerVoteType::Max, 0}; return {LayerHistory::LayerVoteType::Max, 0};
} }

View file

@ -54,7 +54,8 @@ class LayerInfoV2 {
friend class LayerHistoryTestV2; friend class LayerHistoryTestV2;
public: public:
LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote); LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
LayerHistory::LayerVoteType defaultVote);
LayerInfoV2(const LayerInfo&) = delete; LayerInfoV2(const LayerInfo&) = delete;
LayerInfoV2& operator=(const LayerInfoV2&) = delete; LayerInfoV2& operator=(const LayerInfoV2&) = delete;
@ -83,11 +84,7 @@ public:
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
void clearHistory() { void clearHistory() {
// Mark mFrameTimeValidSince to now to ignore all previous frame times. mFrameTimes.clear();
// We are not deleting the old frame to keep track of whether we should treat the first
// buffer as Max as we don't know anything about this layer or Min as this layer is
// posting infrequent updates.
mFrameTimeValidSince = std::chrono::steady_clock::now();
mLastReportedRefreshRate = 0.0f; mLastReportedRefreshRate = 0.0f;
} }
@ -101,7 +98,8 @@ private:
bool isFrequent(nsecs_t now) const; bool isFrequent(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const; bool hasEnoughDataForHeuristic() const;
std::optional<float> calculateRefreshRateIfPossible(); std::optional<float> calculateRefreshRateIfPossible();
bool isFrameTimeValid(const FrameTimeData&) const;
const std::string mName;
// Used for sanitizing the heuristic data // Used for sanitizing the heuristic data
const nsecs_t mHighRefreshRatePeriod; const nsecs_t mHighRefreshRatePeriod;
@ -118,8 +116,6 @@ private:
} mLayerVote; } mLayerVote;
std::deque<FrameTimeData> mFrameTimes; std::deque<FrameTimeData> mFrameTimes;
std::chrono::time_point<std::chrono::steady_clock> mFrameTimeValidSince =
std::chrono::steady_clock::now();
static constexpr size_t HISTORY_SIZE = 90; static constexpr size_t HISTORY_SIZE = 90;
static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s; static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
}; };

View file

@ -103,7 +103,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
ATRACE_CALL(); ATRACE_CALL();
ALOGV("getRefreshRateForContent %zu layers", layers.size()); ALOGV("getRefreshRateForContent %zu layers", layers.size());
*touchConsidered = false; if (touchConsidered) *touchConsidered = false;
std::lock_guard lock(mLock); std::lock_guard lock(mLock);
int noVoteLayers = 0; int noVoteLayers = 0;
@ -131,7 +131,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
// Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've
// selected a refresh rate to see if we should apply touch boost. // selected a refresh rate to see if we should apply touch boost.
if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) { if (touchActive && explicitDefaultVoteLayers == 0 && explicitExactOrMultipleVoteLayers == 0) {
*touchConsidered = true; ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
if (touchConsidered) *touchConsidered = true;
return getMaxRefreshRateByPolicyLocked(); return getMaxRefreshRateByPolicyLocked();
} }
@ -145,6 +146,7 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
// Only if all layers want Min we should return Min // Only if all layers want Min we should return Min
if (noVoteLayers + minVoteLayers == layers.size()) { if (noVoteLayers + minVoteLayers == layers.size()) {
ALOGV("all layers Min - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str());
return getMinRefreshRateByPolicyLocked(); return getMinRefreshRateByPolicyLocked();
} }
@ -243,9 +245,11 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
return 1.0f / iter; return 1.0f / iter;
}(); }();
ALOGV("%s (ExplicitExactOrMultiple, weight %.2f) %.2fHz gives %s score of %.2f", ALOGV("%s (%s, weight %.2f) %.2fHz gives %s score of %.2f", layer.name.c_str(),
layer.name.c_str(), weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layer.vote == LayerVoteType::ExplicitExactOrMultiple
layerScore); ? "ExplicitExactOrMultiple"
: "Heuristic",
weight, 1e9f / layerPeriod, scores[i].first->name.c_str(), layerScore);
scores[i].second += weight * layerScore; scores[i].second += weight * layerScore;
continue; continue;
} }
@ -266,7 +270,8 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
if (touchActive && explicitDefaultVoteLayers == 0 && if (touchActive && explicitDefaultVoteLayers == 0 &&
bestRefreshRate->fps < touchRefreshRate.fps) { bestRefreshRate->fps < touchRefreshRate.fps) {
*touchConsidered = true; if (touchConsidered) *touchConsidered = true;
ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
return touchRefreshRate; return touchRefreshRate;
} }

View file

@ -526,7 +526,9 @@ void Scheduler::idleTimerCallback(TimerState state) {
void Scheduler::touchTimerCallback(TimerState state) { void Scheduler::touchTimerCallback(TimerState state) {
const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */); if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) {
mLayerHistory->clear();
}
ATRACE_INT("TouchState", static_cast<int>(touch)); ATRACE_INT("TouchState", static_cast<int>(touch));
} }
@ -549,18 +551,19 @@ void Scheduler::dump(std::string& result) const {
} }
template <class T> template <class T>
void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
ConfigEvent event = ConfigEvent::None; ConfigEvent event = ConfigEvent::None;
HwcConfigIndexType newConfigId; HwcConfigIndexType newConfigId;
bool touchConsidered = false;
{ {
std::lock_guard<std::mutex> lock(mFeatureStateLock); std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) { if (*currentState == newState) {
return; return touchConsidered;
} }
*currentState = newState; *currentState = newState;
newConfigId = calculateRefreshRateConfigIndexType(); newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered);
if (mFeatures.configId == newConfigId) { if (mFeatures.configId == newConfigId) {
return; return touchConsidered;
} }
mFeatures.configId = newConfigId; mFeatures.configId = newConfigId;
if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) { if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) {
@ -569,10 +572,12 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO
} }
const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate, event); mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
return touchConsidered;
} }
HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() { HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) {
ATRACE_CALL(); ATRACE_CALL();
if (touchConsidered) *touchConsidered = false;
// If Display Power is not in normal operation we want to be in performance mode. When coming // 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. // back to normal mode, a grace period is given with DisplayPowerTimer.
@ -607,18 +612,9 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
.getConfigId(); .getConfigId();
} }
bool touchConsidered; return mRefreshRateConfigs
const auto& ret = mRefreshRateConfigs .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered)
.getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, .getConfigId();
&touchConsidered)
.getConfigId();
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;
} }
std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() { std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {

View file

@ -177,14 +177,15 @@ private:
// handles various timer features to change the refresh rate. // handles various timer features to change the refresh rate.
template <class T> template <class T>
void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection); bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
void setVsyncPeriod(nsecs_t period); void setVsyncPeriod(nsecs_t period);
// This function checks whether individual features that are affecting the refresh rate // This function checks whether individual features that are affecting the refresh rate
// selection were initialized, prioritizes them, and calculates the HwcConfigIndexType // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
// for the suggested refresh rate. // for the suggested refresh rate.
HwcConfigIndexType calculateRefreshRateConfigIndexType() REQUIRES(mFeatureStateLock); HwcConfigIndexType calculateRefreshRateConfigIndexType(bool* touchConsidered = nullptr)
REQUIRES(mFeatureStateLock);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection { struct Connection {

View file

@ -103,12 +103,22 @@ TEST_F(LayerHistoryTestV2, oneLayer) {
EXPECT_TRUE(history().summarize(time).empty()); EXPECT_TRUE(history().summarize(time).empty());
EXPECT_EQ(0, activeLayerCount()); EXPECT_EQ(0, activeLayerCount());
// The first few updates are considered infrequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
history().record(layer.get(), 0, time);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
// Max returned if active layers have insufficient history. // Max returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
history().record(layer.get(), 0, time); history().record(layer.get(), 0, time);
ASSERT_EQ(1, history().summarize(time).size()); ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
} }
// Max is returned since we have enough history but there is no timestamp votes. // Max is returned since we have enough history but there is no timestamp votes.
@ -117,6 +127,7 @@ TEST_F(LayerHistoryTestV2, oneLayer) {
ASSERT_EQ(1, history().summarize(time).size()); ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
} }
} }
@ -134,7 +145,7 @@ TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
auto summary = history().summarize(time); auto summary = history().summarize(time);
ASSERT_EQ(1, history().summarize(time).size()); ASSERT_EQ(1, history().summarize(time).size());
// Layer is still considered inactive so we expect to get Min // Layer is still considered inactive so we expect to get Min
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false)); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
@ -464,28 +475,15 @@ TEST_F(LayerHistoryTestV2, inactiveLayers) {
nsecs_t time = systemTime(); nsecs_t time = systemTime();
// the very first updates makes the layer frequent // The first few updates are considered infrequent
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
history().record(layer.get(), time, time); history().record(layer.get(), 0, time);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
ASSERT_EQ(1, history().summarize(time).size()); ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote); EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount()); EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time)); EXPECT_EQ(0, frequentLayerCount(time));
} }
// the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
history().record(layer.get(), time, time);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
// advance the time for the previous frame to be inactive // advance the time for the previous frame to be inactive
time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
@ -528,9 +526,11 @@ TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
nsecs_t time = systemTime(); nsecs_t time = systemTime();
// Post a buffer to the layers to make them active // Post a few buffers to the layers to make them active
history().record(explicitVisiblelayer.get(), time, time); for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
history().record(explicitInvisiblelayer.get(), time, time); history().record(explicitVisiblelayer.get(), time, time);
history().record(explicitInvisiblelayer.get(), time, time);
}
EXPECT_EQ(2, layerCount()); EXPECT_EQ(2, layerCount());
ASSERT_EQ(1, history().summarize(time).size()); ASSERT_EQ(1, history().summarize(time).size());