SurfaceFlinger: missing configChanged event on idle
This change fixes a condition where a configChanged event
will be dropped due to idle timer.
Change-Id: I803f478c261be9fceb3495526576b495d7d0f385
Bug: 155367075
Test: CtsGraphicsTestCases
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index ea27955..6dbff14 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -115,12 +115,24 @@
}
const RefreshRate& RefreshRateConfigs::getBestRefreshRate(
- const std::vector<LayerRequirement>& layers, bool touchActive, bool idle,
- bool* touchConsidered) const {
+ const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered) const {
ATRACE_CALL();
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);
int noVoteLayers = 0;
@@ -150,9 +162,9 @@
// 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.
- if (touchActive && !hasExplicitVoteLayers) {
+ if (globalSignals.touch && !hasExplicitVoteLayers) {
ALOGV("TouchBoost - choose %s", getMaxRefreshRateByPolicyLocked().getName().c_str());
- if (touchConsidered) *touchConsidered = true;
+ setTouchConsidered();
return getMaxRefreshRateByPolicyLocked();
}
@@ -162,8 +174,10 @@
const Policy* policy = getCurrentPolicyLocked();
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());
+ setIdleConsidered();
return getMinRefreshRateByPolicyLocked();
}
@@ -307,9 +321,9 @@
// actually increase the refresh rate over the normal selection.
const RefreshRate& touchRefreshRate = getMaxRefreshRateByPolicyLocked();
- if (touchActive && explicitDefaultVoteLayers == 0 &&
+ if (globalSignals.touch && explicitDefaultVoteLayers == 0 &&
bestRefreshRate->fps < touchRefreshRate.fps) {
- if (touchConsidered) *touchConsidered = true;
+ setTouchConsidered();
ALOGV("TouchBoost - choose %s", touchRefreshRate.getName().c_str());
return touchRefreshRate;
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 88e4eb5..584a5e7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -211,14 +211,22 @@
const RefreshRate& getRefreshRateForContent(const std::vector<LayerRequirement>& layers) const
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.
// layers - The layer requirements to consider.
- // touchActive - Whether the user touched the screen recently. Used to apply touch boost.
- // idle - True if the system hasn't seen any buffers posted to layers recently.
- // touchConsidered - An output param that tells the caller whether the refresh rate was chosen
- // based on touch boost.
+ // globalSignals - global state of touch and idle
+ // outSignalsConsidered - An output param that tells the caller whether the refresh rate was
+ // chosen based on touch boost and/or idle timer.
const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers,
- bool touchActive, bool idle, bool* touchConsidered) const
+ const GlobalSignals& globalSignals,
+ GlobalSignals* outSignalsConsidered = nullptr) const
EXCLUDES(mLock);
// Returns all the refresh rates supported by the device. This won't change at runtime.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index bfa7493..5c0ba01 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -228,8 +228,36 @@
mConnections[handle].thread->onScreenReleased();
}
-void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- HwcConfigIndexType configId, nsecs_t vsyncPeriod) {
+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) {
RETURN_IF_INVALID_HANDLE(handle);
mConnections[handle].thread->onConfigChanged(displayId, configId, vsyncPeriod);
}
@@ -446,13 +474,21 @@
mFeatures.contentDetectionV1 =
!summary.empty() ? ContentDetectionState::On : ContentDetectionState::Off;
- newConfigId = calculateRefreshRateConfigIndexType();
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
+ newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
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;
}
mFeatures.configId = newConfigId;
auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
}
}
@@ -522,21 +558,20 @@
}
void Scheduler::idleTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */);
+ handleTimerStateChanged(&mFeatures.idleTimer, state);
ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
void Scheduler::touchTimerCallback(TimerState state) {
const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive;
- if (handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */)) {
+ if (handleTimerStateChanged(&mFeatures.touch, touch)) {
mLayerHistory->clear();
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
void Scheduler::displayPowerTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.displayPowerTimer, state,
- true /* eventOnContentDetection */);
+ handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
@@ -553,33 +588,37 @@
}
template <class T>
-bool Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
- ConfigEvent event = ConfigEvent::None;
+bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
HwcConfigIndexType newConfigId;
- bool touchConsidered = false;
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) {
- return touchConsidered;
+ return false;
}
*currentState = newState;
- newConfigId = calculateRefreshRateConfigIndexType(&touchConsidered);
+ newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
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();
+ }
+ return consideredSignals.touch;
}
mFeatures.configId = newConfigId;
- if (eventOnContentDetection && !mFeatures.contentRequirements.empty()) {
- event = ConfigEvent::Changed;
- }
}
const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
- return touchConsidered;
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
+ return consideredSignals.touch;
}
-HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(bool* touchConsidered) {
+HwcConfigIndexType Scheduler::calculateRefreshRateConfigIndexType(
+ scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals) {
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
// back to normal mode, a grace period is given with DisplayPowerTimer.
@@ -600,6 +639,7 @@
// If timer has expired as it means there is no new content on the screen.
if (idle) {
+ if (consideredSignals) consideredSignals->idle = true;
return mRefreshRateConfigs.getMinRefreshRateByPolicy().getConfigId();
}
@@ -615,7 +655,8 @@
}
return mRefreshRateConfigs
- .getBestRefreshRate(mFeatures.contentRequirements, touchActive, idle, touchConsidered)
+ .getBestRefreshRate(mFeatures.contentRequirements, {.touch = touchActive, .idle = idle},
+ consideredSignals)
.getConfigId();
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ebee9e3..9e24f909 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -81,9 +81,11 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId,
- nsecs_t vsyncPeriod);
-
+ void onPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod)
+ EXCLUDES(mFeatureStateLock);
+ void onNonPrimaryDisplayConfigChanged(ConnectionHandle, PhysicalDisplayId,
+ HwcConfigIndexType configId, nsecs_t vsyncPeriod);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -179,16 +181,19 @@
// handles various timer features to change the refresh rate.
template <class T>
- bool handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection);
+ bool handleTimerStateChanged(T* currentState, T newState);
void setVsyncPeriod(nsecs_t period);
// This function checks whether individual features that are affecting the refresh rate
// selection were initialized, prioritizes them, and calculates the HwcConfigIndexType
// for the suggested refresh rate.
- HwcConfigIndexType calculateRefreshRateConfigIndexType(bool* touchConsidered = nullptr)
+ HwcConfigIndexType calculateRefreshRateConfigIndexType(
+ scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
REQUIRES(mFeatureStateLock);
+ void dispatchCachedReportedConfig() REQUIRES(mFeatureStateLock);
+
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
sp<EventThreadConnection> connection;
@@ -240,6 +245,16 @@
LayerHistory::Summary contentRequirements;
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);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;