Merge changes from topic "b:140112642:5" into qt-surfaceflinger-dev
* changes:
SurfaceFlinger: clamp frame refresh duration to min refresh duration
SurfaceFlinger: calculate expected present once
[SurfaceFlinger] Exclude first vsync duration from period calculation
SurfaceFlinger: tune LayerHistory to be less aggressive
[SurfaceFlinger] Don't touch hw vsync in DEFAULT RR with kernel timeout
SurfaceFlinger: add display power state timer
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 83fd42b..0c94052 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -668,7 +668,13 @@
nsecs_t durationSum = 0;
nsecs_t minDuration = INT64_MAX;
nsecs_t maxDuration = 0;
- for (size_t i = 1; i < mNumResyncSamples; i++) {
+ // We skip the first 2 samples because the first vsync duration on some
+ // devices may be much more inaccurate than on other devices, e.g. due
+ // to delays in ramping up from a power collapse. By doing so this
+ // actually increases the accuracy of the DispSync model even though
+ // we're effectively relying on fewer sample points.
+ static constexpr size_t numSamplesSkipped = 2;
+ for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
size_t prev = (idx + MAX_RESYNC_SAMPLES - 1) % MAX_RESYNC_SAMPLES;
nsecs_t duration = mResyncSamples[idx] - mResyncSamples[prev];
@@ -679,15 +685,14 @@
// Exclude the min and max from the average
durationSum -= minDuration + maxDuration;
- mPeriod = durationSum / (mNumResyncSamples - 3);
+ mPeriod = durationSum / (mNumResyncSamples - numSamplesSkipped - 2);
ALOGV("[%s] mPeriod = %" PRId64, mName, ns2us(mPeriod));
double sampleAvgX = 0;
double sampleAvgY = 0;
double scale = 2.0 * M_PI / double(mPeriod);
- // Intentionally skip the first sample
- for (size_t i = 1; i < mNumResyncSamples; i++) {
+ for (size_t i = numSamplesSkipped; i < mNumResyncSamples; i++) {
size_t idx = (mFirstResyncSample + i) % MAX_RESYNC_SAMPLES;
nsecs_t sample = mResyncSamples[idx] - mReferenceTime;
double samplePhase = double(sample % mPeriod) * scale;
@@ -695,8 +700,8 @@
sampleAvgY += sin(samplePhase);
}
- sampleAvgX /= double(mNumResyncSamples - 1);
- sampleAvgY /= double(mNumResyncSamples - 1);
+ sampleAvgX /= double(mNumResyncSamples - numSamplesSkipped);
+ sampleAvgY /= double(mNumResyncSamples - numSamplesSkipped);
mPhase = nsecs_t(atan2(sampleAvgY, sampleAvgX) / scale);
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 38afb7e..6e5df12 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -43,7 +43,7 @@
mLastPresentTime = lastPresentTime;
// Ignore time diff that are too high - those are stale values
if (timeDiff > OBSOLETE_TIME_EPSILON_NS.count()) return;
- const nsecs_t refreshDuration = (timeDiff > 0) ? timeDiff : mMinRefreshDuration;
+ const nsecs_t refreshDuration = std::max(timeDiff, mMinRefreshDuration);
const int fps = 1e9f / refreshDuration;
mRefreshRateHistory.insertRefreshRate(fps);
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 66df9dc..a733781 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -129,8 +129,8 @@
private:
std::deque<nsecs_t> mElements;
- static constexpr size_t HISTORY_SIZE = 10;
- static constexpr std::chrono::nanoseconds HISTORY_TIME = 500ms;
+ static constexpr size_t HISTORY_SIZE = 90;
+ static constexpr std::chrono::nanoseconds HISTORY_TIME = 1s;
};
public:
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index cfdbd91..a194106 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -76,6 +76,7 @@
mSupportKernelTimer = support_kernel_idle_timer(false);
mSetTouchTimerMs = set_touch_timer_ms(0);
+ mSetDisplayPowerTimerMs = set_display_power_timer_ms(0);
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.set_idle_timer_ms", value, "0");
@@ -110,10 +111,22 @@
[this] { expiredTouchTimerCallback(); });
mTouchTimer->start();
}
+
+ if (mSetDisplayPowerTimerMs > 0) {
+ mDisplayPowerTimer =
+ std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+ mSetDisplayPowerTimerMs),
+ [this] { resetDisplayPowerTimerCallback(); },
+ [this] {
+ expiredDisplayPowerTimerCallback();
+ });
+ mDisplayPowerTimer->start();
+ }
}
Scheduler::~Scheduler() {
// Ensure the IdleTimer thread is joined before we start destroying state.
+ mDisplayPowerTimer.reset();
mTouchTimer.reset();
mIdleTimer.reset();
}
@@ -380,11 +393,17 @@
}
void Scheduler::setChangeRefreshRateCallback(
- const ChangeRefreshRateCallback& changeRefreshRateCallback) {
+ const ChangeRefreshRateCallback&& changeRefreshRateCallback) {
std::lock_guard<std::mutex> lock(mCallbackLock);
mChangeRefreshRateCallback = changeRefreshRateCallback;
}
+void Scheduler::setGetCurrentRefreshRateTypeCallback(
+ const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateTypeCallback) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mGetCurrentRefreshRateTypeCallback = getCurrentRefreshRateTypeCallback;
+}
+
void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
std::lock_guard<std::mutex> lock(mCallbackLock);
mGetVsyncPeriod = getVsyncPeriod;
@@ -419,41 +438,75 @@
mLayerHistory.clearHistory();
}
+void Scheduler::setDisplayPowerState(bool normal) {
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mIsDisplayPowerStateNormal = normal;
+ }
+
+ if (mDisplayPowerTimer) {
+ mDisplayPowerTimer->reset();
+ }
+
+ // Display Power event will boost the refresh rate to performance.
+ // Clear Layer History to get fresh FPS detection
+ mLayerHistory.clearHistory();
+}
+
void Scheduler::resetTimerCallback() {
- timerChangeRefreshRate(IdleTimerState::RESET);
+ handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false);
ATRACE_INT("ExpiredIdleTimer", 0);
}
void Scheduler::resetKernelTimerCallback() {
ATRACE_INT("ExpiredKernelIdleTimer", 0);
std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mGetVsyncPeriod) {
- resyncToHardwareVsync(true, mGetVsyncPeriod());
+ if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) {
+ // If we're not in performance mode then the kernel timer shouldn't do
+ // anything, as the refresh rate during DPU power collapse will be the
+ // same.
+ if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) {
+ resyncToHardwareVsync(true, mGetVsyncPeriod());
+ }
}
}
void Scheduler::expiredTimerCallback() {
- timerChangeRefreshRate(IdleTimerState::EXPIRED);
+ handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false);
ATRACE_INT("ExpiredIdleTimer", 1);
}
void Scheduler::resetTouchTimerCallback() {
- // We do not notify the applications about config changes when idle timer is reset.
- touchChangeRefreshRate(TouchState::ACTIVE);
+ handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true);
ATRACE_INT("TouchState", 1);
}
void Scheduler::expiredTouchTimerCallback() {
- // We do not notify the applications about config changes when idle timer expires.
- touchChangeRefreshRate(TouchState::INACTIVE);
+ handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true);
ATRACE_INT("TouchState", 0);
}
+void Scheduler::resetDisplayPowerTimerCallback() {
+ handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true);
+ ATRACE_INT("ExpiredDisplayPowerTimer", 0);
+}
+
+void Scheduler::expiredDisplayPowerTimerCallback() {
+ handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true);
+ ATRACE_INT("ExpiredDisplayPowerTimer", 1);
+}
+
void Scheduler::expiredKernelTimerCallback() {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
ATRACE_INT("ExpiredKernelIdleTimer", 1);
- // Disable HW Vsync if the timer expired, as we don't need it
- // enabled if we're not pushing frames.
- disableHardwareVsync(false);
+ if (mGetCurrentRefreshRateTypeCallback) {
+ if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) {
+ // Disable HW Vsync if the timer expired, as we don't need it
+ // enabled if we're not pushing frames, and if we're in PERFORMANCE
+ // mode then we'll need to re-update the DispSync model anyways.
+ disableHardwareVsync(false);
+ }
+ }
}
std::string Scheduler::doDump() {
@@ -463,39 +516,23 @@
return stream.str();
}
-void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
- RefreshRateType newRefreshRateType;
- {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mCurrentIdleTimerState == idleTimerState) {
- return;
- }
- mCurrentIdleTimerState = idleTimerState;
- newRefreshRateType = calculateRefreshRateType();
- if (mRefreshRateType == newRefreshRateType) {
- return;
- }
- mRefreshRateType = newRefreshRateType;
- }
- changeRefreshRate(newRefreshRateType, ConfigEvent::None);
-}
-
-void Scheduler::touchChangeRefreshRate(TouchState touchState) {
+template <class T>
+void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
ConfigEvent event = ConfigEvent::None;
RefreshRateType newRefreshRateType;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mCurrentTouchState == touchState) {
+ if (*currentState == newState) {
return;
}
- mCurrentTouchState = touchState;
+ *currentState = newState;
newRefreshRateType = calculateRefreshRateType();
if (mRefreshRateType == newRefreshRateType) {
return;
}
mRefreshRateType = newRefreshRateType;
- // Send an event in case that content detection is on as touch has a higher priority
- if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
+ if (eventOnContentDetection &&
+ mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) {
event = ConfigEvent::Changed;
}
}
@@ -508,6 +545,12 @@
return RefreshRateType::DEFAULT;
}
+ // 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 (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) {
+ return RefreshRateType::PERFORMANCE;
+ }
+
// As long as touch is active we want to be in performance mode
if (mCurrentTouchState == TouchState::ACTIVE) {
return RefreshRateType::PERFORMANCE;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index eaad37c..5d8bb4c 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -49,6 +49,7 @@
}
using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>;
using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
using GetVsyncPeriod = std::function<nsecs_t()>;
@@ -165,7 +166,9 @@
// Updates FPS based on the most content presented.
void updateFpsBasedOnContent();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
- void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+ void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback);
+ void setGetCurrentRefreshRateTypeCallback(
+ const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateType);
void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
// Returns whether idle timer is enabled or not
@@ -177,6 +180,9 @@
// Function that resets the touch timer.
void notifyTouchEvent();
+ // Function that sets whether display power mode is normal or not.
+ void setDisplayPowerState(bool normal);
+
// Returns relevant information about Scheduler for dumpsys purposes.
std::string doDump();
@@ -197,6 +203,7 @@
enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF };
enum class IdleTimerState { EXPIRED, RESET };
enum class TouchState { INACTIVE, ACTIVE };
+ enum class DisplayPowerTimerState { EXPIRED, RESET };
// Creates a connection on the given EventThread and forwards the given callbacks.
sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&,
@@ -221,12 +228,15 @@
void resetTouchTimerCallback();
// Function that is called when the touch timer expires.
void expiredTouchTimerCallback();
+ // Function that is called when the display power timer resets.
+ void resetDisplayPowerTimerCallback();
+ // Function that is called when the display power timer expires.
+ void expiredDisplayPowerTimerCallback();
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
- // Idle timer feature's function to change the refresh rate.
- void timerChangeRefreshRate(IdleTimerState idleTimerState);
- // Touch timer feature's function to change the refresh rate.
- void touchChangeRefreshRate(TouchState touchState);
+ // handles various timer features to change the refresh rate.
+ template <class T>
+ void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
// Calculate the new refresh rate type
RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
// Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
@@ -282,7 +292,12 @@
int64_t mSetTouchTimerMs = 0;
std::unique_ptr<scheduler::IdleTimer> mTouchTimer;
+ // Timer used to monitor display power mode.
+ int64_t mSetDisplayPowerTimerMs = 0;
+ std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer;
+
std::mutex mCallbackLock;
+ GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock);
ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
@@ -293,9 +308,12 @@
ContentFeatureState::CONTENT_DETECTION_OFF;
IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE;
+ DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) =
+ DisplayPowerTimerState::EXPIRED;
uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock);
RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock);
bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false;
+ bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true;
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a2c87bd..42188d1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -715,6 +715,24 @@
Mutex::Autolock lock(mStateLock);
setRefreshRateTo(type, event);
});
+ mScheduler->setGetCurrentRefreshRateTypeCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display) {
+ // If we don't have a default display the fallback to the default
+ // refresh rate type
+ return RefreshRateType::DEFAULT;
+ }
+
+ const int configId = display->getActiveConfig();
+ for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) {
+ if (refresh && refresh->configId == configId) {
+ return type;
+ }
+ }
+ // This should never happen, but just gracefully fallback to default.
+ return RefreshRateType::DEFAULT;
+ });
mScheduler->setGetVsyncPeriodCallback([this] {
Mutex::Autolock lock(mStateLock);
return getVsyncPeriod();
@@ -1047,6 +1065,7 @@
desiredActiveConfigChangeDone();
return false;
}
+
mUpcomingActiveConfig = desiredActiveConfig;
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
@@ -1669,22 +1688,26 @@
return fence != Fence::NO_FENCE && (fence->getStatus() == Fence::Status::Unsignaled);
}
-nsecs_t SurfaceFlinger::getExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS {
+void SurfaceFlinger::populateExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS {
DisplayStatInfo stats;
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
// Inflate the expected present time if we're targetting the next vsync.
- const nsecs_t correctedTime =
+ mExpectedPresentTime =
mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
? presentTime
: presentTime + stats.vsyncPeriod;
- return correctedTime;
}
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
+ // calculate the expected present time once and use the cached
+ // value throughout this frame to make sure all layers are
+ // seeing this same value.
+ populateExpectedPresentTime();
+
bool frameMissed = previousFrameMissed();
bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
bool gpuFrameMissed = mHadClientComposition && frameMissed;
@@ -4540,6 +4563,7 @@
if (display->isPrimary()) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats.setPowerMode(mode);
+ mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(*displayId).c_str());
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index a8057aa..ec0b7c5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -301,10 +301,11 @@
// TODO: this should be made accessible only to MessageQueue
void onMessageReceived(int32_t what);
- // Returns the expected present time for this frame.
+ // populates the expected present time for this frame.
// When we are in negative offsets, we perform a correction so that the
// predicted vsync for the *next* frame is used instead.
- nsecs_t getExpectedPresentTime();
+ void populateExpectedPresentTime();
+ nsecs_t getExpectedPresentTime() const { return mExpectedPresentTime; }
// for debugging only
// TODO: this should be made accessible only to HWComposer
@@ -1184,6 +1185,8 @@
// Flags to capture the state of Vsync in HWC
HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
+
+ nsecs_t mExpectedPresentTime;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 2b33ba1..3eedb67 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -234,6 +234,14 @@
return defaultValue;
}
+int32_t set_display_power_timer_ms(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::set_display_power_timer_ms();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
bool use_smart_90_for_video(bool defaultValue) {
auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 1964ccd..68986f7 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -74,6 +74,8 @@
int32_t set_touch_timer_ms(int32_t defaultValue);
+int32_t set_display_power_timer_ms(int32_t defaultValue);
+
bool use_smart_90_for_video(bool defaultValue);
bool enable_protected_contents(bool defaultValue);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index decabd5..c77137a 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -309,6 +309,18 @@
prop_name: "ro.surface_flinger.set_touch_timer_ms"
}
+# setDisplayPowerTimerMs indicates what is considered a timeout in milliseconds for Scheduler.
+# This value is used by the Scheduler to trigger display power inactivity callbacks that will
+# keep the display in peak refresh rate as long as display power is not in normal mode.
+# Setting this property to 0 means there is no timer.
+prop {
+ api_name: "set_display_power_timer_ms"
+ type: Integer
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.set_display_power_timer_ms"
+}
+
# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
# screen refresh rate based on that.
prop {
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
index 6ae3ac1..794c1d6 100644
--- a/services/surfaceflinger/sysprop/api/system-current.txt
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -17,6 +17,7 @@
method public static java.util.Optional<java.lang.Long> present_time_offset_from_vsync_ns();
method public static java.util.Optional<android.sysprop.SurfaceFlingerProperties.primary_display_orientation_values> primary_display_orientation();
method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework();
+ method public static java.util.Optional<java.lang.Integer> set_display_power_timer_ms();
method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms();
method public static java.util.Optional<java.lang.Integer> set_touch_timer_ms();
method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service();
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 7ec9066..8e7440c 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -27,6 +27,15 @@
static constexpr float MIN_REFRESH_RATE = 30.f;
static constexpr float MAX_REFRESH_RATE = 90.f;
+ static constexpr auto RELEVANT_FRAME_THRESHOLD = 90u;
+ static constexpr uint64_t THIRTY_FPS_INTERVAL = 33'333'333;
+
+ void forceRelevancy(const std::unique_ptr<LayerHistory::LayerHandle>& testLayer) {
+ mLayerHistory->setVisibility(testLayer, true);
+ for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ }
+ };
};
LayerHistoryTest::LayerHistoryTest() {
@@ -39,24 +48,18 @@
std::unique_ptr<LayerHistory::LayerHandle> testLayer =
mLayerHistory->createLayer("TestLayer", MIN_REFRESH_RATE, MAX_REFRESH_RATE);
mLayerHistory->setVisibility(testLayer, true);
+ for (auto i = 0u; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ }
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
-
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- // This is still 0, because the layer is not considered recently active if it
- // has been present in less than 10 frames.
- EXPECT_FLOAT_EQ(0.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
- // This should be MAX_REFRESH_RATE as we have more than 10 samples
- EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ // Add a few more. This time we should get MAX refresh rate as the layer
+ // becomes relevant
+ static constexpr auto A_FEW = 10;
+ for (auto i = 0u; i < A_FEW; i++) {
+ EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
+ mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
+ }
}
TEST_F(LayerHistoryTest, oneHDRLayer) {
@@ -79,8 +82,9 @@
mLayerHistory->setVisibility(test30FpsLayer, true);
nsecs_t startTime = systemTime();
- for (int i = 0; i < 31; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
}
EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
@@ -98,19 +102,21 @@
mLayerHistory->setVisibility(testLayer2, true);
nsecs_t startTime = systemTime();
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
mLayerHistory->insert(testLayer, 0, false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
startTime = systemTime();
- for (int i = 0; i < 10; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ for (int i = 0; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
- for (int i = 10; i < 30; i++) {
- mLayerHistory->insert(test30FpsLayer, startTime + (i * 33333333), false /*isHDR*/);
+ for (int i = 10; i < RELEVANT_FRAME_THRESHOLD; i++) {
+ mLayerHistory->insert(test30FpsLayer, startTime + (i * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
}
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
@@ -122,8 +128,10 @@
EXPECT_FLOAT_EQ(MAX_REFRESH_RATE, mLayerHistory->getDesiredRefreshRateAndHDR().first);
// After 1200 ms frames become obsolete.
std::this_thread::sleep_for(std::chrono::milliseconds(1500));
- // Insert the 31st frame.
- mLayerHistory->insert(test30FpsLayer, startTime + (30 * 33333333), false /*isHDR*/);
+
+ mLayerHistory->insert(test30FpsLayer,
+ startTime + (RELEVANT_FRAME_THRESHOLD * THIRTY_FPS_INTERVAL),
+ false /*isHDR*/);
EXPECT_FLOAT_EQ(30.f, mLayerHistory->getDesiredRefreshRateAndHDR().first);
}