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

Commit b9afd79e authored by Ana Krulec's avatar Ana Krulec
Browse files

Move toggling of kernel idle timer to SF

Idle timer is toggled from the SF code using the following logic:
- If the device policy refresh rate min = max, we turn off the timer.
- If the device policy refresh rate min > the device min,
  we turn off the timer.
- Do not toggle the timer if deviceMin == policyMin.
- Timer is on in all other cases.

Bug: 140204874
Bug: 158012424
Bug: 145561154
Test: atest SurfaceFlinger_test
Test: atest libsurfaceflinger_unittest
Test: Select force 90hz through Developer Settings. Toggles timer off.
Test: Low brightness zone. Toggles timer off.
Change-Id: I9858765861a3b13e11c3930be8f77d85dae6c0c0
parent 72b3fa46
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -595,4 +595,28 @@ float RefreshRateConfigs::findClosestKnownFrameRate(float frameRate) const {
    return distance1 < distance2 ? *lowerBound : *std::prev(lowerBound);
}

RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const {
    std::lock_guard lock(mLock);
    const auto& deviceMin = getMinRefreshRate();
    const auto& minByPolicy = getMinRefreshRateByPolicyLocked();
    const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked();

    // Kernel idle timer will set the refresh rate to the device min. If DisplayManager says that
    // the min allowed refresh rate is higher than the device min, we do not want to enable the
    // timer.
    if (deviceMin < minByPolicy) {
        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
    }
    if (minByPolicy == maxByPolicy) {
        // Do not sent the call to toggle off kernel idle timer if the device min and policy min and
        // max are all the same. This saves us extra unnecessary calls to sysprop.
        if (deviceMin == minByPolicy) {
            return RefreshRateConfigs::KernelIdleTimerAction::NoChange;
        }
        return RefreshRateConfigs::KernelIdleTimerAction::TurnOff;
    }
    // Turn on the timer in all other cases.
    return RefreshRateConfigs::KernelIdleTimerAction::TurnOn;
}

} // namespace android::scheduler
+13 −0
Original line number Diff line number Diff line
@@ -82,6 +82,8 @@ public:
            return configId != other.configId || hwcConfig != other.hwcConfig;
        }

        bool operator<(const RefreshRate& other) const { return getFps() < other.getFps(); }

        bool operator==(const RefreshRate& other) const { return !(*this != other); }

    private:
@@ -271,6 +273,17 @@ public:
    RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
                       HwcConfigIndexType currentConfigId);

    // Class to enumerate options around toggling the kernel timer on and off. We have an option
    // for no change to avoid extra calls to kernel.
    enum class KernelIdleTimerAction {
        NoChange, // Do not change the idle timer.
        TurnOff,  // Turn off the idle timer.
        TurnOn    // Turn on the idle timer.
    };
    // Checks whether kernel idle timer should be active depending the policy decisions around
    // refresh rates.
    KernelIdleTimerAction getIdleTimerAction() const;

private:
    friend class RefreshRateConfigsTest;

+37 −5
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@

#include "SurfaceFlinger.h"

#include <android-base/properties.h>
#include <android/configuration.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
@@ -444,6 +445,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI
    }

    useFrameRateApi = use_frame_rate_api(true);

    mKernelIdleTimerEnabled = mSupportKernelIdleTimer = sysprop::support_kernel_idle_timer(false);
    base::SetProperty(KERNEL_IDLE_TIMER_PROP, mKernelIdleTimerEnabled ? "true" : "false");
}

SurfaceFlinger::~SurfaceFlinger() = default;
@@ -5355,8 +5359,7 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) {
        const auto& min = mRefreshRateConfigs->getMinRefreshRate();

        if (current != min) {
            const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
            const bool timerExpired = kernelTimerEnabled && expired;
            const bool timerExpired = mKernelIdleTimerEnabled && expired;

            if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) {
                mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
@@ -5366,6 +5369,35 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) {
    }));
}

void SurfaceFlinger::toggleKernelIdleTimer() {
    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;

    // If the support for kernel idle timer is disabled in SF code, don't do anything.
    if (!mSupportKernelIdleTimer) {
        return;
    }
    const KernelIdleTimerAction action = mRefreshRateConfigs->getIdleTimerAction();

    switch (action) {
        case KernelIdleTimerAction::TurnOff:
            if (mKernelIdleTimerEnabled) {
                ATRACE_INT("KernelIdleTimer", 0);
                base::SetProperty(KERNEL_IDLE_TIMER_PROP, "false");
                mKernelIdleTimerEnabled = false;
            }
            break;
        case KernelIdleTimerAction::TurnOn:
            if (!mKernelIdleTimerEnabled) {
                ATRACE_INT("KernelIdleTimer", 1);
                base::SetProperty(KERNEL_IDLE_TIMER_PROP, "true");
                mKernelIdleTimerEnabled = true;
            }
            break;
        case KernelIdleTimerAction::NoChange:
            break;
    }
}

// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -5992,14 +6024,14 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(
          currentPolicy.primaryRange.max, currentPolicy.appRequestRange.min,
          currentPolicy.appRequestRange.max);

    // TODO(b/140204874): This hack triggers a notification that something has changed, so
    // that listeners that care about a change in allowed configs can get the notification.
    // Giving current ActiveConfig so that most other listeners would just drop the event
    // TODO(b/140204874): Leave the event in until we do proper testing with all apps that might
    // be depending in this callback.
    const nsecs_t vsyncPeriod =
            mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig())
                    .getVsyncPeriod();
    mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value,
                                              display->getActiveConfig(), vsyncPeriod);
    toggleKernelIdleTimer();

    auto configId = mScheduler->getPreferredConfigId();
    auto& preferredRefreshRate = configId
+7 −0
Original line number Diff line number Diff line
@@ -535,6 +535,13 @@ private:
    void repaintEverythingForHWC() override;
    // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
    void kernelTimerChanged(bool expired) override;
    // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates.
    void toggleKernelIdleTimer();
    // Keeps track of whether the kernel idle timer is currently enabled, so we don't have to
    // make calls to sys prop each time.
    bool mKernelIdleTimerEnabled = false;
    // Keeps track of whether the kernel timer is supported on the SF side.
    bool mSupportKernelIdleTimer = false;
    /* ------------------------------------------------------------------------
     * Message handling
     */
+28 −0
Original line number Diff line number Diff line
@@ -1420,6 +1420,34 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_KnownFrameRate) {
    }
}

TEST_F(RefreshRateConfigsTest, testComparisonOperator) {
    EXPECT_TRUE(mExpected60Config < mExpected90Config);
    EXPECT_FALSE(mExpected60Config < mExpected60Config);
    EXPECT_FALSE(mExpected90Config < mExpected90Config);
}

TEST_F(RefreshRateConfigsTest, testKernelIdleTimerAction) {
    using KernelIdleTimerAction = scheduler::RefreshRateConfigs::KernelIdleTimerAction;

    auto refreshRateConfigs =
            std::make_unique<RefreshRateConfigs>(m60_90Device,
                                                 /*currentConfigId=*/HWC_CONFIG_ID_90);
    // SetPolicy(60, 90), current 90Hz => TurnOn.
    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());

    // SetPolicy(60, 90), current 60Hz => TurnOn.
    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 90}}), 0);
    EXPECT_EQ(KernelIdleTimerAction::TurnOn, refreshRateConfigs->getIdleTimerAction());

    // SetPolicy(60, 60), current 60Hz => NoChange, avoid extra calls.
    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {60, 60}}), 0);
    EXPECT_EQ(KernelIdleTimerAction::NoChange, refreshRateConfigs->getIdleTimerAction());

    // SetPolicy(90, 90), current 90Hz => TurnOff.
    ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {90, 90}}), 0);
    EXPECT_EQ(KernelIdleTimerAction::TurnOff, refreshRateConfigs->getIdleTimerAction());
}

} // namespace
} // namespace scheduler
} // namespace android