SF: Don't bump to PERFORMANCE refresh rate with infrequent updates
Testing scenario:
1. Set brightness around 50%-55%, set dark theme.
2. Open Chrome.
3. Click search bar.
4. Can see the cursor show up.
Notice the flickering of the screen.
Explanation:
Kernel idle timer detects inactivity after 100ms, and turns the refresh rate to 60Hz.
When a cursor update happens (every 500ms), SF receives a new frame, notifies kernel,
the refresh rate bumps to 90Hz. After 100ms the kernel again decreases the refresh rate to 60Hz.
Desired goals:
Stop the flickering (eg. changing between 60-90Hz too often).
Continue having low battery impact.
Solution in this AG:
Add logic to SF to detect infrequent updates (for all layers). Description of the algorithm:
1) Store the timestamp of the last two buffers.
2) If the first buffer is older than 250 ms, detect inactivity, go into DEFAULT refresh rate.
3) EXIT: on touch event, layer requests 2 or more frames in less than 250ms.
NOTE: if the application is explicitly requesting 90Hz, SF does not override that. Idle kernel
still kicks in, and the flickering happens.
tested on Chrome v74 Beta, and messaging app.
Test: manual, b/135009095
Bug: 135718869
Change-Id: I72d8cd48b3ec900989afcf0fab1cdc3046b87274
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 90d4269..2c50053 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -96,8 +96,9 @@
return false;
}
- // The last update should not be older than TIME_EPSILON_NS nanoseconds.
- const int64_t obsoleteEpsilon = systemTime() - scheduler::TIME_EPSILON_NS.count();
+ // The last update should not be older than OBSOLETE_TIME_EPSILON_NS nanoseconds.
+ const int64_t obsoleteEpsilon =
+ systemTime() - scheduler::OBSOLETE_TIME_EPSILON_NS.count();
if (mElements.at(mElements.size() - 1) < obsoleteEpsilon) {
return false;
}
@@ -105,6 +106,25 @@
return true;
}
+ bool isLowActivityLayer() const {
+ // We want to make sure that we received more than two frames from the layer
+ // in order to check low activity.
+ if (mElements.size() < 2) {
+ return false;
+ }
+
+ const int64_t obsoleteEpsilon =
+ systemTime() - scheduler::LOW_ACTIVITY_EPSILON_NS.count();
+ // Check the frame before last to determine whether there is low activity.
+ // If that frame is older than LOW_ACTIVITY_EPSILON_NS, the layer is sending
+ // infrequent updates.
+ if (mElements.at(mElements.size() - 2) < obsoleteEpsilon) {
+ return true;
+ }
+
+ return false;
+ }
+
void clearHistory() { mElements.clear(); }
private:
@@ -114,7 +134,7 @@
};
public:
- LayerInfo(const std::string name, float maxRefreshRate);
+ LayerInfo(const std::string name, float minRefreshRate, float maxRefreshRate);
~LayerInfo();
LayerInfo(const LayerInfo&) = delete;
@@ -144,6 +164,10 @@
// Calculate the average refresh rate.
float getDesiredRefreshRate() const {
std::lock_guard lock(mLock);
+
+ if (mPresentTimeHistory.isLowActivityLayer()) {
+ return 1e9f / mLowActivityRefreshDuration;
+ }
return mRefreshRateHistory.getRefreshRateAvg();
}
@@ -175,6 +199,7 @@
private:
const std::string mName;
const nsecs_t mMinRefreshDuration;
+ const nsecs_t mLowActivityRefreshDuration;
mutable std::mutex mLock;
nsecs_t mLastUpdatedTime GUARDED_BY(mLock) = 0;
nsecs_t mLastPresentTime GUARDED_BY(mLock) = 0;