Turn on and off hw vsyncs for secondary display
When the power mode changes on non-"active" displays, still turn on/off
hw VSYNC callbacks. The call to turn them on is crucial - when a display
is turned off, not only are hw VSYNCs turned off, they are disallowed.
It is only by calls to resyncToHardwareVsyncLocked that they are again
allowed. Without this change, this would have to wait for a call to
resync or kernelIdleTimerCallback. This improves predictions for the
secondary display.
When turning off the active display, if there is an activatable display,
still call disableHardwareVsync for the old active display.
Bug: 255601557
Test: SetPowerModeInternalTest and FoldableTest
Change-Id: Ida666af9a3a9b2e940c1f861ce3b765f67738f1f
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index bcad6ac..9c7bd99 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -5804,12 +5804,15 @@
}
getHwComposer().setPowerMode(displayId, mode);
- if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) {
+ if (mode != hal::PowerMode::DOZE_SUSPEND &&
+ (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
const bool enable =
mScheduler->getVsyncSchedule(displayId)->getPendingHardwareVsyncState();
requestHardwareVsync(displayId, enable);
- mScheduler->enableSyntheticVsync(false);
+ if (displayId == mActiveDisplayId) {
+ mScheduler->enableSyntheticVsync(false);
+ }
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
@@ -5818,8 +5821,8 @@
mVisibleRegionsDirty = true;
scheduleComposite(FrameHint::kActive);
} else if (mode == hal::PowerMode::OFF) {
+ const bool currentModeNotDozeSuspend = (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND);
// Turn off the display
-
if (displayId == mActiveDisplayId) {
if (const auto display = getActivatableDisplay()) {
onActiveDisplayChangedLocked(activeDisplay.get(), *display);
@@ -5833,14 +5836,24 @@
strerror(errno));
}
- if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
- mScheduler->disableHardwareVsync(displayId, true);
+ if (currentModeNotDozeSuspend) {
+ if (!FlagManager::getInstance().multithreaded_present()) {
+ mScheduler->disableHardwareVsync(displayId, true);
+ }
mScheduler->enableSyntheticVsync();
}
}
}
+ if (currentModeNotDozeSuspend && FlagManager::getInstance().multithreaded_present()) {
+ constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(displayId, kDisallow);
+ }
- // Disable VSYNC before turning off the display.
+ // We must disable VSYNC *before* turning off the display. The call to
+ // disableHardwareVsync, above, schedules a task to turn it off after
+ // this method returns. But by that point, the display is OFF, so the
+ // call just updates the pending state, without actually disabling
+ // VSYNC.
requestHardwareVsync(displayId, false);
getHwComposer().setPowerMode(displayId, mode);
@@ -5849,18 +5862,24 @@
} else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) {
// Update display while dozing
getHwComposer().setPowerMode(displayId, mode);
- if (displayId == mActiveDisplayId && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) {
- ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
- mVisibleRegionsDirty = true;
- scheduleRepaint();
- mScheduler->enableSyntheticVsync(false);
- mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */,
- activeMode.get());
+ if (*currentModeOpt == hal::PowerMode::DOZE_SUSPEND &&
+ (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present())) {
+ if (displayId == mActiveDisplayId) {
+ ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON.");
+ mVisibleRegionsDirty = true;
+ scheduleRepaint();
+ mScheduler->enableSyntheticVsync(false);
+ }
+ constexpr bool kAllowToEnable = true;
+ mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
}
} else if (mode == hal::PowerMode::DOZE_SUSPEND) {
// Leave display going to doze
+ if (displayId == mActiveDisplayId || FlagManager::getInstance().multithreaded_present()) {
+ constexpr bool kDisallow = true;
+ mScheduler->disableHardwareVsync(displayId, kDisallow);
+ }
if (displayId == mActiveDisplayId) {
- mScheduler->disableHardwareVsync(displayId, true);
mScheduler->enableSyntheticVsync();
}
getHwComposer().setPowerMode(displayId, mode);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
index ed8d909..844b96c 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp
@@ -188,5 +188,38 @@
scheduler.onHardwareVsyncRequest(mOuterDisplay->getPhysicalId(), true);
}
+TEST_F(FoldableTest, requestVsyncOnPowerOn) {
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true))
+ .Times(1);
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true))
+ .Times(1);
+
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+}
+
+TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) {
+ // When the device boots, the inner display should be the pacesetter.
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId);
+
+ testing::InSequence seq;
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true))
+ .Times(1);
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true))
+ .Times(1);
+
+ // Turning off the pacesetter will result in disabling VSYNC.
+ EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, false))
+ .Times(1);
+
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::ON);
+ mFlinger.setPowerModeInternal(mOuterDisplay, PowerMode::ON);
+
+ mFlinger.setPowerModeInternal(mInnerDisplay, PowerMode::OFF);
+
+ // Other display is now the pacesetter.
+ ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kOuterDisplayId);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 31e1330..15fe600 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -73,11 +73,13 @@
struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant {
static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) {
- setupVsyncNoCallExpectations(test);
+ EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, true)).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) {
- setupVsyncNoCallExpectations(test);
+ EXPECT_CALL(test->mFlinger.scheduler()->mockRequestHardwareVsync, Call(_, false)).Times(1);
+ EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0);
}
};
@@ -298,6 +300,11 @@
// A sample configuration for the external display.
// In addition to not having event thread support, we emulate not having doze
// support.
+// TODO (b/267483230): ExternalDisplay supports the features tracked in
+// DispSyncIsSupportedVariant, but is the follower, so the
+// expectations set by DispSyncIsSupportedVariant don't match (wrong schedule).
+// We need a way to retrieve the proper DisplayId from
+// setupResetModelCallExpectations (or pass it in).
template <typename TransitionVariant>
using ExternalDisplayPowerCase =
DisplayPowerCase<ExternalDisplayVariant, DozeNotSupportedVariant<ExternalDisplayVariant>,