Support repurposing idle timer to work with kernel timer.
Idle timer would instead enable/disable hw vsyncs in accordance with
inactivity.
Bug: 130684082
Test: systrace
Change-Id: I289600fc41b7ded863c9486cf6c2ab6155b06550
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/IdleTimer.cpp
index b28b1aa..37fdfc7 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.cpp
+++ b/services/surfaceflinger/Scheduler/IdleTimer.cpp
@@ -84,8 +84,11 @@
constexpr auto zero = std::chrono::steady_clock::duration::zero();
auto waitTime = triggerTime - std::chrono::steady_clock::now();
if (waitTime > zero) mCondition.wait_for(mMutex, waitTime);
- if (mState == TimerState::WAITING &&
- (triggerTime - std::chrono::steady_clock::now()) <= zero) {
+ if (mState == TimerState::RESET) {
+ triggerTime = std::chrono::steady_clock::now() + mInterval;
+ mState = TimerState::WAITING;
+ } else if (mState == TimerState::WAITING &&
+ (triggerTime - std::chrono::steady_clock::now()) <= zero) {
triggerTimeout = true;
mState = TimerState::IDLE;
}
diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/IdleTimer.h
index 19f1267..2646688 100644
--- a/services/surfaceflinger/Scheduler/IdleTimer.h
+++ b/services/surfaceflinger/Scheduler/IdleTimer.h
@@ -39,13 +39,31 @@
const TimeoutCallback& timeoutCallback);
~IdleTimer();
+ // Initializes and turns on the idle timer.
void start();
+ // Stops the idle timer and any held resources.
void stop();
+ // Resets the wakeup time and fires the reset callback.
void reset();
private:
// Enum to track in what state is the timer.
- enum class TimerState { STOPPED = 0, RESET = 1, WAITING = 2, IDLE = 3 };
+ enum class TimerState {
+ // The internal timer thread has been destroyed, and no state is
+ // tracked.
+ // Possible state transitions: RESET
+ STOPPED = 0,
+ // An external thread has just reset this timer.
+ // If there is a reset callback, then that callback is fired.
+ // Possible state transitions: STOPPED, WAITING
+ RESET = 1,
+ // This timer is waiting for the timeout interval to expire.
+ // Possible state transaitions: STOPPED, RESET, IDLE
+ WAITING = 2,
+ // The timeout interval has expired, so we are sleeping now.
+ // Possible state transaitions: STOPPED, RESET
+ IDLE = 3
+ };
// Function that loops until the condition for stopping is met.
void loop();
@@ -59,6 +77,7 @@
// Lock used for synchronizing the waiting thread with the application thread.
std::mutex mMutex;
+ // Current timer state
TimerState mState GUARDED_BY(mMutex) = TimerState::RESET;
// Interval after which timer expires.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 88d2638..b91c66e 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -73,6 +73,7 @@
mEventControlThread = std::make_unique<impl::EventControlThread>(function);
mSetIdleTimerMs = set_idle_timer_ms(0);
+ mSupportKernelTimer = support_kernel_idle_timer(false);
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.set_idle_timer_ms", value, "0");
@@ -82,10 +83,20 @@
}
if (mSetIdleTimerMs > 0) {
- mIdleTimer =
- std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetIdleTimerMs),
- [this] { resetTimerCallback(); },
- [this] { expiredTimerCallback(); });
+ if (mSupportKernelTimer) {
+ mIdleTimer =
+ std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+ mSetIdleTimerMs),
+ [this] { resetKernelTimerCallback(); },
+ [this] {
+ expiredKernelTimerCallback();
+ });
+ } else {
+ mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(
+ mSetIdleTimerMs),
+ [this] { resetTimerCallback(); },
+ [this] { expiredTimerCallback(); });
+ }
mIdleTimer->start();
}
}
@@ -344,6 +355,11 @@
mChangeRefreshRateCallback = changeRefreshRateCallback;
}
+void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mGetVsyncPeriod = getVsyncPeriod;
+}
+
void Scheduler::updateFrameSkipping(const int64_t skipCount) {
ATRACE_INT("FrameSkipCount", skipCount);
if (mSkipCount != skipCount) {
@@ -360,17 +376,30 @@
}
void Scheduler::resetTimerCallback() {
- // We do not notify the applications about config changes when idle timer is reset.
timerChangeRefreshRate(IdleTimerState::RESET);
ATRACE_INT("ExpiredIdleTimer", 0);
}
+void Scheduler::resetKernelTimerCallback() {
+ ATRACE_INT("ExpiredKernelIdleTimer", 0);
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mGetVsyncPeriod) {
+ resyncToHardwareVsync(false, mGetVsyncPeriod());
+ }
+}
+
void Scheduler::expiredTimerCallback() {
- // We do not notify the applications about config changes when idle timer expires.
timerChangeRefreshRate(IdleTimerState::EXPIRED);
ATRACE_INT("ExpiredIdleTimer", 1);
}
+void Scheduler::expiredKernelTimerCallback() {
+ 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);
+}
+
std::string Scheduler::doDump() {
std::ostringstream stream;
stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 34327b5..c8ff377 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -157,6 +157,7 @@
void updateFpsBasedOnContent();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+ void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod);
// Returns whether idle timer is enabled or not
bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
@@ -191,6 +192,14 @@
void resetTimerCallback();
// Function that is called when the timer expires.
void expiredTimerCallback();
+ // Function that is called when the timer resets when paired with a display
+ // driver timeout in the kernel. This enables hardware vsync when we move
+ // out from idle.
+ void resetKernelTimerCallback();
+ // Function that is called when the timer expires when paired with a display
+ // driver timeout in the kernel. This disables hardware vsync when we move
+ // into idle.
+ void expiredKernelTimerCallback();
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
// Idle timer feature's function to change the refresh rate.
@@ -242,9 +251,13 @@
// interval, a callback is fired. Set this variable to >0 to use this feature.
int64_t mSetIdleTimerMs = 0;
std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
+ // Enables whether to use idle timer callbacks that support the kernel
+ // timer.
+ bool mSupportKernelTimer;
std::mutex mCallbackLock;
ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+ GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock);
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 661c889..897f7b4 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -704,6 +704,10 @@
Mutex::Autolock lock(mStateLock);
setRefreshRateTo(type, event);
});
+ mScheduler->setGetVsyncPeriodCallback([this] {
+ Mutex::Autolock lock(mStateLock);
+ return getVsyncPeriod();
+ });
mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId()));
mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId()));
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index 3522429..c43d095 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -246,6 +246,14 @@
return defaultValue;
}
+bool support_kernel_idle_timer(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::support_kernel_idle_timer();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
#define DISPLAY_PRIMARY_SIZE 3
constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 1864290..d772fa8 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -76,6 +76,8 @@
bool enable_protected_contents(bool defaultValue);
+bool support_kernel_idle_timer(bool defaultValue);
+
android::ui::DisplayPrimaries getDisplayNativePrimaries();
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index d369096..bd8aacd 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -315,3 +315,13 @@
access: Readonly
prop_name: "ro.surface_flinger.protected_contents"
}
+
+# Indicates whether Scheduler's idle timer should support a display driver timeout in the kernel.
+# The value of set_idle_timer_ms should be shorter in time than the timeout duration in the kernel.
+prop {
+ api_name: "support_kernel_idle_timer"
+ type: Boolean
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.support_kernel_idle_timer"
+}
diff --git a/services/surfaceflinger/sysprop/api/system-current.txt b/services/surfaceflinger/sysprop/api/system-current.txt
index 3c39b51..dd5dbe0 100644
--- a/services/surfaceflinger/sysprop/api/system-current.txt
+++ b/services/surfaceflinger/sysprop/api/system-current.txt
@@ -19,6 +19,7 @@
method public static java.util.Optional<java.lang.Boolean> running_without_sync_framework();
method public static java.util.Optional<java.lang.Integer> set_idle_timer_ms();
method public static java.util.Optional<java.lang.Boolean> start_graphics_allocator_service();
+ method public static java.util.Optional<java.lang.Boolean> support_kernel_idle_timer();
method public static java.util.Optional<java.lang.Boolean> use_color_management();
method public static java.util.Optional<java.lang.Boolean> use_context_priority();
method public static java.util.Optional<java.lang.Boolean> use_smart_90_for_video();
diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
index ea39bf5..eff22b6 100644
--- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp
@@ -111,19 +111,19 @@
EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
mIdleTimer->reset();
- EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
mIdleTimer->reset();
- EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
mIdleTimer->reset();
- EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
mIdleTimer->reset();
- EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value());
+ EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value());
EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value());
// A single callback should be generated after 30ms