SurfaceFlinger: override frame rate for applications that use setFrameRate
When possible, override the frame rate of an app if it explicitly
set a preferred frame rate.
Test: atest FrameRateOverrideHostTest
Test: SF unit tests
Bug: 170502573
Change-Id: Ifcb7dd1ad0423b05efe8a0e86f2d988c49e74054
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 07411b0..d2eb725 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -206,8 +206,32 @@
readyDuration, traceVsync, name);
}
+std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const {
+ std::lock_guard lock(mFeatureStateLock);
+ {
+ const auto iter = mFrameRateOverridesFromBackdoor.find(uid);
+ if (iter != mFrameRateOverridesFromBackdoor.end()) {
+ return std::make_optional<Fps>(iter->second);
+ }
+ }
+
+ {
+ const auto iter = mFeatures.frameRateOverrides.find(uid);
+ if (iter != mFeatures.frameRateOverrides.end()) {
+ return std::make_optional<Fps>(iter->second);
+ }
+ }
+
+ return std::nullopt;
+}
+
bool Scheduler::isVsyncValid(nsecs_t expectedVsyncTimestamp, uid_t uid) const {
- const auto divider = mRefreshRateConfigs.getRefreshRateDividerForUid(uid);
+ const auto frameRate = getFrameRateOverride(uid);
+ if (!frameRate.has_value()) {
+ return true;
+ }
+
+ const auto divider = mRefreshRateConfigs.getRefreshRateDivider(*frameRate);
if (divider <= 1) {
return true;
}
@@ -290,8 +314,19 @@
thread->onScreenReleased();
}
-void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- std::vector<FrameRateOverride> overrides) {
+void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) {
+ std::vector<FrameRateOverride> overrides;
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ for (const auto& [uid, frameRate] : mFeatures.frameRateOverrides) {
+ if (mFrameRateOverridesFromBackdoor.count(uid) == 0) {
+ overrides.emplace_back(FrameRateOverride{uid, frameRate.getValue()});
+ }
+ }
+ }
android::EventThread* thread;
{
std::lock_guard<std::mutex> lock(mConnectionsLock);
@@ -303,9 +338,11 @@
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};
+ {
+ 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);
}
@@ -550,7 +587,10 @@
ATRACE_CALL();
scheduler::LayerHistory::Summary summary = mLayerHistory->summarize(systemTime());
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
HwcConfigIndexType newConfigId;
+ bool frameRateChanged;
+ bool frameRateOverridesChanged;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (mFeatures.contentRequirements == summary) {
@@ -558,22 +598,32 @@
}
mFeatures.contentRequirements = summary;
- scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+ auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ frameRateOverridesChanged =
+ updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
+
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;
+ frameRateChanged = false;
+ } else {
+ mFeatures.configId = newConfigId;
+ frameRateChanged = true;
}
- mFeatures.configId = newConfigId;
+ }
+ if (frameRateChanged) {
auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate,
consideredSignals.idle ? ConfigEvent::None
: ConfigEvent::Changed);
}
+ if (frameRateOverridesChanged) {
+ mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ }
}
void Scheduler::resetIdleTimer() {
@@ -666,6 +716,21 @@
StringAppendF(&result, "+ Content detection: %s %s\n\n",
toContentDetectionString(mOptions.useContentDetection),
mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+
+ {
+ std::lock_guard lock(mFeatureStateLock);
+ StringAppendF(&result, "Frame Rate Overrides (backdoor): {");
+ for (const auto& [uid, frameRate] : mFrameRateOverridesFromBackdoor) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+
+ StringAppendF(&result, "Frame Rate Overrides (setFrameRate): {");
+ for (const auto& [uid, frameRate] : mFeatures.frameRateOverrides) {
+ StringAppendF(&result, "[uid: %d frameRate: %s], ", uid, to_string(frameRate).c_str());
+ }
+ StringAppendF(&result, "}\n");
+ }
}
void Scheduler::dumpVsync(std::string& s) const {
@@ -677,9 +742,35 @@
mVsyncSchedule.dispatch->dump(s);
}
+bool Scheduler::updateFrameRateOverrides(
+ scheduler::RefreshRateConfigs::GlobalSignals consideredSignals, Fps displayRefreshRate) {
+ if (consideredSignals.touch) {
+ const bool changed = !mFeatures.frameRateOverrides.empty();
+ mFeatures.frameRateOverrides.clear();
+ return changed;
+ }
+
+ if (!consideredSignals.idle) {
+ const auto frameRateOverrides =
+ mRefreshRateConfigs.getFrameRateOverrides(mFeatures.contentRequirements,
+ displayRefreshRate);
+ if (!std::equal(mFeatures.frameRateOverrides.begin(), mFeatures.frameRateOverrides.end(),
+ frameRateOverrides.begin(), frameRateOverrides.end(),
+ [](const std::pair<uid_t, Fps>& a, const std::pair<uid_t, Fps>& b) {
+ return a.first == b.first && a.second.equalsWithMargin(b.second);
+ })) {
+ mFeatures.frameRateOverrides = frameRateOverrides;
+ return true;
+ }
+ }
+ return false;
+}
+
template <class T>
bool Scheduler::handleTimerStateChanged(T* currentState, T newState) {
HwcConfigIndexType newConfigId;
+ bool refreshRateChanged = false;
+ bool frameRateOverridesChanged;
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
@@ -688,20 +779,32 @@
}
*currentState = newState;
newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals);
+ const RefreshRate& newRefreshRate =
+ mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ frameRateOverridesChanged =
+ updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps());
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 consideredSignals.touch;
+ } else {
+ mFeatures.configId = newConfigId;
+ refreshRateChanged = true;
}
- mFeatures.configId = newConfigId;
}
- const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ConfigEvent::None
- : ConfigEvent::Changed);
+ if (refreshRateChanged) {
+ const RefreshRate& newRefreshRate =
+ mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+
+ mSchedulerCallback.changeRefreshRate(newRefreshRate,
+ consideredSignals.idle ? ConfigEvent::None
+ : ConfigEvent::Changed);
+ }
+ if (frameRateOverridesChanged) {
+ mSchedulerCallback.triggerOnFrameRateOverridesChanged();
+ }
return consideredSignals.touch;
}
@@ -775,4 +878,17 @@
}
}
+void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
+ if (frameRateOverride.frameRateHz > 0.f && frameRateOverride.frameRateHz < 1.f) {
+ return;
+ }
+
+ std::lock_guard lock(mFeatureStateLock);
+ if (frameRateOverride.frameRateHz != 0.f) {
+ mFrameRateOverridesFromBackdoor[frameRateOverride.uid] = Fps(frameRateOverride.frameRateHz);
+ } else {
+ mFrameRateOverridesFromBackdoor.erase(frameRateOverride.uid);
+ }
+}
+
} // namespace android