diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 471fd44324ffd..3929acde06528 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -765,6 +765,10 @@ FILE: ../../../flutter/shell/common/switches.h FILE: ../../../flutter/shell/common/switches_unittests.cc FILE: ../../../flutter/shell/common/thread_host.cc FILE: ../../../flutter/shell/common/thread_host.h +FILE: ../../../flutter/shell/common/variable_refresh_rate_display.cc +FILE: ../../../flutter/shell/common/variable_refresh_rate_display.h +FILE: ../../../flutter/shell/common/variable_refresh_rate_display_unittests.cc +FILE: ../../../flutter/shell/common/variable_refresh_rate_reporter.h FILE: ../../../flutter/shell/common/vsync_waiter.cc FILE: ../../../flutter/shell/common/vsync_waiter.h FILE: ../../../flutter/shell/common/vsync_waiter_fallback.cc diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 62112b4b9d917..c1f816331339e 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -96,6 +96,9 @@ source_set("common") { "switches.h", "thread_host.cc", "thread_host.h", + "variable_refresh_rate_display.cc", + "variable_refresh_rate_display.h", + "variable_refresh_rate_reporter.h", "vsync_waiter.cc", "vsync_waiter.h", "vsync_waiter_fallback.cc", @@ -266,6 +269,7 @@ if (enable_unittests) { "shell_unittests.cc", "skp_shader_warmup_unittests.cc", "switches_unittests.cc", + "variable_refresh_rate_display_unittests.cc", ] deps = [ diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 32403f1c2e70a..08d216d89ed0b 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -206,6 +206,10 @@ void Animator::Render(std::unique_ptr layer_tree) { std::move(frame_timings_recorder_)); } +const VsyncWaiter& Animator::GetVsyncWaiter() const { + return *waiter_.get(); +} + bool Animator::CanReuseLastLayerTree() { return !regenerate_layer_tree_; } diff --git a/shell/common/animator.h b/shell/common/animator.h index 85f940700dbc6..06dfdb3f297e3 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -54,6 +54,8 @@ class Animator final { void Render(std::unique_ptr layer_tree); + const VsyncWaiter& GetVsyncWaiter() const; + //-------------------------------------------------------------------------- /// @brief Schedule a secondary callback to be executed right after the /// main `VsyncWaiter::AsyncWaitForVsync` callback (which is added diff --git a/shell/common/display.h b/shell/common/display.h index 37e964a217f34..a4cd5aa0863e0 100644 --- a/shell/common/display.h +++ b/shell/common/display.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "flutter/shell/common/variable_refresh_rate_reporter.h" namespace flutter { diff --git a/shell/common/engine.cc b/shell/common/engine.cc index d0e28aa233899..d382e5e94c8a0 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -600,4 +600,8 @@ void Engine::LoadDartDeferredLibraryError(intptr_t loading_unit_id, } } +const VsyncWaiter& Engine::GetVsyncWaiter() const { + return animator_->GetVsyncWaiter(); +} + } // namespace flutter diff --git a/shell/common/engine.h b/shell/common/engine.h index 0c37d176d6edd..b0444abdaa907 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -903,6 +903,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { return runtime_controller_.get(); } + const VsyncWaiter& GetVsyncWaiter() const; + private: // |RuntimeDelegate| std::string DefaultRouteName() override; diff --git a/shell/common/shell.cc b/shell/common/shell.cc index ff891ffbcac17..91b238324a6dc 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -1872,4 +1872,8 @@ Shell::GetPlatformMessageHandler() const { return platform_message_handler_; } +const VsyncWaiter& Shell::GetVsyncWaiter() const { + return engine_->GetVsyncWaiter(); +} + } // namespace flutter diff --git a/shell/common/shell.h b/shell/common/shell.h index 0448da1c424d7..38f343d3b6ddd 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -401,6 +401,8 @@ class Shell final : public PlatformView::Delegate, const std::shared_ptr& GetPlatformMessageHandler() const; + const VsyncWaiter& GetVsyncWaiter() const; + private: using ServiceProtocolHandler = std::function + +#include "display.h" +#include "flutter/fml/macros.h" +#include "variable_refresh_rate_reporter.h" + +namespace flutter { + +/// A Display where the refresh rate can change over time. +class VariableRefreshRateDisplay : public Display { + public: + explicit VariableRefreshRateDisplay( + DisplayId display_id, + const VariableRefreshRateReporter& refresh_rate_reporter); + explicit VariableRefreshRateDisplay( + const VariableRefreshRateReporter& refresh_rate_reporter); + ~VariableRefreshRateDisplay() = default; + + // |Display| + double GetRefreshRate() const override; + + private: + const VariableRefreshRateReporter& refresh_rate_reporter_; + + FML_DISALLOW_COPY_AND_ASSIGN(VariableRefreshRateDisplay); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_COMMON_VARIABLE_REFRESH_RATE_DISPLAY_H_ diff --git a/shell/common/variable_refresh_rate_display_unittests.cc b/shell/common/variable_refresh_rate_display_unittests.cc new file mode 100644 index 0000000000000..25d24afc09d7d --- /dev/null +++ b/shell/common/variable_refresh_rate_display_unittests.cc @@ -0,0 +1,29 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "variable_refresh_rate_display.h" +#include "vsync_waiters_test.h" + +#include "gtest/gtest.h" + +namespace flutter { +namespace testing { + +TEST(VariableRefreshRateDisplayTest, ReportCorrectInitialRefreshRate) { + auto refresh_rate_reporter = std::make_unique(60); + auto display = + flutter::VariableRefreshRateDisplay(*refresh_rate_reporter.get()); + ASSERT_EQ(display.GetRefreshRate(), 60); +} + +TEST(VariableRefreshRateDisplayTest, ReportCorrectRefreshRateWhenUpdated) { + auto refresh_rate_reporter = std::make_unique(60); + auto display = + flutter::VariableRefreshRateDisplay(*refresh_rate_reporter.get()); + refresh_rate_reporter->UpdateRefreshRate(30); + ASSERT_EQ(display.GetRefreshRate(), 30); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/common/variable_refresh_rate_reporter.h b/shell/common/variable_refresh_rate_reporter.h new file mode 100644 index 0000000000000..882eea2279749 --- /dev/null +++ b/shell/common/variable_refresh_rate_reporter.h @@ -0,0 +1,28 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_COMMON_VARIABLE_REFRESH_RATE_REPORTER_H_ +#define FLUTTER_SHELL_COMMON_VARIABLE_REFRESH_RATE_REPORTER_H_ + +#include +#include +#include +#include + +namespace flutter { + +/// Abstract class that reprents a platform specific mechanism to report current +/// refresh rates. +class VariableRefreshRateReporter { + public: + VariableRefreshRateReporter() = default; + + virtual double GetRefreshRate() const = 0; + + FML_DISALLOW_COPY_AND_ASSIGN(VariableRefreshRateReporter); +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_COMMON_VARIABLE_REFRESH_RATE_REPORTER_H_ diff --git a/shell/common/vsync_waiters_test.cc b/shell/common/vsync_waiters_test.cc index 762f9f58c0cac..ff26cc6775270 100644 --- a/shell/common/vsync_waiters_test.cc +++ b/shell/common/vsync_waiters_test.cc @@ -64,5 +64,16 @@ void ConstantFiringVsyncWaiter::AwaitVSync() { }); } +TestRefreshRateReporter::TestRefreshRateReporter(double refresh_rate) + : refresh_rate_(refresh_rate) {} + +void TestRefreshRateReporter::UpdateRefreshRate(double refresh_rate) { + refresh_rate_ = refresh_rate; +} + +double TestRefreshRateReporter::GetRefreshRate() const { + return refresh_rate_; +} + } // namespace testing } // namespace flutter diff --git a/shell/common/vsync_waiters_test.h b/shell/common/vsync_waiters_test.h index 66f474b765b82..80fa3abfb6c24 100644 --- a/shell/common/vsync_waiters_test.h +++ b/shell/common/vsync_waiters_test.h @@ -56,6 +56,18 @@ class ConstantFiringVsyncWaiter : public VsyncWaiter { void AwaitVSync() override; }; +class TestRefreshRateReporter final : public VariableRefreshRateReporter { + public: + explicit TestRefreshRateReporter(double refresh_rate); + void UpdateRefreshRate(double refresh_rate); + + // |RefreshRateReporter| + double GetRefreshRate() const override; + + private: + double refresh_rate_; +}; + } // namespace testing } // namespace flutter diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 0a7d8ddc4b41d..748fae734e681 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -17,6 +17,7 @@ #include "flutter/shell/common/shell.h" #include "flutter/shell/common/switches.h" #include "flutter/shell/common/thread_host.h" +#include "flutter/shell/common/variable_refresh_rate_display.h" #import "flutter/shell/platform/darwin/common/command_line.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterBinaryMessengerRelay.h" #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h" @@ -699,9 +700,10 @@ - (BOOL)createShell:(NSString*)entrypoint } - (void)initializeDisplays { - double refresh_rate = [DisplayLinkManager displayRefreshRate]; + const flutter::VsyncWaiterIOS& vsync_waiter_ios = + static_cast(_shell->GetVsyncWaiter()); std::vector> displays; - displays.push_back(std::make_unique(refresh_rate)); + displays.push_back(std::make_unique(vsync_waiter_ios)); _shell->OnDisplayUpdates(flutter::DisplayUpdateType::kStartup, std::move(displays)); } diff --git a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h index f20bb26ef47d4..f5c3e656b258c 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h @@ -8,6 +8,7 @@ #include "flutter/fml/macros.h" #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/platform/darwin/scoped_nsobject.h" +#include "flutter/shell/common/variable_refresh_rate_reporter.h" #include "flutter/shell/common/vsync_waiter.h" @interface DisplayLinkManager : NSObject @@ -34,16 +35,21 @@ - (void)invalidate; +- (double)getRefreshRate; + @end namespace flutter { -class VsyncWaiterIOS final : public VsyncWaiter { +class VsyncWaiterIOS final : public VsyncWaiter, public VariableRefreshRateReporter { public: explicit VsyncWaiterIOS(flutter::TaskRunners task_runners); ~VsyncWaiterIOS() override; + // |VariableRefreshRateReporter| + double GetRefreshRate() const override; + private: fml::scoped_nsobject client_; diff --git a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm index 7877f438b2eea..91cabc13dd985 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm @@ -39,11 +39,17 @@ [client_.get() await]; } +// |VariableRefreshRateReporter| +double VsyncWaiterIOS::GetRefreshRate() const { + return [client_.get() getRefreshRate]; +} + } // namespace flutter @implementation VSyncClient { flutter::VsyncWaiter::Callback callback_; fml::scoped_nsobject display_link_; + double current_refresh_rate_; } - (instancetype)initWithTaskRunner:(fml::RefPtr)task_runner @@ -51,6 +57,7 @@ - (instancetype)initWithTaskRunner:(fml::RefPtr)task_runner self = [super init]; if (self) { + current_refresh_rate_ = [DisplayLinkManager displayRefreshRate]; callback_ = std::move(callback); display_link_ = fml::scoped_nsobject { [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain] @@ -87,6 +94,9 @@ - (void)onDisplayLink:(CADisplayLink*)link { std::unique_ptr recorder = std::make_unique(); + + current_refresh_rate_ = round(1 / (frame_target_time - frame_start_time).ToSecondsF()); + recorder->RecordVsync(frame_start_time, frame_target_time); display_link_.get().paused = YES; @@ -103,6 +113,10 @@ - (void)dealloc { [super dealloc]; } +- (double)getRefreshRate { + return current_refresh_rate_; +} + @end @implementation DisplayLinkManager