Merge "SurfaceFlinger: more aggressive infrequent layer detection" into rvc-dev am: 51a1625ac0
Change-Id: Ib129989cc509ff51f4f747d9fc8b9f3adb64f58c
This commit is contained in:
commit
7053d9017d
7 changed files with 77 additions and 96 deletions
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in a new issue