SurfaceFlinger: introduce PhaseOffsetsAsDurations
Currently we define phase offset for each refresh rate. This works for
<= 2 refresh rates, but doesn't scale well. This change is introducing
a new way to calculate phase offsets, which is based on duration. Then,
based on the duration and refresh rate, a phase offset is calculated.
The calculation is captured here: https://docs.google.com/spreadsheets/d/1a_5cVNY3LUAkeg-yL56rYQNwved6Hy-dvEcKSxp6f8k/edit#gid=0
Bug: 145561086
Bug: 141329414
Test: jank tests
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Change-Id: I16aaf7437d30c4b12f955bdaac36582dd100519f
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 3403b28..dddd552 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -260,7 +260,6 @@
mFrameTracer(std::make_unique<FrameTracer>()),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
- mPhaseOffsets(mFactory.createPhaseOffsets()),
mPendingSyncInputWindows(false) {}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
@@ -582,8 +581,6 @@
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
-
Mutex::Autolock _l(mStateLock);
// Get a RenderEngine for the given display / config (can't fail)
@@ -816,7 +813,7 @@
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(info.fps);
+ const auto offset = mPhaseConfiguration->getOffsetsForRefreshRate(info.fps);
info.appVsyncOffset = offset.late.app;
// This is how far in advance a buffer must be queued for
@@ -896,8 +893,8 @@
// DispSync model is locked.
mVSyncModulator->onRefreshRateChangeInitiated();
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
mDesiredActiveConfigChanged = true;
@@ -930,8 +927,8 @@
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -948,8 +945,8 @@
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
bool SurfaceFlinger::performSetActiveConfig() {
@@ -1597,10 +1594,8 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- const sp<Fence>& fence =
- mVSyncModulator->getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
if (fence == Fence::NO_FENCE) {
return false;
@@ -1618,10 +1613,8 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
// Inflate the expected present time if we're targetting the next vsync.
- mExpectedPresentTime.store(mVSyncModulator->getOffsets().sf <
- mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? presentTime
- : presentTime + stats.vsyncPeriod);
+ mExpectedPresentTime.store(
+ mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod);
}
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
@@ -1865,11 +1858,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
- ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
- : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
+ nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ ? (stats.vsyncPeriod -
+ (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
+ : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
- // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
+ // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -1878,8 +1872,8 @@
// composition and present times, which often have >1ms of jitter.
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
- // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
- // with (presentLatency % interval).
+ // Snapping also allows an app to precisely calculate
+ // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2570,24 +2564,24 @@
currentConfig, HWC_POWER_MODE_OFF);
mRefreshRateStats->setConfigMode(currentConfig);
+ mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+
// start the EventThread
mScheduler =
getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
*mRefreshRateConfigs, *this);
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->getCurrentOffsets());
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
@@ -3985,7 +3979,7 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseOffsets->dump(result);
+ mPhaseConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriod());