Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -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 services/surfaceflinger/Scheduler/RefreshRateConfigs.h +13 −0 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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; Loading services/surfaceflinger/SurfaceFlinger.cpp +37 −5 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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: Loading Loading @@ -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 Loading services/surfaceflinger/SurfaceFlinger.h +7 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +28 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +24 −0 Original line number Diff line number Diff line Loading @@ -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
services/surfaceflinger/Scheduler/RefreshRateConfigs.h +13 −0 Original line number Diff line number Diff line Loading @@ -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: Loading Loading @@ -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; Loading
services/surfaceflinger/SurfaceFlinger.cpp +37 −5 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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: Loading Loading @@ -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 Loading
services/surfaceflinger/SurfaceFlinger.h +7 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading
services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +28 −0 Original line number Diff line number Diff line Loading @@ -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