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/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 0c4a752..a950c76 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,6 +45,7 @@
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
"LayerMetadataTest.cpp",
+ "PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"RefreshRateConfigsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0aa8cf5..2e705da 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -51,7 +51,6 @@
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
- static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
static constexpr int mIterations = 100;
};
@@ -79,8 +78,7 @@
void DispSyncSourceTest::createDispSyncSource() {
createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
- mOffsetThresholdForNextVsync.count(), true,
+ mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
"DispSyncSourceTest");
mDispSyncSource->setCallback(this);
}
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index da4eea0..b50ddf5 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -22,7 +22,7 @@
namespace android::scheduler {
-struct FakePhaseOffsets : PhaseOffsets {
+struct FakePhaseOffsets : PhaseConfiguration {
static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
@@ -30,8 +30,7 @@
Offsets getCurrentOffsets() const override {
return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- FAKE_PHASE_OFFSET_NS};
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
}
void setRefreshRateFps(float) override {}
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
new file mode 100644
index 0000000..6360ec1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/PhaseOffsets.h"
+
+using namespace testing;
+
+namespace android {
+namespace scheduler {
+
+class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
+public:
+ TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
+ sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
+ appEarlyGlDuration) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest()
+ : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
+
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsetsAsDurations mPhaseOffsets;
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseOffsets.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 6'166'667);
+
+ EXPECT_EQ(offsets.late.app, 2'333'334);
+
+ EXPECT_EQ(offsets.early.sf, 666'667);
+
+ EXPECT_EQ(offsets.early.app, 500'001);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+
+ EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseOffsets.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 611'111);
+
+ EXPECT_EQ(offsets.late.app, 2'333'333);
+
+ EXPECT_EQ(offsets.early.sf, -4'888'889);
+
+ EXPECT_EQ(offsets.early.app, 6'055'555);
+
+ EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+
+ EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+ TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](auto& offsets) {
+ EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.late.app, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.early.app, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+ };
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
+ currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 8a7c132..c6c3147 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -79,7 +79,8 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
@@ -203,6 +204,8 @@
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
/*powerMode=*/HWC_POWER_MODE_OFF);
+ mFlinger->mPhaseConfiguration =
+ mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
@@ -214,7 +217,7 @@
mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseOffsets->getCurrentOffsets());
+ mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }