Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fc9e7f0 authored by Leon Scroggins III's avatar Leon Scroggins III
Browse files

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
parent 792ea802
Loading
Loading
Loading
Loading
+33 −14
Original line number Diff line number Diff line
@@ -5804,12 +5804,15 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
        }

        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);

            if (displayId == mActiveDisplayId) {
                mScheduler->enableSyntheticVsync(false);
            }

            constexpr bool kAllowToEnable = true;
            mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, activeMode.get());
@@ -5818,8 +5821,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
        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 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
                          strerror(errno));
                }

                if (*currentModeOpt != hal::PowerMode::DOZE_SUSPEND) {
                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 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal:
    } 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) {
        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);
            mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */,
                                              activeMode.get());
            }
            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);
+33 −0
Original line number Diff line number Diff line
@@ -188,5 +188,38 @@ TEST_F(FoldableTest, requestsHardwareVsyncForBothDisplays) {
    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
+9 −2
Original line number Diff line number Diff line
@@ -73,11 +73,13 @@ struct EventThreadBaseSupportedVariant {

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 @@ using PrimaryDisplayPowerCase =
// 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>,