SF: Adding callback to Scheduler for setting refresh rate to 60 and 90.
When device is idle, refresh rate is set to 60. When not it's set to 90.
See go/surface-flinger-scheduler for more info.
Test: All SF tests pass.
Bug: 113612090
Bug: 122347908
Change-Id: Ica6e483118db276f72d3cb4e79535303c76f99d7
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 1683982..bf925b2 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -67,7 +67,13 @@
}
void EventThreadConnection::requestNextVsync() {
- mEventThread->requestNextVsync(this);
+ ATRACE_NAME("requestNextVsync");
+ mEventThread->requestNextVsync(this, true);
+}
+
+void EventThreadConnection::requestNextVsyncForHWC() {
+ ATRACE_NAME("requestNextVsyncForHWC");
+ mEventThread->requestNextVsync(this, false);
}
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
@@ -184,16 +190,17 @@
}
}
-void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection) {
- std::lock_guard<std::mutex> lock(mMutex);
- if (mResetIdleTimer) {
+void EventThread::requestNextVsync(const sp<EventThreadConnection>& connection, bool reset) {
+ if (mResetIdleTimer && reset) {
+ ATRACE_NAME("resetIdleTimer");
mResetIdleTimer();
}
-
if (mResyncWithRateLimitCallback) {
mResyncWithRateLimitCallback();
}
+ std::lock_guard<std::mutex> lock(mMutex);
+
if (connection->count < 0) {
connection->count = 0;
mCondition.notify_all();
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 66f54bd..e110488 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -68,6 +68,9 @@
status_t stealReceiveChannel(gui::BitTube* outChannel) override;
status_t setVsyncRate(uint32_t count) override;
void requestNextVsync() override; // asynchronous
+ // Requesting Vsync for HWC does not reset the idle timer, since HWC requires a refresh
+ // in order to update the configs.
+ void requestNextVsyncForHWC();
// count >= 1 : continuous event. count is the vsync rate
// count == 0 : one-shot event that has not fired
@@ -105,7 +108,9 @@
virtual status_t registerDisplayEventConnection(
const sp<EventThreadConnection>& connection) = 0;
virtual void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) = 0;
- virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
+ // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
+ virtual void requestNextVsync(const sp<EventThreadConnection>& connection,
+ bool resetIdleTimer) = 0;
};
namespace impl {
@@ -129,7 +134,8 @@
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t count, const sp<EventThreadConnection>& connection) override;
- void requestNextVsync(const sp<EventThreadConnection>& connection) override;
+ void requestNextVsync(const sp<EventThreadConnection>& connection,
+ bool resetIdleTimer) override;
// called before the screen is turned off from main thread
void onScreenReleased() override;
@@ -166,6 +172,9 @@
// Implements VSyncSource::Callback
void onVSyncEvent(nsecs_t timestamp) override;
+ // Acquires mutex and requests next vsync.
+ void requestNextVsyncInternal(const sp<EventThreadConnection>& connection) EXCLUDES(mMutex);
+
// TODO(b/113612090): Once the Scheduler is complete this pointer will become obsolete.
VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr;
std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr;
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 36403cc..66f42bb 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -148,6 +148,10 @@
mEvents->requestNextVsync();
}
+void MessageQueue::invalidateForHWC() {
+ mEvents->requestNextVsyncForHWC();
+}
+
void MessageQueue::refresh() {
mHandler->dispatchRefresh();
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 24a3834..0bf00b0 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -91,6 +91,7 @@
virtual void waitMessage() = 0;
virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0;
virtual void invalidate() = 0;
+ virtual void invalidateForHWC() = 0;
virtual void refresh() = 0;
};
@@ -134,6 +135,9 @@
// sends INVALIDATE message at next VSYNC
void invalidate() override;
+
+ // sends INVALIDATE message at next VSYNC, without resetting the idle timer in the Scheduler
+ void invalidateForHWC();
// sends REFRESH message at next VSYNC
void refresh() override;
};
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index c363ba5..fec53af 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -238,6 +238,16 @@
mLayerHistory.incrementCounter();
}
+void Scheduler::setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mExpiredTimerCallback = expiredTimerCallback;
+}
+
+void Scheduler::setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ mResetTimerCallback = resetTimerCallback;
+}
+
void Scheduler::updateFrameSkipping(const int64_t skipCount) {
ATRACE_INT("FrameSkipCount", skipCount);
if (mSkipCount != skipCount) {
@@ -314,12 +324,12 @@
// TODO(b/113612090): This are current numbers from trial and error while running videos
// from YouTube at 24, 30, and 60 fps.
if (mean > 14 && mean < 18) {
- ATRACE_INT("FPS", 60);
+ ATRACE_INT("MediaFPS", 60);
} else if (mean > 31 && mean < 34) {
- ATRACE_INT("FPS", 30);
+ ATRACE_INT("MediaFPS", 30);
return;
} else if (mean > 39 && mean < 42) {
- ATRACE_INT("FPS", 24);
+ ATRACE_INT("MediaFPS", 24);
}
}
@@ -328,13 +338,19 @@
mIdleTimer->reset();
ATRACE_INT("ExpiredIdleTimer", 0);
}
+
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mResetTimerCallback) {
+ mResetTimerCallback();
+ }
}
void Scheduler::expiredTimerCallback() {
- // TODO(b/113612090): Each time a timer expired, we should record the information into
- // a circular buffer. Once this has happened a given amount (TBD) of times, we can comfortably
- // say that the device is sitting in idle.
- ATRACE_INT("ExpiredIdleTimer", 1);
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mExpiredTimerCallback) {
+ mExpiredTimerCallback();
+ ATRACE_INT("ExpiredIdleTimer", 1);
+ }
}
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 9d7dd4d..3538f31 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -36,6 +36,9 @@
class Scheduler {
public:
+ using ExpiredIdleTimerCallback = std::function<void()>;
+ using ResetIdleTimerCallback = std::function<void()>;
+
// Enum to indicate whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
@@ -111,6 +114,10 @@
const std::string layerName);
// Increments counter in the layer history to indicate that SF has started a new frame.
void incrementFrameCounter();
+ // Callback that gets invoked once the idle timer expires.
+ void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback);
+ // Callback that gets invoked once the idle timer is reset.
+ void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback);
protected:
virtual std::unique_ptr<EventThread> makeEventThread(
@@ -173,6 +180,10 @@
// interval, a callback is fired. Set this variable to >0 to use this feature.
int64_t mSetIdleTimerMs = 0;
std::unique_ptr<scheduler::IdleTimer> mIdleTimer;
+
+ std::mutex mCallbackLock;
+ ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock);
+ ResetIdleTimerCallback mResetTimerCallback GUARDED_BY(mCallbackLock);
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index dc82b32..bd16d64 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -729,6 +729,11 @@
ALOGE("Run StartPropertySetThread failed!");
}
+ if (mUseScheduler) {
+ mScheduler->setExpiredIdleTimerCallback([this]() { setRefreshRateTo(60.f /* fps */); });
+ mScheduler->setResetIdleTimerCallback([this]() { setRefreshRateTo(90.f /* fps */); });
+ }
+
ALOGV("Done initializing");
}
@@ -934,14 +939,45 @@
return display->getActiveConfig();
}
-void SurfaceFlinger::setActiveConfigInternal(const sp<DisplayDevice>& display, int mode) {
+status_t SurfaceFlinger::setActiveConfigAsync(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_NAME("setActiveConfigAsync");
+ postMessageAsync(new LambdaMessage([=] { setActiveConfigInternal(displayToken, mode); }));
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
+ ATRACE_NAME("setActiveConfigSync");
+ postMessageSync(new LambdaMessage([&] { setActiveConfigInternal(displayToken, mode); }));
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setActiveConfigInternal(const sp<IBinder>& displayToken, int mode) {
+ Vector<DisplayInfo> configs;
+ getDisplayConfigs(displayToken, &configs);
+ if (mode < 0 || mode >= static_cast<int>(configs.size())) {
+ ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size());
+ return;
+ }
+
+ const auto display = getDisplayDevice(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set active config %d for invalid display token %p", mode,
+ displayToken.get());
+ return;
+ }
if (display->isVirtual()) {
- ALOGE("%s: Invalid operation on virtual display", __FUNCTION__);
+ ALOGW("Attempt to set active config %d for virtual display", mode);
+ return;
+ }
+ int currentDisplayPowerMode = display->getPowerMode();
+ if (currentDisplayPowerMode != HWC_POWER_MODE_NORMAL) {
+ // Don't change active config when in AoD.
return;
}
int currentMode = display->getActiveConfig();
if (mode == currentMode) {
+ // Don't update config if we are already running in the desired mode.
return;
}
@@ -950,29 +986,9 @@
display->setActiveConfig(mode);
getHwComposer().setActiveConfig(*displayId, mode);
-}
-status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
- postMessageSync(new LambdaMessage([&] {
- Vector<DisplayInfo> configs;
- getDisplayConfigs(displayToken, &configs);
- if (mode < 0 || mode >= static_cast<int>(configs.size())) {
- ALOGE("Attempt to set active config %d for display with %zu configs", mode,
- configs.size());
- return;
- }
- const auto display = getDisplayDevice(displayToken);
- if (!display) {
- ALOGE("Attempt to set active config %d for invalid display token %p", mode,
- displayToken.get());
- } else if (display->isVirtual()) {
- ALOGW("Attempt to set active config %d for virtual display", mode);
- } else {
- setActiveConfigInternal(display, mode);
- }
- }));
-
- return NO_ERROR;
+ ATRACE_INT("ActiveConfigMode", mode);
+ resyncToHardwareVsync(true);
}
status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken,
@@ -1382,6 +1398,49 @@
*compositorTiming = getBE().mCompositorTiming;
}
+void SurfaceFlinger::setRefreshRateTo(float newFps) {
+ const auto displayId = getInternalDisplayId();
+ if (!displayId || mBootStage != BootStage::FINISHED) {
+ return;
+ }
+ // TODO(b/113612090): There should be a message queue flush here. Because this esentially
+ // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better
+ // manner, once the setActiveConfig is synchronous, and is executed at a known time in a
+ // refresh cycle.
+
+ // Don't do any updating if the current fps is the same as the new one.
+ const auto activeConfig = getHwComposer().getActiveConfig(*displayId);
+ const nsecs_t currentVsyncPeriod = activeConfig->getVsyncPeriod();
+ if (currentVsyncPeriod == 0) {
+ return;
+ }
+ // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than
+ // floating numbers.
+ const float currentFps = 1e9 / currentVsyncPeriod;
+ if (std::abs(currentFps - newFps) <= 1) {
+ return;
+ }
+
+ auto configs = getHwComposer().getConfigs(*displayId);
+ for (int i = 0; i < configs.size(); i++) {
+ const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod();
+ if (vsyncPeriod == 0) {
+ continue;
+ }
+ const float fps = 1e9 / vsyncPeriod;
+ // TODO(b/113612090): There should be a better way at determining which config
+ // has the right refresh rate.
+ if (std::abs(fps - newFps) <= 1) {
+ const auto display = getBuiltInDisplay(HWC_DISPLAY_PRIMARY);
+ if (!display) return;
+ // This is posted in async function to avoid deadlock when getDisplayDevice
+ // requires mStateLock.
+ setActiveConfigAsync(display, i);
+ ATRACE_INT("FPS", newFps);
+ }
+ }
+}
+
void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
HWC2::Connection connection) {
ALOGV("%s(%d, %" PRIu64 ", %s)", __FUNCTION__, sequenceId, hwcDisplayId,
@@ -1413,7 +1472,7 @@
if (sequenceId != getBE().mComposerSequenceId) {
return;
}
- repaintEverything();
+ repaintEverythingForHWC();
}
void SurfaceFlinger::setVsyncEnabled(EventThread::DisplayType /*displayType*/, bool enabled) {
@@ -5177,6 +5236,11 @@
signalTransaction();
}
+void SurfaceFlinger::repaintEverythingForHWC() {
+ mRepaintEverything = true;
+ mEventQueue->invalidateForHWC();
+}
+
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ac730ab..eb7127e 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -311,6 +311,9 @@
// force full composition on all displays
void repaintEverything();
+ // force full composition on all displays without resetting the scheduler idle timer.
+ void repaintEverythingForHWC();
+
surfaceflinger::Factory& getFactory() { return mFactory; }
// The CompositionEngine encapsulates all composition related interfaces and actions.
@@ -502,8 +505,10 @@
// called on the main thread in response to initializeDisplays()
void onInitializeDisplays();
+ // setActiveConfigInternal() posted on a main thread for async execution
+ status_t setActiveConfigAsync(const sp<IBinder>& displayToken, int mode);
// called on the main thread in response to setActiveConfig()
- void setActiveConfigInternal(const sp<DisplayDevice>& display, int mode);
+ void setActiveConfigInternal(const sp<IBinder>& displayToken, int mode);
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode);
@@ -763,6 +768,9 @@
void resyncWithRateLimit();
void getCompositorTiming(CompositorTiming* compositorTiming);
private:
+ // Sets the refresh rate to newFps by switching active configs, if they are available for
+ // the desired refresh rate.
+ void setRefreshRateTo(float newFps);
/* ------------------------------------------------------------------------
* Debugging & dumpsys
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index acbed51..2d26bb3 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -200,7 +200,7 @@
TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) {
// Signal that we want the next vsync event to be posted to the connection
- mThread->requestNextVsync(mConnection);
+ mThread->requestNextVsync(mConnection, false);
// EventThread should immediately request a resync.
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index bb6e183..48d45fa 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -37,7 +37,7 @@
MOCK_METHOD1(registerDisplayEventConnection,
status_t(const sp<android::EventThreadConnection> &));
MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &));
- MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &));
+ MOCK_METHOD2(requestNextVsync, void(const sp<android::EventThreadConnection> &, bool));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
index dc8d606..8c113e2 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
@@ -35,6 +35,7 @@
MOCK_METHOD0(waitMessage, void());
MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t));
MOCK_METHOD0(invalidate, void());
+ MOCK_METHOD0(invalidateForHWC, void());
MOCK_METHOD0(refresh, void());
};