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

Commit fc378b0b authored by Dominik Laskowski's avatar Dominik Laskowski
Browse files

SF: Fix display mode transitions for multi-display

SF does not yet support concurrent modeset on multiple displays, as the
scheduler/modeset/VSYNC state machines are tied to the internal display
that is powered on, a.k.a. the active display.

When SF detects a change in the DM policy for a display, it initiates a
transition to the new display mode, which includes syncing to its VSYNC.
However, the per-display calls to setDesiredDisplayModeSpecs that start
this process occur after the setPowerMode calls to turn off/on the old/
new active display, respectively. Before this CL, a change in policy on
the now inactive (powered-off) display triggered a partial display mode
transition (that would start resync but abort before HWC modeset), such
that SF wound up internally inconsistent and out of sync with HWC.

Fix this by deferring the applyRefreshRateSelectorPolicy of the inactive
display until it becomes active. Later, in onActiveDisplayChangedLocked,
the Scheduler::Policy is cleared by Scheduler::setLeaderDisplay, so
ensure that Scheduler::getPreferredDisplayMode subsequently initializes
Scheduler::Policy::modeOpt to the chosen mode for the newly active
display. Otherwise, applyRefreshRateSelectorPolicy falls back to its
default mode.

Bug: 260092798
Test: No intermittent jank on outer/inner displays after fold/unfold.
Test: DisplayModeSwitchingTest.multiDisplay
Change-Id: Iebe1a6bb4749630333ef954955ac33807c95dd9f
parent 80a1dd74
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -452,7 +452,8 @@ void DisplayDevice::animateRefreshRateOverlay() {
    }
}

auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredActiveModeAction {
auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info, bool force)
        -> DesiredActiveModeAction {
    ATRACE_CALL();

    LOG_ALWAYS_FATAL_IF(!info.modeOpt, "desired mode not provided");
@@ -473,7 +474,7 @@ auto DisplayDevice::setDesiredActiveMode(const ActiveModeInfo& info) -> DesiredA
    const auto& desiredMode = *info.modeOpt->modePtr;

    // Check if we are already at the desired mode
    if (refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
    if (!force && refreshRateSelector().getActiveMode().modePtr->getId() == desiredMode.getId()) {
        if (refreshRateSelector().getActiveMode() == info.modeOpt) {
            return DesiredActiveModeAction::None;
        }
+2 −1
Original line number Diff line number Diff line
@@ -210,7 +210,8 @@ public:
        InitiateDisplayModeSwitch,
        InitiateRenderRateSwitch
    };
    DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&) EXCLUDES(mActiveModeLock);
    DesiredActiveModeAction setDesiredActiveMode(const ActiveModeInfo&, bool force = false)
            EXCLUDES(mActiveModeLock);
    std::optional<ActiveModeInfo> getDesiredActiveMode() const EXCLUDES(mActiveModeLock);
    void clearDesiredActiveModeState() EXCLUDES(mActiveModeLock);
    ActiveModeInfo getUpcomingActiveMode() const REQUIRES(kMainThreadContext) {
+9 −9
Original line number Diff line number Diff line
@@ -815,18 +815,18 @@ GlobalSignals Scheduler::makeGlobalSignals() const {
            .powerOnImminent = powerOnImminent};
}

ftl::Optional<FrameRateMode> Scheduler::getPreferredDisplayMode() {
FrameRateMode Scheduler::getPreferredDisplayMode() {
    std::lock_guard<std::mutex> lock(mPolicyLock);
    // Make sure the stored mode is up to date.
    if (mPolicy.modeOpt) {
        const auto ranking =
    const auto frameRateMode =
            leaderSelectorPtr()
                    ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals())
                        .ranking;
                    .ranking.front()
                    .frameRateMode;

        mPolicy.modeOpt = ranking.front().frameRateMode;
    }
    return mPolicy.modeOpt;
    // Make sure the stored mode is up to date.
    mPolicy.modeOpt = frameRateMode;

    return frameRateMode;
}

void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
+2 −2
Original line number Diff line number Diff line
@@ -209,8 +209,8 @@ public:
    void dump(ConnectionHandle, std::string&) const;
    void dumpVsync(std::string&) const;

    // Get the appropriate refresh for current conditions.
    ftl::Optional<FrameRateMode> getPreferredDisplayMode();
    // Returns the preferred refresh rate and frame rate for the leader display.
    FrameRateMode getPreferredDisplayMode();

    // Notifies the scheduler about a refresh rate timeline change.
    void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline);
+33 −8
Original line number Diff line number Diff line
@@ -1141,7 +1141,7 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* ou
    return NO_ERROR;
}

void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request) {
void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) {
    ATRACE_CALL();

    auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId());
@@ -1153,7 +1153,8 @@ void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request)
    const auto mode = request.mode;
    const bool emitEvent = request.emitEvent;

    switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)))) {
    switch (display->setDesiredActiveMode(DisplayDevice::ActiveModeInfo(std::move(request)),
                                          force)) {
        case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch:
            scheduleComposite(FrameHint::kNone);

@@ -3467,10 +3468,21 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest

    for (auto& request : modeRequests) {
        const auto& modePtr = request.mode.modePtr;
        const auto display = getDisplayDeviceLocked(modePtr->getPhysicalDisplayId());

        const auto displayId = modePtr->getPhysicalDisplayId();
        const auto display = getDisplayDeviceLocked(displayId);

        if (!display) continue;

        const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
                                               .transform(&PhysicalDisplay::isInternal)
                                               .value_or(false);

        if (isInternalDisplay && displayId != mActiveDisplayId) {
            ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
            continue;
        }

        if (display->refreshRateSelector().isModeAllowed(request.mode)) {
            setDesiredActiveMode(std::move(request));
        } else {
@@ -6730,7 +6742,7 @@ void SurfaceFlinger::traverseLayersInLayerStack(ui::LayerStack layerStack, const
ftl::Optional<scheduler::FrameRateMode> SurfaceFlinger::getPreferredDisplayMode(
        PhysicalDisplayId displayId, DisplayModeId defaultModeId) const {
    if (const auto schedulerMode = mScheduler->getPreferredDisplayMode();
        schedulerMode && schedulerMode->modePtr->getPhysicalDisplayId() == displayId) {
        schedulerMode.modePtr->getPhysicalDisplayId() == displayId) {
        return schedulerMode;
    }

@@ -6765,12 +6777,24 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal(
        case SetPolicyResult::Unchanged:
            return NO_ERROR;
        case SetPolicyResult::Changed:
            return applyRefreshRateSelectorPolicy(displayId, selector);
            break;
    }

    const bool isInternalDisplay = mPhysicalDisplays.get(displayId)
                                           .transform(&PhysicalDisplay::isInternal)
                                           .value_or(false);

    if (isInternalDisplay && displayId != mActiveDisplayId) {
        // The policy will be be applied when the display becomes active.
        ALOGV("%s(%s): Inactive display", __func__, to_string(displayId).c_str());
        return NO_ERROR;
    }

    return applyRefreshRateSelectorPolicy(displayId, selector);
}

status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
        PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) {
        PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector, bool force) {
    const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
    ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());

@@ -6800,7 +6824,7 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
        return INVALID_OPERATION;
    }

    setDesiredActiveMode({std::move(preferredMode), .emitEvent = true});
    setDesiredActiveMode({std::move(preferredMode), .emitEvent = true}, force);
    return NO_ERROR;
}

@@ -7104,7 +7128,8 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const sp<DisplayDevice>& inact
    // case, its preferred mode has not been propagated to HWC (via setDesiredActiveMode). In either
    // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
    // and the kernel idle timer of the newly active display must be toggled.
    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector());
    constexpr bool kForce = true;
    applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay->refreshRateSelector(), kForce);
}

status_t SurfaceFlinger::addWindowInfosListener(
Loading