Merge "SurfaceFlinger: more aggressive infrequent layer detection" into rvc-dev am: 51a1625ac0

Change-Id: Ib129989cc509ff51f4f747d9fc8b9f3adb64f58c
This commit is contained in:
Ady Abraham 2020-05-18 21:21:11 +00:00 committed by Automerger Merge Worker
commit 7053d9017d
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,
LayerVoteType type) {
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);
mLayerInfos.emplace_back(layer, std::move(info));
}

View file

@ -27,8 +27,10 @@
namespace android::scheduler {
LayerInfoV2::LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote)
: mHighRefreshRatePeriod(highRefreshRatePeriod),
LayerInfoV2::LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
LayerHistory::LayerVoteType defaultVote)
: mName(name),
mHighRefreshRatePeriod(highRefreshRatePeriod),
mDefaultVote(defaultVote),
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 {
// Find the first valid frame time
auto it = mFrameTimes.begin();
for (; it != mFrameTimes.end(); ++it) {
if (isFrameTimeValid(*it)) {
break;
for (auto it = mFrameTimes.crbegin(); it != mFrameTimes.crend(); ++it) {
if (now - it->queueTime >= MAX_FREQUENT_LAYER_PERIOD_NS.count()) {
ALOGV("%s infrequent (last frame is %.2fms ago", mName.c_str(),
(now - mFrameTimes.back().queueTime) / 1e6f);
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
// 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;
ALOGV("%s infrequent (not enough frames %zu)", mName.c_str(), mFrameTimes.size());
return false;
}
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
@ -89,10 +72,6 @@ bool LayerInfoV2::hasEnoughDataForHeuristic() const {
return false;
}
if (!isFrameTimeValid(mFrameTimes.front())) {
return false;
}
if (mFrameTimes.size() < HISTORY_SIZE &&
mFrameTimes.back().queueTime - mFrameTimes.front().queueTime < HISTORY_TIME.count()) {
return false;
@ -167,18 +146,22 @@ std::optional<float> LayerInfoV2::calculateRefreshRateIfPossible() {
std::pair<LayerHistory::LayerVoteType, float> LayerInfoV2::getRefreshRate(nsecs_t now) {
if (mLayerVote.type != LayerHistory::LayerVoteType::Heuristic) {
ALOGV("%s voted %d ", mName.c_str(), static_cast<int>(mLayerVote.type));
return {mLayerVote.type, mLayerVote.fps};
}
if (!isFrequent(now)) {
ALOGV("%s is infrequent", mName.c_str());
return {LayerHistory::LayerVoteType::Min, 0};
}
auto refreshRate = calculateRefreshRateIfPossible();
if (refreshRate.has_value()) {
ALOGV("%s calculated refresh rate: %.2f", mName.c_str(), refreshRate.value());
return {LayerHistory::LayerVoteType::Heuristic, refreshRate.value()};
}
ALOGV("%s Max (can't resolve refresh rate", mName.c_str());
return {LayerHistory::LayerVoteType::Max, 0};
}

View file

@ -54,7 +54,8 @@ class LayerInfoV2 {
friend class LayerHistoryTestV2;
public:
LayerInfoV2(nsecs_t highRefreshRatePeriod, LayerHistory::LayerVoteType defaultVote);
LayerInfoV2(const std::string& name, nsecs_t highRefreshRatePeriod,
LayerHistory::LayerVoteType defaultVote);
LayerInfoV2(const LayerInfo&) = delete;
LayerInfoV2& operator=(const LayerInfoV2&) = delete;
@ -83,11 +84,7 @@ public:
nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; }
void clearHistory() {
// Mark mFrameTimeValidSince to now to ignore all previous frame times.
// 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();
mFrameTimes.clear();
mLastReportedRefreshRate = 0.0f;
}
@ -101,7 +98,8 @@ private:
bool isFrequent(nsecs_t now) const;
bool hasEnoughDataForHeuristic() const;
std::optional<float> calculateRefreshRateIfPossible();
bool isFrameTimeValid(const FrameTimeData&) const;
const std::string mName;
// Used for sanitizing the heuristic data
const nsecs_t mHighRefreshRatePeriod;
@ -118,8 +116,6 @@ private:
} mLayerVote;
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 std::chrono::nanoseconds HISTORY_TIME = 1s;
};

View file

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

View file

@ -526,7 +526,9 @@ void Scheduler::idleTimerCallback(TimerState state) {
void Scheduler::touchTimerCallback(TimerState state) {
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));
}
@ -549,18 +551,19 @@ void Scheduler::dump(std::string& result) const {
}
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;
HwcConfigIndexType newConfigId;
bool touchConsidered = false;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) {
return;
return touchConsidered;
}
*currentState = newState;
newConfigId = calculateRefreshRateConfigIndexType();
newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered);
if (mFeatures.configId == newConfigId) {
return;
return touchConsidered;
}
mFeatures.configId = newConfigId;
if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) {
@ -569,10 +572,12 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO
}
const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
return touchConsidered;
}
HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) {
ATRACE_CALL();
if (touchConsidered) *touchConsidered = false;
// 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.
@ -607,18 +612,9 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType() {
.getConfigId();
}
bool touchConsidered;
const auto& ret = mRefreshRateConfigs
.getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle,
&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;
return mRefreshRateConfigs
.getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered)
.getConfigId();
}
std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {

View file

@ -177,14 +177,15 @@ private:
// handles various timer features to change the refresh rate.
template <class T>
void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
void setVsyncPeriod(nsecs_t period);
// This function checks whether individual features that are affecting the refresh rate
// selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
// 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.
struct Connection {

View file

@ -103,12 +103,22 @@ TEST_F(LayerHistoryTestV2, oneLayer) {
EXPECT_TRUE(history().summarize(time).empty());
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.
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);
ASSERT_EQ(1, history().summarize(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
}
// 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());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
}
}
@ -134,7 +145,7 @@ TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
auto summary = history().summarize(time);
ASSERT_EQ(1, history().summarize(time).size());
// 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_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
@ -464,28 +475,15 @@ TEST_F(LayerHistoryTestV2, inactiveLayers) {
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++) {
history().record(layer.get(), time, time);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, layerCount());
history().record(layer.get(), 0, time);
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, 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
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
@ -528,9 +526,11 @@ TEST_F(LayerHistoryTestV2, invisibleExplicitLayer) {
nsecs_t time = systemTime();
// Post a buffer to the layers to make them active
history().record(explicitVisiblelayer.get(), time, time);
history().record(explicitInvisiblelayer.get(), time, time);
// Post a few buffers to the layers to make them active
for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) {
history().record(explicitVisiblelayer.get(), time, time);
history().record(explicitInvisiblelayer.get(), time, time);
}
EXPECT_EQ(2, layerCount());
ASSERT_EQ(1, history().summarize(time).size());