SF: move idle timer to refresh rate configs
Move Idle timer management to RefreshRateConfigs to be able to maintain
separate timer for each display.
Bug: 188838426
Test: check idle timer is still working for both kernel idle timer and
SF idle timer
Change-Id: If0bd6c9a1ba7e204160e722edc6aedaaeef84997
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 81a669a..011229e 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -22,6 +22,7 @@
#pragma clang diagnostic ignored "-Wextra"
#include "RefreshRateConfigs.h"
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <utils/Trace.h>
#include <chrono>
@@ -679,9 +680,34 @@
RefreshRateConfigs::RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
Config config)
: mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
+ initializeIdleTimer();
updateDisplayModes(modes, currentModeId);
}
+void RefreshRateConfigs::initializeIdleTimer() {
+ const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms", 0);
+
+ if (const auto millis = setIdleTimerMs ? setIdleTimerMs : sysprop::set_idle_timer_ms(0);
+ millis > 0) {
+ const auto getCallback = [this]() -> std::optional<IdleTimerCallbacks::Callbacks> {
+ std::scoped_lock lock(mIdleTimerCallbacksMutex);
+ if (!mIdleTimerCallbacks.has_value()) return {};
+ return mConfig.supportKernelTimer ? mIdleTimerCallbacks->kernel
+ : mIdleTimerCallbacks->platform;
+ };
+
+ mIdleTimer.emplace(
+ "IdleTimer", std::chrono::milliseconds(millis),
+ [getCallback] {
+ if (const auto callback = getCallback()) callback->onReset();
+ },
+ [getCallback] {
+ if (const auto callback = getCallback()) callback->onExpired();
+ });
+ mIdleTimer->start();
+ }
+}
+
void RefreshRateConfigs::updateDisplayModes(const DisplayModes& modes,
DisplayModeId currentModeId) {
std::lock_guard lock(mLock);
@@ -944,6 +970,8 @@
base::StringAppendF(&result, "Supports Frame Rate Override: %s\n",
mSupportsFrameRateOverride ? "yes" : "no");
+ base::StringAppendF(&result, "Idle timer: %s\n",
+ mIdleTimer ? mIdleTimer->dump().c_str() : "off");
result.append("\n");
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 4a9a1fd..0f0fe22 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -27,6 +27,7 @@
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
#include "Fps.h"
+#include "Scheduler/OneShotTimer.h"
#include "Scheduler/SchedulerUtils.h"
#include "Scheduler/Seamlessness.h"
#include "Scheduler/StrongTyping.h"
@@ -305,11 +306,15 @@
// or heuristic, such that refresh rates higher than this value will not be voted for. 0 if
// no threshold is set.
int frameRateMultipleThreshold = 0;
+
+ // Whether to use idle timer callbacks that support the kernel timer.
+ bool supportKernelTimer = false;
};
RefreshRateConfigs(const DisplayModes& modes, DisplayModeId currentModeId,
Config config = {.enableFrameRateOverride = false,
- .frameRateMultipleThreshold = 0});
+ .frameRateMultipleThreshold = 0,
+ .supportKernelTimer = false});
// Returns whether switching modes (refresh rate or resolution) is possible.
// TODO(b/158780872): Consider HAL support, and skip frame rate detection if the modes only
@@ -345,6 +350,30 @@
Fps displayFrameRate, bool touch) const
EXCLUDES(mLock);
+ bool supportsKernelIdleTimer() const { return mConfig.supportKernelTimer; }
+
+ void setIdleTimerCallbacks(std::function<void()> platformTimerReset,
+ std::function<void()> platformTimerExpired,
+ std::function<void()> kernelTimerReset,
+ std::function<void()> kernelTimerExpired) {
+ std::scoped_lock lock(mIdleTimerCallbacksMutex);
+ mIdleTimerCallbacks.emplace();
+ mIdleTimerCallbacks->platform.onReset = platformTimerReset;
+ mIdleTimerCallbacks->platform.onExpired = platformTimerExpired;
+ mIdleTimerCallbacks->kernel.onReset = kernelTimerReset;
+ mIdleTimerCallbacks->kernel.onExpired = kernelTimerExpired;
+ }
+
+ void resetIdleTimer(bool kernelOnly) {
+ if (!mIdleTimer) {
+ return;
+ }
+ if (kernelOnly && !mConfig.supportKernelTimer) {
+ return;
+ }
+ mIdleTimer->reset();
+ };
+
void dump(std::string& result) const EXCLUDES(mLock);
RefreshRateConfigs(const RefreshRateConfigs&) = delete;
@@ -403,6 +432,8 @@
void updateDisplayModes(const DisplayModes& mode, DisplayModeId currentModeId) EXCLUDES(mLock);
+ void initializeIdleTimer();
+
// The list of refresh rates, indexed by display modes ID. This may change after this
// object is initialized.
AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock);
@@ -446,6 +477,22 @@
};
mutable std::optional<GetBestRefreshRateInvocation> lastBestRefreshRateInvocation
GUARDED_BY(mLock);
+
+ // Timer that records time between requests for next vsync.
+ std::optional<scheduler::OneShotTimer> mIdleTimer;
+
+ struct IdleTimerCallbacks {
+ struct Callbacks {
+ std::function<void()> onReset;
+ std::function<void()> onExpired;
+ };
+
+ Callbacks platform;
+ Callbacks kernel;
+ };
+
+ std::mutex mIdleTimerCallbacksMutex;
+ std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 80b2504..1a5f0f4 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -120,28 +120,15 @@
Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
ISchedulerCallback& callback)
: Scheduler(configs, callback,
- {.supportKernelTimer = sysprop::support_kernel_idle_timer(false),
- .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
+ {.useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
}
Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
ISchedulerCallback& callback, Options options)
- : Scheduler(createVsyncSchedule(options.supportKernelTimer), configs, callback,
+ : Scheduler(createVsyncSchedule(configs->supportsKernelIdleTimer()), configs, callback,
createLayerHistory(), options) {
using namespace sysprop;
- const int setIdleTimerMs = base::GetIntProperty("debug.sf.set_idle_timer_ms"s, 0);
-
- if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) {
- const auto callback = mOptions.supportKernelTimer ? &Scheduler::kernelIdleTimerCallback
- : &Scheduler::idleTimerCallback;
- mIdleTimer.emplace(
- "IdleTimer", std::chrono::milliseconds(millis),
- [this, callback] { std::invoke(callback, this, TimerState::Reset); },
- [this, callback] { std::invoke(callback, this, TimerState::Expired); });
- mIdleTimer->start();
- }
-
if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
// Touch events are coming to SF every 100ms, so the timer needs to be higher than that
mTouchTimer.emplace(
@@ -168,11 +155,11 @@
mVsyncSchedule(std::move(schedule)),
mLayerHistory(std::move(layerHistory)),
mSchedulerCallback(schedulerCallback),
- mRefreshRateConfigs(configs),
mPredictedVsyncTracer(
base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
: nullptr) {
+ setRefreshRateConfigs(configs);
mSchedulerCallback.setVsyncEnabled(false);
}
@@ -180,7 +167,7 @@
// Ensure the OneShotTimer threads are joined before we start destroying state.
mDisplayPowerTimer.reset();
mTouchTimer.reset();
- mIdleTimer.reset();
+ mRefreshRateConfigs.reset();
}
Scheduler::VsyncSchedule Scheduler::createVsyncSchedule(bool supportKernelTimer) {
@@ -672,18 +659,16 @@
}
void Scheduler::resetIdleTimer() {
- if (mIdleTimer) {
- mIdleTimer->reset();
- }
+ std::scoped_lock lock(mRefreshRateConfigsLock);
+ mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ false);
}
void Scheduler::notifyTouchEvent() {
if (mTouchTimer) {
mTouchTimer->reset();
- if (mOptions.supportKernelTimer && mIdleTimer) {
- mIdleTimer->reset();
- }
+ std::scoped_lock lock(mRefreshRateConfigsLock);
+ mRefreshRateConfigs->resetIdleTimer(/*kernelOnly*/ true);
}
}
@@ -755,7 +740,6 @@
void Scheduler::dump(std::string& result) const {
using base::StringAppendF;
- StringAppendF(&result, "+ Idle timer: %s\n", mIdleTimer ? mIdleTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Touch timer: %s\n",
mTouchTimer ? mTouchTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Content detection: %s %s\n\n",
@@ -867,7 +851,7 @@
}
const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
- const bool idle = mIdleTimer && mFeatures.idleTimer == TimerState::Expired;
+ const bool idle = mFeatures.idleTimer == TimerState::Expired;
return refreshRateConfigs
->getBestRefreshRate(mFeatures.contentRequirements,
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 4b6905b..2f97328 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -133,7 +133,6 @@
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent() EXCLUDES(mRefreshRateConfigsLock);
- bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
void resetIdleTimer();
// Function that resets the touch timer.
@@ -184,6 +183,15 @@
EXCLUDES(mRefreshRateConfigsLock) {
std::scoped_lock lock(mRefreshRateConfigsLock);
mRefreshRateConfigs = std::move(refreshRateConfigs);
+ mRefreshRateConfigs->setIdleTimerCallbacks(
+ [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); },
+ [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired); },
+ [this] {
+ std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset);
+ },
+ [this] {
+ std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired);
+ });
}
nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
@@ -201,8 +209,6 @@
enum class TouchState { Inactive, Active };
struct Options {
- // Whether to use idle timer callbacks that support the kernel timer.
- bool supportKernelTimer;
// Whether to use content detection at all.
bool useContentDetection;
};
@@ -288,8 +294,6 @@
// Used to choose refresh rate if content detection is enabled.
std::unique_ptr<LayerHistory> mLayerHistory;
- // Timer that records time between requests for next vsync.
- std::optional<scheduler::OneShotTimer> mIdleTimer;
// Timer used to monitor touch events.
std::optional<scheduler::OneShotTimer> mTouchTimer;
// Timer used to monitor display power mode.
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 7b5d462..ee973f7 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -42,6 +42,7 @@
: mClock(std::move(clock)),
mTracker(tracker),
mPendingLimit(pendingFenceLimit),
+ // TODO(adyabr): change mSupportKernelIdleTimer when the active display changes
mSupportKernelIdleTimer(supportKernelIdleTimer) {}
VSyncReactor::~VSyncReactor() = default;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9e23152..a7092ab 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2646,7 +2646,8 @@
scheduler::RefreshRateConfigs::Config config =
{.enableFrameRateOverride = android::sysprop::enable_frame_rate_override(false),
.frameRateMultipleThreshold =
- base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0)};
+ base::GetIntProperty("debug.sf.frame_rate_multiple_threshold", 0),
+ .supportKernelTimer = sysprop::support_kernel_idle_timer(false)};
creationArgs.refreshRateConfigs =
std::make_shared<scheduler::RefreshRateConfigs>(creationArgs.supportedModes,
creationArgs.activeModeId, config);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index a99dabe..1d21bd4 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -44,7 +44,7 @@
ISchedulerCallback& callback)
: Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr},
refreshRateConfigs, callback, createLayerHistory(),
- {.supportKernelTimer = false, .useContentDetection = true}) {}
+ {.useContentDetection = true}) {}
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {