HW Vsync turns off correctly in Doze/AOD.
Otherwise when entering AOD mode, SF keeps HW Vsync on indefinitely,
leading to power inefficiency.
Bug: 219109873
Test: atest libsurfaceflinger_test
Test: perfetto trace
Change-Id: Idf80d4c64abe78593809be9d1ba6b7dd42d29054
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 37f0fec..08a1ede 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -554,11 +554,12 @@
}
}
-void Scheduler::setDisplayPowerState(bool normal) {
+void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) {
{
std::lock_guard<std::mutex> lock(mPolicyLock);
- mPolicy.isDisplayPowerStateNormal = normal;
+ mPolicy.displayPowerMode = powerMode;
}
+ mVsyncSchedule->getController().setDisplayPowerMode(powerMode);
if (mDisplayPowerTimer) {
mDisplayPowerTimer->reset();
@@ -706,7 +707,8 @@
// 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 (mDisplayPowerTimer &&
- (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
+ (mPolicy.displayPowerMode != hal::PowerMode::ON ||
+ mPolicy.displayPowerTimer == TimerState::Reset)) {
constexpr GlobalSignals kNoSignals;
return {configs->getMaxRefreshRateByPolicy(), kNoSignals};
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 0c72124..a8043bf 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -186,7 +186,7 @@
// Indicates that touch interaction is taking place.
void onTouchHint();
- void setDisplayPowerState(bool normal);
+ void setDisplayPowerMode(hal::PowerMode powerMode);
VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
@@ -325,7 +325,7 @@
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- bool isDisplayPowerStateNormal = true;
+ hal::PowerMode displayPowerMode = hal::PowerMode::ON;
// Chosen display mode.
DisplayModePtr mode;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index bdcab51..13cd304 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -146,6 +146,11 @@
return false;
}
+ if (mDisplayPowerMode == hal::PowerMode::DOZE ||
+ mDisplayPowerMode == hal::PowerMode::DOZE_SUSPEND) {
+ return true;
+ }
+
if (!mLastHwVsync && !HwcVsyncPeriod) {
return false;
}
@@ -206,6 +211,11 @@
return mMoreSamplesNeeded;
}
+void VSyncReactor::setDisplayPowerMode(hal::PowerMode powerMode) {
+ std::scoped_lock lock(mMutex);
+ mDisplayPowerMode = powerMode;
+}
+
void VSyncReactor::dump(std::string& result) const {
std::lock_guard lock(mMutex);
StringAppendF(&result, "VsyncReactor in use\n");
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 6a1950a..4501487 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -49,6 +49,8 @@
bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed) final;
+ void setDisplayPowerMode(hal::PowerMode powerMode) final;
+
void dump(std::string& result) const final;
private:
@@ -73,6 +75,8 @@
std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex);
std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex);
+ hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON;
+
const bool mSupportKernelIdleTimer = false;
};
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 59f6537..726a420 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -18,7 +18,10 @@
#include <cstddef>
#include <memory>
+#include <mutex>
+#include <DisplayHardware/HWComposer.h>
+#include <DisplayHardware/Hal.h>
#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
@@ -70,6 +73,13 @@
*/
virtual void setIgnorePresentFences(bool ignore) = 0;
+ /*
+ * Sets the primary display power mode to the controller.
+ *
+ * \param [in] powerMode
+ */
+ virtual void setDisplayPowerMode(hal::PowerMode powerMode) = 0;
+
virtual void dump(std::string& result) const = 0;
protected:
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5cf9558..906cf73 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4947,7 +4947,7 @@
if (isDisplayActiveLocked(display)) {
mTimeStats->setPowerMode(mode);
mRefreshRateStats->setPowerMode(mode);
- mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON);
+ mScheduler->setDisplayPowerMode(mode);
}
ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str());
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index aab2795..93c809e 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -159,8 +159,8 @@
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
- constexpr bool kPowerStateNormal = true;
- mScheduler->setDisplayPowerState(kPowerStateNormal);
+ constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+ mScheduler->setDisplayPowerMode(kPowerModeOn);
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
@@ -226,8 +226,8 @@
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
- constexpr bool kPowerStateNormal = true;
- mScheduler->setDisplayPowerState(kPowerStateNormal);
+ constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON;
+ mScheduler->setDisplayPowerMode(kPowerModeOn);
constexpr uint32_t kDisplayArea = 999'999;
mScheduler->onActiveDisplayAreaChanged(kDisplayArea);
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 4eb9055..30a3f9a 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -349,6 +349,23 @@
}
}
+TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) {
+ bool periodFlushed = false;
+ nsecs_t const newPeriod = 4000;
+
+ mReactor.startPeriodTransition(newPeriod);
+
+ auto time = 0;
+ // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps.
+ EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+
+ // Set power mode to DOZE to trigger period flushing.
+ mReactor.setDisplayPowerMode(hal::PowerMode::DOZE);
+ EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+}
+
TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) {
auto time = 0;
bool periodFlushed = false;
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 314f681..4ef91da 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -31,6 +31,7 @@
MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
MOCK_METHOD1(setIgnorePresentFences, void(bool));
+ MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override));
MOCK_CONST_METHOD1(dump, void(std::string&));
};