Loading services/surfaceflinger/Scheduler/PhaseOffsets.cpp +32 −25 Original line number Diff line number Diff line Loading @@ -36,6 +36,19 @@ bool fpsEqualsWithMargin(float fpsA, float fpsB) { 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 { Loading @@ -45,14 +58,21 @@ PhaseConfiguration::~PhaseConfiguration() = default; 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(); Loading @@ -67,12 +87,12 @@ void PhaseOffsets::dump(std::string& result) const { } 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; } Loading Loading @@ -243,19 +263,6 @@ PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) }; } 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; Loading services/surfaceflinger/Scheduler/PhaseOffsets.h +5 −2 Original line number Diff line number Diff line Loading @@ -66,9 +66,12 @@ public: // 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; Loading services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp +63 −15 Original line number Diff line number Diff line Loading @@ -42,25 +42,25 @@ public: 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, 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); Loading @@ -76,10 +76,10 @@ TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) { 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); Loading @@ -95,7 +95,7 @@ TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) { 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) { Loading Loading @@ -125,6 +125,54 @@ TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) { 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 Loading Loading
services/surfaceflinger/Scheduler/PhaseOffsets.cpp +32 −25 Original line number Diff line number Diff line Loading @@ -36,6 +36,19 @@ bool fpsEqualsWithMargin(float fpsA, float fpsB) { 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 { Loading @@ -45,14 +58,21 @@ PhaseConfiguration::~PhaseConfiguration() = default; 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(); Loading @@ -67,12 +87,12 @@ void PhaseOffsets::dump(std::string& result) const { } 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; } Loading Loading @@ -243,19 +263,6 @@ PhaseDurations::Offsets PhaseDurations::constructOffsets(nsecs_t vsyncDuration) }; } 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; Loading
services/surfaceflinger/Scheduler/PhaseOffsets.h +5 −2 Original line number Diff line number Diff line Loading @@ -66,9 +66,12 @@ public: // 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; Loading
services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp +63 −15 Original line number Diff line number Diff line Loading @@ -42,25 +42,25 @@ public: 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, 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); Loading @@ -76,10 +76,10 @@ TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) { 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); Loading @@ -95,7 +95,7 @@ TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) { 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) { Loading Loading @@ -125,6 +125,54 @@ TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) { 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 Loading