Change SF power hints to use early frame predictions
Currently we issue hints at the end of the frame reporting what happened
that frame. However, this is often too late anyway and lacks critical
information not known until after the frame completes, like gpu timing.
By waiting until the next frame start and using those timings along with
information about gpu duration, the new expected present time, and the
new wakeup time, we can instead use those timings to predict the
duration of the upcoming frame and signal in advance of a frame drop.
This patch also changes how and where frame timing is done and measures
gpu duration so we can predict if a cpu speedup can help gpu composition
happen earlier, preventing a drop that way.
Bug: b/195990840
Test: manual
Change-Id: I5f0ae796198631fced503dce8e6c495885fb256a
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e72e21c..0cb8e0e 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -444,6 +444,11 @@
}
mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false);
+
+ // Power hint session mode, representing which hint(s) to send: early, late, or both)
+ mPowerHintSessionMode =
+ {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true),
+ .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, true)};
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -1991,12 +1996,6 @@
bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
- // we set this once at the beginning of commit to ensure consistency throughout the whole frame
- mPowerHintSessionData.sessionEnabled = mPowerAdvisor->usePowerHintSession();
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.commitStart = systemTime();
- }
-
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
@@ -2010,10 +2009,6 @@
const nsecs_t lastScheduledPresentTime = mScheduledPresentTime;
mScheduledPresentTime = expectedVsyncTime;
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerAdvisor->setTargetWorkDuration(mExpectedPresentTime -
- mPowerHintSessionData.commitStart);
- }
const auto vsyncIn = [&] {
if (!ATRACE_ENABLED()) return 0.f;
return (mExpectedPresentTime - systemTime()) / 1e6f;
@@ -2089,6 +2084,30 @@
}
}
+ // Save this once per commit + composite to ensure consistency
+ mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession();
+ if (mPowerHintSessionEnabled) {
+ nsecs_t vsyncPeriod;
+ {
+ Mutex::Autolock lock(mStateLock);
+ vsyncPeriod = getVsyncPeriodFromHWC();
+ }
+ mPowerAdvisor->setCommitStart(frameTime);
+ mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime);
+ const nsecs_t idealSfWorkDuration =
+ mVsyncModulator->getVsyncConfig().sfWorkDuration.count();
+ // Frame delay is how long we should have minus how long we actually have
+ mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime));
+ mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
+ mPowerAdvisor->setTargetWorkDuration(vsyncPeriod);
+
+ // Send early hint here to make sure there's not another frame pending
+ if (mPowerHintSessionMode.early) {
+ // Send a rough prediction for this frame based on last frame's timing info
+ mPowerAdvisor->sendPredictedWorkDuration();
+ }
+ }
+
if (mTracingEnabledChanged) {
mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
@@ -2165,16 +2184,15 @@
FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId);
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.compositeStart = systemTime();
- }
-
compositionengine::CompositionRefreshArgs refreshArgs;
const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays);
refreshArgs.outputs.reserve(displays.size());
+ std::vector<DisplayId> displayIds;
for (const auto& [_, display] : displays) {
refreshArgs.outputs.push_back(display->getCompositionDisplay());
+ displayIds.push_back(display->getId());
}
+ mPowerAdvisor->setDisplays(displayIds);
mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) {
if (auto layerFE = layer->getCompositionEngineLayerFE())
refreshArgs.layers.push_back(layerFE);
@@ -2222,12 +2240,15 @@
mCompositionEngine->present(refreshArgs);
- if (mPowerHintSessionData.sessionEnabled) {
- mPowerHintSessionData.presentEnd = systemTime();
- }
-
mTimeStats->recordFrameDuration(frameTime, systemTime());
+ // Send a power hint hint after presentation is finished
+ if (mPowerHintSessionEnabled) {
+ if (mPowerHintSessionMode.late) {
+ mPowerAdvisor->sendActualWorkDuration();
+ }
+ }
+
if (mScheduler->onPostComposition(presentTime)) {
scheduleComposite(FrameHint::kNone);
}
@@ -2272,11 +2293,8 @@
scheduleCommit(FrameHint::kNone);
}
- // calculate total render time for performance hinting if adpf cpu hint is enabled,
- if (mPowerHintSessionData.sessionEnabled) {
- const nsecs_t flingerDuration =
- (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart);
- mPowerAdvisor->sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd);
+ if (mPowerHintSessionEnabled) {
+ mPowerAdvisor->setCompositeEnd(systemTime());
}
}