diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 901669237596c..6a5581b6b7f4e 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -699,6 +699,7 @@ FILE: ../../../flutter/shell/common/display.cc FILE: ../../../flutter/shell/common/display.h FILE: ../../../flutter/shell/common/display_manager.cc FILE: ../../../flutter/shell/common/display_manager.h +FILE: ../../../flutter/shell/common/display_manager_unittests.cc FILE: ../../../flutter/shell/common/engine.cc FILE: ../../../flutter/shell/common/engine.h FILE: ../../../flutter/shell/common/engine_unittests.cc diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 62112b4b9d917..269d08f7fa251 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -258,6 +258,7 @@ if (enable_unittests) { sources = [ "animator_unittests.cc", "canvas_spy_unittests.cc", + "display_manager_unittests.cc", "engine_unittests.cc", "input_events_unittests.cc", "persistent_cache_unittests.cc", diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 32403f1c2e70a..a63ad5cf8455a 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -133,9 +133,10 @@ void Animator::BeginFrame( // We have acquired a valid continuation from the pipeline and are ready // to service potential frame. FML_DCHECK(producer_continuation_); + const fml::TimePoint vsync_start_time = + frame_timings_recorder_->GetVsyncStartTime(); fml::tracing::TraceEventAsyncComplete( - "flutter", "VsyncSchedulingOverhead", - frame_timings_recorder_->GetVsyncStartTime(), + "flutter", "VsyncSchedulingOverhead", vsync_start_time, frame_timings_recorder_->GetBuildStartTime()); const fml::TimePoint frame_target_time = frame_timings_recorder_->GetVsyncTargetTime(); @@ -144,7 +145,8 @@ void Animator::BeginFrame( TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame", FrameParity()); uint64_t frame_number = frame_timings_recorder_->GetFrameNumber(); - delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number); + delegate_.OnAnimatorBeginFrame(vsync_start_time, frame_target_time, + frame_number); } if (!frame_scheduled_ && has_rendered_) { diff --git a/shell/common/animator.h b/shell/common/animator.h index 85f940700dbc6..e7c51d43a4a42 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -31,7 +31,8 @@ class Animator final { public: class Delegate { public: - virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, + virtual void OnAnimatorBeginFrame(fml::TimePoint vsync_start_time, + fml::TimePoint frame_target_time, uint64_t frame_number) = 0; virtual void OnAnimatorNotifyIdle(int64_t deadline) = 0; diff --git a/shell/common/animator_unittests.cc b/shell/common/animator_unittests.cc index 0b11d06c8929f..5b5c34f5e2242 100644 --- a/shell/common/animator_unittests.cc +++ b/shell/common/animator_unittests.cc @@ -20,7 +20,8 @@ namespace testing { class FakeAnimatorDelegate : public Animator::Delegate { public: - void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, + void OnAnimatorBeginFrame(fml::TimePoint vsync_start_time, + fml::TimePoint frame_target_time, uint64_t frame_number) override {} void OnAnimatorNotifyIdle(int64_t deadline) override { diff --git a/shell/common/display_manager.cc b/shell/common/display_manager.cc index 75211308de727..8d90f97b12214 100644 --- a/shell/common/display_manager.cc +++ b/shell/common/display_manager.cc @@ -32,6 +32,11 @@ void DisplayManager::HandleDisplayUpdates( FML_CHECK(displays_.empty()); displays_ = std::move(displays); return; + case DisplayUpdateType::kUpdateRefreshRate: + displays_.insert(displays_.begin(), + std::make_move_iterator(displays.begin()), + std::make_move_iterator(displays.end())); + break; default: FML_CHECK(false) << "Unknown DisplayUpdateType."; } @@ -47,4 +52,24 @@ void DisplayManager::CheckDisplayConfiguration( } } +void DisplayManager::UpdateRefreshRateIfNecessary( + DisplayUpdateType update_type, + fml::TimePoint vsync_start_time, + fml::TimePoint frame_target_time) { + auto is_frame_rate_same = [](double fr1, double fr2) { + const double kRefreshRateCompareEpsilon = 1; + return fabs(fr1 - fr2) < kRefreshRateCompareEpsilon; + }; + + double new_frame_rate = + round((1 / (frame_target_time - vsync_start_time).ToSecondsF())); + if (is_frame_rate_same(GetMainDisplayRefreshRate(), new_frame_rate)) { + return; + } + + std::vector> displays; + displays.push_back(std::make_unique(new_frame_rate)); + HandleDisplayUpdates(update_type, std::move(displays)); +} + } // namespace flutter diff --git a/shell/common/display_manager.h b/shell/common/display_manager.h index 774373d54e70b..6ffea962fcdc6 100644 --- a/shell/common/display_manager.h +++ b/shell/common/display_manager.h @@ -5,9 +5,11 @@ #ifndef FLUTTER_SHELL_COMMON_DISPLAY_MANAGER_H_ #define FLUTTER_SHELL_COMMON_DISPLAY_MANAGER_H_ +#include #include #include +#include "flutter/fml/time/time_point.h" #include "flutter/shell/common/display.h" namespace flutter { @@ -20,7 +22,11 @@ enum class DisplayUpdateType { /// 1. The frame buffer hardware is connected. /// 2. The display is drawable, e.g. it isn't being mirrored from another /// connected display or sleeping. - kStartup + kStartup, + + /// The `flutter::Display` that were active and a new frame indicates that the + /// refresh rate might be updated. + kUpdateRefreshRate, }; /// Manages lifecycle of the connected displays. This class is thread-safe. @@ -42,6 +48,14 @@ class DisplayManager { void HandleDisplayUpdates(DisplayUpdateType update_type, std::vector> displays); + /// Updates the frame rate indicated in the |DisplayManager| if necessary. + /// + /// If the |DisplayManager| decides an update is required, + /// |GetMainDisplayRefreshRate| will return the updated value. + void UpdateRefreshRateIfNecessary(DisplayUpdateType update_type, + fml::TimePoint vsync_start_time, + fml::TimePoint frame_target_time); + private: /// Guards `displays_` vector. mutable std::mutex displays_mutex_; diff --git a/shell/common/display_manager_unittests.cc b/shell/common/display_manager_unittests.cc new file mode 100644 index 0000000000000..7c9502851f24c --- /dev/null +++ b/shell/common/display_manager_unittests.cc @@ -0,0 +1,60 @@ +#include "display.h" +#include "display_manager.h" + +#include "flutter/fml/time/time_point.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(DisplayManagerTest, SetsDisplayOnStartup) { + DisplayManager manager; + const double refresh_rate = 60; + std::vector> displays; + displays.push_back(std::make_unique(refresh_rate)); + manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kStartup, + std::move(displays)); + ASSERT_EQ(manager.GetMainDisplayRefreshRate(), refresh_rate); +} + +TEST(DisplayManagerTest, UpdatesDisplayIfTypeIsUpdateRefreshRate) { + DisplayManager manager; + const double refresh_rate = 60; + std::vector> displays1; + displays1.push_back(std::make_unique(refresh_rate)); + manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kStartup, + std::move(displays1)); + + const double new_refresh_rate = 120; + std::vector> displays2; + displays2.push_back(std::make_unique(new_refresh_rate)); + manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kUpdateRefreshRate, + std::move(displays2)); + ASSERT_EQ(manager.GetMainDisplayRefreshRate(), new_refresh_rate); +} + +TEST(DisplayManagerTest, UpdatesDisplayIfNewlyCalculatedRefreshRateChanged) { + DisplayManager manager; + const double refresh_rate = 60; + std::vector> displays; + displays.push_back(std::make_unique(refresh_rate)); + manager.HandleDisplayUpdates(flutter::DisplayUpdateType::kStartup, + std::move(displays)); + fml::TimePoint vsync_start = + fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMilliseconds(0)); + fml::TimePoint vsync_target = + fml::TimePoint::FromEpochDelta(fml::TimeDelta::FromMilliseconds(8)); + + manager.UpdateRefreshRateIfNecessary( + flutter::DisplayUpdateType::kUpdateRefreshRate, vsync_start, + vsync_target); + + const double kRefreshRateCompareEpsilon = 0.1; + ASSERT_NEAR(manager.GetMainDisplayRefreshRate(), + round(1 / (vsync_target - vsync_start).ToSecondsF()), + kRefreshRateCompareEpsilon); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/common/shell.cc b/shell/common/shell.cc index ff891ffbcac17..64570e76d74ed 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1068,11 +1068,16 @@ void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) { } // |Animator::Delegate| -void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time, +void Shell::OnAnimatorBeginFrame(fml::TimePoint vsync_start_time, + fml::TimePoint frame_target_time, uint64_t frame_number) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); + display_manager_->UpdateRefreshRateIfNecessary( + flutter::DisplayUpdateType::kUpdateRefreshRate, vsync_start_time, + frame_target_time); + // record the target time for use by rasterizer. { std::scoped_lock time_recorder_lock(time_recorder_mutex_); diff --git a/shell/common/shell.h b/shell/common/shell.h index 0448da1c424d7..2101bc08c1b7c 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -567,7 +567,8 @@ class Shell final : public PlatformView::Delegate, AssetResolver::AssetResolverType type) override; // |Animator::Delegate| - void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, + void OnAnimatorBeginFrame(fml::TimePoint vsync_start_time, + fml::TimePoint frame_target_time, uint64_t frame_number) override; // |Animator::Delegate|