SurfaceFlinger: add a test for unknown offset
Add a unit test that tries to get phase offset for a
refresh rate that is not part of the boot list.
The scenarios simulates an external display hotplug
with different refresh rate than the internal one.
Bug: 151859083
Test: adb shell /data/nativetest64/libsurfaceflinger_unittest/libsurfaceflinger_unittest
Change-Id: I3adee92b91d0c1a337c9a6ffd7431e7b854bbe0d
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 7941cda..43883fb 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -36,6 +36,19 @@
return std::abs(fpsA - fpsB) <= MARGIN;
}
+std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate->fps);
+ }
+
+ return refreshRates;
+}
+
} // namespace
namespace android::scheduler {
@@ -45,14 +58,21 @@
namespace impl {
PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
- // for the N+2 vsync instead of N+1. This means that:
- // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
- // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
- mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())),
- mOffsets(initializeOffsets(refreshRateConfigs)),
- mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
+ : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().fps,
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync)
+ : mThresholdForNextVsync(thresholdForNextVsync),
+ mOffsets(initializeOffsets(refreshRates)),
+ mRefreshRateFps(currentFps) {}
void PhaseOffsets::dump(std::string& result) const {
const auto [early, earlyGl, late] = getCurrentOffsets();
@@ -67,12 +87,12 @@
}
std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+ const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
- for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
- const float fps = refreshRate->fps;
- offsets.emplace(fps, getPhaseOffsets(fps, refreshRate->vsyncPeriod));
+ for (const auto& refreshRate : refreshRates) {
+ offsets.emplace(refreshRate,
+ getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
}
return offsets;
}
@@ -243,19 +263,6 @@
};
}
-static std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate->fps);
- }
-
- return refreshRates;
-}
-
std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 208f06b..fa8011d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -66,9 +66,12 @@
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
-private:
+protected:
+ // Used for unit tests
+ PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync);
std::unordered_map<float, Offsets> initializeOffsets(
- const scheduler::RefreshRateConfigs&) const;
+ const std::vector<float>& refreshRates) const;
Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index 910e73b..8d49201 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -42,25 +42,25 @@
appEarlyGlDuration) {}
};
-class PhaseOffsetsTest : public testing::Test {
+class PhaseDurationTest : 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) {}
+ PhaseDurationTest()
+ : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
- ~PhaseOffsetsTest() = default;
+ ~PhaseDurationTest() = default;
- TestablePhaseOffsetsAsDurations mPhaseOffsets;
+ TestablePhaseOffsetsAsDurations mPhaseDurations;
};
namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseOffsets.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseDurations.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 6'166'667);
@@ -76,10 +76,10 @@
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);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseDurations.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 611'111);
@@ -95,7 +95,7 @@
EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
}
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
auto validateOffsets = [](auto& offsets) {
@@ -125,6 +125,54 @@
validateOffsets(offsets);
}
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sf, 57'527'208);
+
+ EXPECT_EQ(offsets.late.app, 37'027'208);
+
+ EXPECT_EQ(offsets.early.sf, 52'027'208);
+
+ EXPECT_EQ(offsets.early.app, 18'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.app, 16'527'208);
+}
+
+} // namespace
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets() : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 10'000'000) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets;
+};
+
+namespace {
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+
+ 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);
+}
+
} // namespace
} // namespace scheduler
} // namespace android