SurfaceFlinger: HWVsync when display is off
HWC expects that HWVsync will not be turned on if display if off.
To ensure that we move enabling/disabling of HWVsync to the main thread
and caching the latest request in case the display is off. When the
display is turned on again we apply the latest state of HWVsync.
Bug: 134050982
Test: Toggle screen on/off in a loop
Change-Id: Ib87f885f95195b8bb402d5a5b2c75f20e213d2aa
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e735e10..a543bc7 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1537,10 +1537,25 @@
void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) {
ATRACE_CALL();
- Mutex::Autolock lock(mStateLock);
+
+ // Enable / Disable HWVsync from the main thread to avoid race conditions with
+ // display power state.
+ postMessageAsync(new LambdaMessage(
+ [=]() NO_THREAD_SAFETY_ANALYSIS { setPrimaryVsyncEnabledInternal(enabled); }));
+}
+
+void SurfaceFlinger::setPrimaryVsyncEnabledInternal(bool enabled) {
+ ATRACE_CALL();
+
if (const auto displayId = getInternalDisplayIdLocked()) {
- getHwComposer().setVsyncEnabled(*displayId,
- enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+ sp<DisplayDevice> display = getDefaultDisplayDeviceLocked();
+ if (display && display->isPoweredOn()) {
+ getHwComposer().setVsyncEnabled(*displayId,
+ enabled ? HWC2::Vsync::Enable : HWC2::Vsync::Disable);
+ } else {
+ // Cache the latest vsync state and apply it when screen is on again
+ mEnableHWVsyncScreenOn = enabled;
+ }
}
}
@@ -4438,6 +4453,11 @@
// Turn on the display
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
+ if (mEnableHWVsyncScreenOn) {
+ setPrimaryVsyncEnabledInternal(mEnableHWVsyncScreenOn);
+ mEnableHWVsyncScreenOn = false;
+ }
+
mScheduler->onScreenAcquired(mAppConnectionHandle);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
}