Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +22 −8 Original line number Original line Diff line number Diff line Loading @@ -115,12 +115,24 @@ std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPe } } const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const std::vector<LayerRequirement>& layers, bool touchActive, bool idle, const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals, bool* touchConsidered) const { GlobalSignals* outSignalsConsidered) const { ATRACE_CALL(); ATRACE_CALL(); ALOGV("getRefreshRateForContent %zu layers", layers.size()); ALOGV("getRefreshRateForContent %zu layers", layers.size()); if (touchConsidered) *touchConsidered = false; if (outSignalsConsidered) *outSignalsConsidered = {}; const auto setTouchConsidered = [&] { if (outSignalsConsidered) { outSignalsConsidered->touch = true; } }; const auto setIdleConsidered = [&] { if (outSignalsConsidered) { outSignalsConsidered->idle = true; } }; std::lock_guard lock(mLock); std::lock_guard lock(mLock); int noVoteLayers = 0; int noVoteLayers = 0; Loading Loading @@ -150,9 +162,9 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // selected a refresh rate to see if we should apply touch boost. // selected a refresh rate to see if we should apply touch boost. if (touchActive && !hasExplicitVoteLayers) { if (globalSignals.touch && !hasExplicitVoteLayers) { ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); if (touchConsidered) *touchConsidered = true; setTouchConsidered(); return getMaxRefreshRateByPolicyLocked(); return getMaxRefreshRateByPolicyLocked(); } } Loading @@ -162,8 +174,10 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const Policy* policy = getCurrentPolicyLocked(); const Policy* policy = getCurrentPolicyLocked(); const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max; const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max; if (!touchActive && idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { if (!globalSignals.touch && globalSignals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); setIdleConsidered(); return getMinRefreshRateByPolicyLocked(); return getMinRefreshRateByPolicyLocked(); } } Loading Loading @@ -307,9 +321,9 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // actually increase the refresh rate over the normal selection. // actually increase the refresh rate over the normal selection. const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); if (touchActive && explicitDefaultVoteLayers == 0 && if (globalSignals.touch && explicitDefaultVoteLayers == 0 && bestRefreshRate->fps < touchRefreshRate.fps) { bestRefreshRate->fps < touchRefreshRate.fps) { if (touchConsidered) *touchConsidered = true; setTouchConsidered(); ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); return touchRefreshRate; return touchRefreshRate; } } Loading services/surfaceflinger/Scheduler/RefreshRateConfigs.h +13 −5 Original line number Original line Diff line number Diff line Loading @@ -211,14 +211,22 @@ public: const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const EXCLUDES(mLock); EXCLUDES(mLock); // Global state describing signals that affect refresh rate choice. struct GlobalSignals { // Whether the user touched the screen recently. Used to apply touch boost. bool touch = false; // True if the system hasn't seen any buffers posted to layers recently. bool idle = false; }; // Returns the refresh rate that fits best to the given layers. // Returns the refresh rate that fits best to the given layers. // layers - The layer requirements to consider. // layers - The layer requirements to consider. // touchActive - Whether the user touched the screen recently. Used to apply touch boost. // globalSignals - global state of touch and idle // idle - True if the system hasn't seen any buffers posted to layers recently. // outSignalsConsidered - An output param that tells the caller whether the refresh rate was // touchConsidered - An output param that tells the caller whether the refresh rate was chosen // chosen based on touch boost and/or idle timer. // based on touch boost. const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers, const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers, bool touchActive, bool idle, bool* touchConsidered) const const GlobalSignals& globalSignals, GlobalSignals* outSignalsConsidered = nullptr) const EXCLUDES(mLock); EXCLUDES(mLock); // Returns all the refresh rates supported by the device. This won't change at runtime. // Returns all the refresh rates supported by the device. This won't change at runtime. Loading services/surfaceflinger/Scheduler/Scheduler.cpp +63 −22 Original line number Original line Diff line number Diff line Loading @@ -228,7 +228,35 @@ void Scheduler::onScreenReleased(ConnectionHandle handle) { mConnections[handle].thread->onScreenReleased(); mConnections[handle].thread->onScreenReleased(); } } void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, HwcConfigIndexType configId, nsecs_t vsyncPeriod) { std::lock_guard<std::mutex> lock(mFeatureStateLock); // Cache the last reported config for primary display. mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod}; onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod); } void Scheduler::dispatchCachedReportedConfig() { const auto configId = *mFeatures.configId; const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod(); // If there is no change from cached config, there is no need to dispatch an event if (configId == mFeatures.cachedConfigChangedParams->configId && vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) { return; } mFeatures.cachedConfigChangedParams->configId = configId; mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod; onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle, mFeatures.cachedConfigChangedParams->displayId, mFeatures.cachedConfigChangedParams->configId, mFeatures.cachedConfigChangedParams->vsyncPeriod); } void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, HwcConfigIndexType configId, nsecs_t vsyncPeriod) { HwcConfigIndexType configId, nsecs_t vsyncPeriod) { RETURN_IF_INVALID_HANDLE(handle); RETURN_IF_INVALID_HANDLE(handle); mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod); mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod); Loading Loading @@ -446,13 +474,21 @@ void Scheduler::chooseRefreshRateForContent() { mFeatures.contentDetectionV1 = mFeatures.contentDetectionV1 = !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off; !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off; newConfigId = calculateRefreshRateConfigIndexType(); scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals); if (mFeatures.configId == newConfigId) { if (mFeatures.configId == newConfigId) { // We don't need to change the config, but we might need to send an event // about a config change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { dispatchCachedReportedConfig(); } return; return; } } mFeatures.configId = newConfigId; mFeatures.configId = newConfigId; auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed); mSchedulerCallback.changeRefreshRate(newRefreshRate, consideredSignals.idle ? ConfigEvent::None : ConfigEvent::Changed); } } } } Loading Loading @@ -522,21 +558,20 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { } } void Scheduler::idleTimerCallback(TimerState state) { void Scheduler::idleTimerCallback(TimerState state) { handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */); handleTimerStateChanged(&mFeatures.idleTimer, state); ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); } } void Scheduler::touchTimerCallback(TimerState state) { void Scheduler::touchTimerCallback(TimerState state) { const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) { if (handleTimerStateChanged(&mFeatures.touch, touch)) { mLayerHistory->clear(); mLayerHistory->clear(); } } ATRACE_INT("TouchState", static_cast<int>(touch)); ATRACE_INT("TouchState", static_cast<int>(touch)); } } void Scheduler::displayPowerTimerCallback(TimerState state) { void Scheduler::displayPowerTimerCallback(TimerState state) { handleTimerStateChanged(&mFeatures.displayPowerTimer, state, handleTimerStateChanged(&mFeatures.displayPowerTimer, state); true /* eventOnContentDetection */); ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); } } Loading @@ -553,33 +588,37 @@ void Scheduler::dump(std::string& result) const { } } template <class T> template <class T> bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { ConfigEvent event = ConfigEvent::None; HwcConfigIndexType newConfigId; HwcConfigIndexType newConfigId; bool touchConsidered = false; scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; { { std::lock_guard<std::mutex> lock(mFeatureStateLock); std::lock_guard<std::mutex> lock(mFeatureStateLock); if (*currentState == newState) { if (*currentState == newState) { return touchConsidered; return false; } } *currentState = newState; *currentState = newState; newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered); newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals); if (mFeatures.configId == newConfigId) { if (mFeatures.configId == newConfigId) { return touchConsidered; // We don't need to change the config, but we might need to send an event // about a config change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { dispatchCachedReportedConfig(); } } mFeatures.configId = newConfigId; return consideredSignals.touch; if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) { event = ConfigEvent::Changed; } } mFeatures.configId = newConfigId; } } const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, event); mSchedulerCallback.changeRefreshRate(newRefreshRate, return touchConsidered; consideredSignals.idle ? ConfigEvent::None : ConfigEvent::Changed); return consideredSignals.touch; } } HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) { HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType( scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) { ATRACE_CALL(); ATRACE_CALL(); if (touchConsidered) *touchConsidered = false; if (consideredSignals) *consideredSignals = {}; // If Display Power is not in normal operation we want to be in performance mode. When coming // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. // back to normal mode, a grace period is given with DisplayPowerTimer. Loading @@ -600,6 +639,7 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchCon // If timer has expired as it means there is no new content on the screen. // If timer has expired as it means there is no new content on the screen. if (idle) { if (idle) { if (consideredSignals) consideredSignals->idle = true; return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId(); return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId(); } } Loading @@ -615,7 +655,8 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchCon } } return mRefreshRateConfigs return mRefreshRateConfigs .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered) .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle}, consideredSignals) .getConfigId(); .getConfigId(); } } Loading services/surfaceflinger/Scheduler/Scheduler.h +20 −5 Original line number Original line Diff line number Diff line Loading @@ -81,9 +81,11 @@ public: sp<EventThreadConnection> getEventConnection(ConnectionHandle); sp<EventThreadConnection> getEventConnection(ConnectionHandle); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId, void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId, nsecs_t vsyncPeriod); HwcConfigIndexType configId, nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock); void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId, nsecs_t vsyncPeriod); void onScreenAcquired(ConnectionHandle); void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); void onScreenReleased(ConnectionHandle); Loading Loading @@ -179,16 +181,19 @@ private: // handles various timer features to change the refresh rate. // handles various timer features to change the refresh rate. template <class T> template <class T> bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection); bool handleTimerStateChanged(T* currentState, T newState); void setVsyncPeriod(nsecs_t period); void setVsyncPeriod(nsecs_t period); // This function checks whether individual features that are affecting the refresh rate // This function checks whether individual features that are affecting the refresh rate // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType // for the suggested refresh rate. // for the suggested refresh rate. HwcConfigIndexType calculateRefreshRateConfigIndexType(bool* touchConsidered = nullptr) HwcConfigIndexType calculateRefreshRateConfigIndexType( scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mFeatureStateLock); REQUIRES(mFeatureStateLock); void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock); // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { struct Connection { sp<EventThreadConnection> connection; sp<EventThreadConnection> connection; Loading Loading @@ -240,6 +245,16 @@ private: LayerHistory::Summary contentRequirements; LayerHistory::Summary contentRequirements; bool isDisplayPowerStateNormal = true; bool isDisplayPowerStateNormal = true; // Used to cache the last parameters of onPrimaryDisplayConfigChanged struct ConfigChangedParams { ConnectionHandle handle; PhysicalDisplayId displayId; HwcConfigIndexType configId; nsecs_t vsyncPeriod; }; std::optional<ConfigChangedParams> cachedConfigChangedParams; } mFeatures GUARDED_BY(mFeatureStateLock); } mFeatures GUARDED_BY(mFeatureStateLock); const scheduler::RefreshRateConfigs& mRefreshRateConfigs; const scheduler::RefreshRateConfigs& mRefreshRateConfigs; Loading services/surfaceflinger/SurfaceFlinger.cpp +8 −8 Original line number Original line Diff line number Diff line Loading @@ -1077,7 +1077,7 @@ void SurfaceFlinger::setActiveConfigInternal() { const nsecs_t vsyncPeriod = const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId) mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId) .getVsyncPeriod(); .getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, mUpcomingActiveConfig.configId, vsyncPeriod); mUpcomingActiveConfig.configId, vsyncPeriod); } } } } Loading Loading @@ -2995,8 +2995,8 @@ void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { // anyway since there are no connected apps at this point. // anyway since there are no connected apps at this point. const nsecs_t vsyncPeriod = const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod(); mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig, mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value, vsyncPeriod); currentConfig, vsyncPeriod); } } void SurfaceFlinger::commitTransaction() void SurfaceFlinger::commitTransaction() Loading Loading @@ -5943,7 +5943,7 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( const nsecs_t vsyncPeriod = getHwComposer() const nsecs_t vsyncPeriod = getHwComposer() .getConfigs(*displayId)[policy->defaultConfig.value()] .getConfigs(*displayId)[policy->defaultConfig.value()] ->getVsyncPeriod(); ->getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, policy->defaultConfig, vsyncPeriod); policy->defaultConfig, vsyncPeriod); return NO_ERROR; return NO_ERROR; } } Loading Loading @@ -5976,7 +5976,7 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( const nsecs_t vsyncPeriod = const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()) mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()) .getVsyncPeriod(); .getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, display->getActiveConfig(), vsyncPeriod); display->getActiveConfig(), vsyncPeriod); auto configId = mScheduler->getPreferredConfigId(); auto configId = mScheduler->getPreferredConfigId(); Loading Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +22 −8 Original line number Original line Diff line number Diff line Loading @@ -115,12 +115,24 @@ std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPe } } const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const std::vector<LayerRequirement>& layers, bool touchActive, bool idle, const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals, bool* touchConsidered) const { GlobalSignals* outSignalsConsidered) const { ATRACE_CALL(); ATRACE_CALL(); ALOGV("getRefreshRateForContent %zu layers", layers.size()); ALOGV("getRefreshRateForContent %zu layers", layers.size()); if (touchConsidered) *touchConsidered = false; if (outSignalsConsidered) *outSignalsConsidered = {}; const auto setTouchConsidered = [&] { if (outSignalsConsidered) { outSignalsConsidered->touch = true; } }; const auto setIdleConsidered = [&] { if (outSignalsConsidered) { outSignalsConsidered->idle = true; } }; std::lock_guard lock(mLock); std::lock_guard lock(mLock); int noVoteLayers = 0; int noVoteLayers = 0; Loading Loading @@ -150,9 +162,9 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // Consider the touch event if there are no Explicit* layers. Otherwise wait until after we've // selected a refresh rate to see if we should apply touch boost. // selected a refresh rate to see if we should apply touch boost. if (touchActive && !hasExplicitVoteLayers) { if (globalSignals.touch && !hasExplicitVoteLayers) { ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str()); if (touchConsidered) *touchConsidered = true; setTouchConsidered(); return getMaxRefreshRateByPolicyLocked(); return getMaxRefreshRateByPolicyLocked(); } } Loading @@ -162,8 +174,10 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( const Policy* policy = getCurrentPolicyLocked(); const Policy* policy = getCurrentPolicyLocked(); const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max; const bool primaryRangeIsSingleRate = policy->primaryRange.min == policy->primaryRange.max; if (!touchActive && idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { if (!globalSignals.touch && globalSignals.idle && !(primaryRangeIsSingleRate && hasExplicitVoteLayers)) { ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); ALOGV("Idle - choose %s", getMinRefreshRateByPolicyLocked().getName().c_str()); setIdleConsidered(); return getMinRefreshRateByPolicyLocked(); return getMinRefreshRateByPolicyLocked(); } } Loading Loading @@ -307,9 +321,9 @@ const RefreshRate& RefreshRateConfigs::getBestRefreshRate( // actually increase the refresh rate over the normal selection. // actually increase the refresh rate over the normal selection. const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked(); if (touchActive && explicitDefaultVoteLayers == 0 && if (globalSignals.touch && explicitDefaultVoteLayers == 0 && bestRefreshRate->fps < touchRefreshRate.fps) { bestRefreshRate->fps < touchRefreshRate.fps) { if (touchConsidered) *touchConsidered = true; setTouchConsidered(); ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str()); return touchRefreshRate; return touchRefreshRate; } } Loading
services/surfaceflinger/Scheduler/RefreshRateConfigs.h +13 −5 Original line number Original line Diff line number Diff line Loading @@ -211,14 +211,22 @@ public: const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const EXCLUDES(mLock); EXCLUDES(mLock); // Global state describing signals that affect refresh rate choice. struct GlobalSignals { // Whether the user touched the screen recently. Used to apply touch boost. bool touch = false; // True if the system hasn't seen any buffers posted to layers recently. bool idle = false; }; // Returns the refresh rate that fits best to the given layers. // Returns the refresh rate that fits best to the given layers. // layers - The layer requirements to consider. // layers - The layer requirements to consider. // touchActive - Whether the user touched the screen recently. Used to apply touch boost. // globalSignals - global state of touch and idle // idle - True if the system hasn't seen any buffers posted to layers recently. // outSignalsConsidered - An output param that tells the caller whether the refresh rate was // touchConsidered - An output param that tells the caller whether the refresh rate was chosen // chosen based on touch boost and/or idle timer. // based on touch boost. const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers, const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers, bool touchActive, bool idle, bool* touchConsidered) const const GlobalSignals& globalSignals, GlobalSignals* outSignalsConsidered = nullptr) const EXCLUDES(mLock); EXCLUDES(mLock); // Returns all the refresh rates supported by the device. This won't change at runtime. // Returns all the refresh rates supported by the device. This won't change at runtime. Loading
services/surfaceflinger/Scheduler/Scheduler.cpp +63 −22 Original line number Original line Diff line number Diff line Loading @@ -228,7 +228,35 @@ void Scheduler::onScreenReleased(ConnectionHandle handle) { mConnections[handle].thread->onScreenReleased(); mConnections[handle].thread->onScreenReleased(); } } void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, void Scheduler::onPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, HwcConfigIndexType configId, nsecs_t vsyncPeriod) { std::lock_guard<std::mutex> lock(mFeatureStateLock); // Cache the last reported config for primary display. mFeatures.cachedConfigChangedParams = {handle, displayId, configId, vsyncPeriod}; onNonPrimaryDisplayConfigChanged(handle, displayId, configId, vsyncPeriod); } void Scheduler::dispatchCachedReportedConfig() { const auto configId = *mFeatures.configId; const auto vsyncPeriod = mRefreshRateConfigs.getRefreshRateFromConfigId(configId).getVsyncPeriod(); // If there is no change from cached config, there is no need to dispatch an event if (configId == mFeatures.cachedConfigChangedParams->configId && vsyncPeriod == mFeatures.cachedConfigChangedParams->vsyncPeriod) { return; } mFeatures.cachedConfigChangedParams->configId = configId; mFeatures.cachedConfigChangedParams->vsyncPeriod = vsyncPeriod; onNonPrimaryDisplayConfigChanged(mFeatures.cachedConfigChangedParams->handle, mFeatures.cachedConfigChangedParams->displayId, mFeatures.cachedConfigChangedParams->configId, mFeatures.cachedConfigChangedParams->vsyncPeriod); } void Scheduler::onNonPrimaryDisplayConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, HwcConfigIndexType configId, nsecs_t vsyncPeriod) { HwcConfigIndexType configId, nsecs_t vsyncPeriod) { RETURN_IF_INVALID_HANDLE(handle); RETURN_IF_INVALID_HANDLE(handle); mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod); mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod); Loading Loading @@ -446,13 +474,21 @@ void Scheduler::chooseRefreshRateForContent() { mFeatures.contentDetectionV1 = mFeatures.contentDetectionV1 = !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off; !summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off; newConfigId = calculateRefreshRateConfigIndexType(); scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals); if (mFeatures.configId == newConfigId) { if (mFeatures.configId == newConfigId) { // We don't need to change the config, but we might need to send an event // about a config change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { dispatchCachedReportedConfig(); } return; return; } } mFeatures.configId = newConfigId; mFeatures.configId = newConfigId; auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed); mSchedulerCallback.changeRefreshRate(newRefreshRate, consideredSignals.idle ? ConfigEvent::None : ConfigEvent::Changed); } } } } Loading Loading @@ -522,21 +558,20 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { } } void Scheduler::idleTimerCallback(TimerState state) { void Scheduler::idleTimerCallback(TimerState state) { handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */); handleTimerStateChanged(&mFeatures.idleTimer, state); ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); } } void Scheduler::touchTimerCallback(TimerState state) { void Scheduler::touchTimerCallback(TimerState state) { const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) { if (handleTimerStateChanged(&mFeatures.touch, touch)) { mLayerHistory->clear(); mLayerHistory->clear(); } } ATRACE_INT("TouchState", static_cast<int>(touch)); ATRACE_INT("TouchState", static_cast<int>(touch)); } } void Scheduler::displayPowerTimerCallback(TimerState state) { void Scheduler::displayPowerTimerCallback(TimerState state) { handleTimerStateChanged(&mFeatures.displayPowerTimer, state, handleTimerStateChanged(&mFeatures.displayPowerTimer, state); true /* eventOnContentDetection */); ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); } } Loading @@ -553,33 +588,37 @@ void Scheduler::dump(std::string& result) const { } } template <class T> template <class T> bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) { bool Scheduler::handleTimerStateChanged(T* currentState, T newState) { ConfigEvent event = ConfigEvent::None; HwcConfigIndexType newConfigId; HwcConfigIndexType newConfigId; bool touchConsidered = false; scheduler::RefreshRateConfigs::GlobalSignals consideredSignals; { { std::lock_guard<std::mutex> lock(mFeatureStateLock); std::lock_guard<std::mutex> lock(mFeatureStateLock); if (*currentState == newState) { if (*currentState == newState) { return touchConsidered; return false; } } *currentState = newState; *currentState = newState; newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered); newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals); if (mFeatures.configId == newConfigId) { if (mFeatures.configId == newConfigId) { return touchConsidered; // We don't need to change the config, but we might need to send an event // about a config change, since it was suppressed due to a previous idleConsidered if (!consideredSignals.idle) { dispatchCachedReportedConfig(); } } mFeatures.configId = newConfigId; return consideredSignals.touch; if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) { event = ConfigEvent::Changed; } } mFeatures.configId = newConfigId; } } const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, event); mSchedulerCallback.changeRefreshRate(newRefreshRate, return touchConsidered; consideredSignals.idle ? ConfigEvent::None : ConfigEvent::Changed); return consideredSignals.touch; } } HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) { HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType( scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) { ATRACE_CALL(); ATRACE_CALL(); if (touchConsidered) *touchConsidered = false; if (consideredSignals) *consideredSignals = {}; // If Display Power is not in normal operation we want to be in performance mode. When coming // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. // back to normal mode, a grace period is given with DisplayPowerTimer. Loading @@ -600,6 +639,7 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchCon // If timer has expired as it means there is no new content on the screen. // If timer has expired as it means there is no new content on the screen. if (idle) { if (idle) { if (consideredSignals) consideredSignals->idle = true; return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId(); return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId(); } } Loading @@ -615,7 +655,8 @@ HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchCon } } return mRefreshRateConfigs return mRefreshRateConfigs .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered) .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle}, consideredSignals) .getConfigId(); .getConfigId(); } } Loading
services/surfaceflinger/Scheduler/Scheduler.h +20 −5 Original line number Original line Diff line number Diff line Loading @@ -81,9 +81,11 @@ public: sp<EventThreadConnection> getEventConnection(ConnectionHandle); sp<EventThreadConnection> getEventConnection(ConnectionHandle); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId, void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId, nsecs_t vsyncPeriod); HwcConfigIndexType configId, nsecs_t vsyncPeriod) EXCLUDES(mFeatureStateLock); void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId, nsecs_t vsyncPeriod); void onScreenAcquired(ConnectionHandle); void onScreenAcquired(ConnectionHandle); void onScreenReleased(ConnectionHandle); void onScreenReleased(ConnectionHandle); Loading Loading @@ -179,16 +181,19 @@ private: // handles various timer features to change the refresh rate. // handles various timer features to change the refresh rate. template <class T> template <class T> bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection); bool handleTimerStateChanged(T* currentState, T newState); void setVsyncPeriod(nsecs_t period); void setVsyncPeriod(nsecs_t period); // This function checks whether individual features that are affecting the refresh rate // This function checks whether individual features that are affecting the refresh rate // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType // selection were initialized, prioritizes them, and calculates the HwcConfigIndexType // for the suggested refresh rate. // for the suggested refresh rate. HwcConfigIndexType calculateRefreshRateConfigIndexType(bool* touchConsidered = nullptr) HwcConfigIndexType calculateRefreshRateConfigIndexType( scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mFeatureStateLock); REQUIRES(mFeatureStateLock); void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock); // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { struct Connection { sp<EventThreadConnection> connection; sp<EventThreadConnection> connection; Loading Loading @@ -240,6 +245,16 @@ private: LayerHistory::Summary contentRequirements; LayerHistory::Summary contentRequirements; bool isDisplayPowerStateNormal = true; bool isDisplayPowerStateNormal = true; // Used to cache the last parameters of onPrimaryDisplayConfigChanged struct ConfigChangedParams { ConnectionHandle handle; PhysicalDisplayId displayId; HwcConfigIndexType configId; nsecs_t vsyncPeriod; }; std::optional<ConfigChangedParams> cachedConfigChangedParams; } mFeatures GUARDED_BY(mFeatureStateLock); } mFeatures GUARDED_BY(mFeatureStateLock); const scheduler::RefreshRateConfigs& mRefreshRateConfigs; const scheduler::RefreshRateConfigs& mRefreshRateConfigs; Loading
services/surfaceflinger/SurfaceFlinger.cpp +8 −8 Original line number Original line Diff line number Diff line Loading @@ -1077,7 +1077,7 @@ void SurfaceFlinger::setActiveConfigInternal() { const nsecs_t vsyncPeriod = const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId) mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId) .getVsyncPeriod(); .getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, mUpcomingActiveConfig.configId, vsyncPeriod); mUpcomingActiveConfig.configId, vsyncPeriod); } } } } Loading Loading @@ -2995,8 +2995,8 @@ void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { // anyway since there are no connected apps at this point. // anyway since there are no connected apps at this point. const nsecs_t vsyncPeriod = const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod(); mRefreshRateConfigs->getRefreshRateFromConfigId(currentConfig).getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, primaryDisplayId.value, currentConfig, mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, primaryDisplayId.value, vsyncPeriod); currentConfig, vsyncPeriod); } } void SurfaceFlinger::commitTransaction() void SurfaceFlinger::commitTransaction() Loading Loading @@ -5943,7 +5943,7 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( const nsecs_t vsyncPeriod = getHwComposer() const nsecs_t vsyncPeriod = getHwComposer() .getConfigs(*displayId)[policy->defaultConfig.value()] .getConfigs(*displayId)[policy->defaultConfig.value()] ->getVsyncPeriod(); ->getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, mScheduler->onNonPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, policy->defaultConfig, vsyncPeriod); policy->defaultConfig, vsyncPeriod); return NO_ERROR; return NO_ERROR; } } Loading Loading @@ -5976,7 +5976,7 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( const nsecs_t vsyncPeriod = const nsecs_t vsyncPeriod = mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()) mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()) .getVsyncPeriod(); .getVsyncPeriod(); mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, mScheduler->onPrimaryDisplayConfigChanged(mAppConnectionHandle, display->getId()->value, display->getActiveConfig(), vsyncPeriod); display->getActiveConfig(), vsyncPeriod); auto configId = mScheduler->getPreferredConfigId(); auto configId = mScheduler->getPreferredConfigId(); Loading