diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4b32abab211b7..9e05118e041a0 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -599,9 +599,6 @@ FILE: ../../../flutter/shell/common/animator_unittests.cc FILE: ../../../flutter/shell/common/canvas_spy.cc FILE: ../../../flutter/shell/common/canvas_spy.h FILE: ../../../flutter/shell/common/canvas_spy_unittests.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/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 ca69f2f353185..a3093d4f44c58 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -64,9 +64,6 @@ source_set("common") { "animator.h", "canvas_spy.cc", "canvas_spy.h", - "display.h", - "display_manager.cc", - "display_manager.h", "engine.cc", "engine.h", "isolate_configuration.cc", diff --git a/shell/common/animator.cc b/shell/common/animator.cc index 8cc8926ca4073..cae6df1666a35 100644 --- a/shell/common/animator.cc +++ b/shell/common/animator.cc @@ -53,6 +53,10 @@ Animator::Animator(Delegate& delegate, Animator::~Animator() = default; +float Animator::GetDisplayRefreshRate() const { + return waiter_->GetDisplayRefreshRate(); +} + void Animator::Stop() { paused_ = true; } diff --git a/shell/common/animator.h b/shell/common/animator.h index effc01111001d..a6508fe24fa1a 100644 --- a/shell/common/animator.h +++ b/shell/common/animator.h @@ -47,6 +47,8 @@ class Animator final { ~Animator(); + float GetDisplayRefreshRate() const; + void RequestFrame(bool regenerate_layer_tree = true); void Render(std::unique_ptr layer_tree); diff --git a/shell/common/display.h b/shell/common/display.h deleted file mode 100644 index 2c7e0fa88ff73..0000000000000 --- a/shell/common/display.h +++ /dev/null @@ -1,55 +0,0 @@ -// 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_DISPLAY_H_ -#define FLUTTER_SHELL_COMMON_DISPLAY_H_ - -#include - -namespace flutter { - -/// Unique ID per display that is stable until the Flutter application restarts. -/// See also: `flutter::Display` -typedef uint64_t DisplayId; - -/// To be used when the display refresh rate is unknown. -static constexpr double kUnknownDisplayRefreshRate = 0; - -/// Display refers to a graphics hardware system consisting of a framebuffer, -/// typically a monitor or a screen. This class holds the various display -/// settings. -class Display { - public: - //------------------------------------------------------------------------------ - /// @brief Construct a new Display object in case where the display id of the - /// display is known. In cases where there is more than one display, every - /// display is expected to have a display id. - /// - Display(DisplayId display_id, double refresh_rate) - : display_id_(display_id), refresh_rate_(refresh_rate) {} - - //------------------------------------------------------------------------------ - /// @brief Construct a new Display object when there is only a single display. - /// When there are multiple displays, every display must have a display id. - /// - explicit Display(double refresh_rate) - : display_id_({}), refresh_rate_(refresh_rate) {} - - ~Display() = default; - - // Get the display's maximum refresh rate in the unit of frame per second. - // Return `kUnknownDisplayRefreshRate` if the refresh rate is unknown. - double GetRefreshRate() const { return refresh_rate_; } - - /// Returns the `DisplayId` of the display. - std::optional GetDisplayId() const { return display_id_; } - - private: - std::optional display_id_; - double refresh_rate_; -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_COMMON_DISPLAY_H_ diff --git a/shell/common/display_manager.cc b/shell/common/display_manager.cc deleted file mode 100644 index a30c80b1a31e8..0000000000000 --- a/shell/common/display_manager.cc +++ /dev/null @@ -1,49 +0,0 @@ -// 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 "flutter/shell/common/display_manager.h" - -#include "flutter/fml/logging.h" -#include "flutter/fml/macros.h" - -namespace flutter { - -DisplayManager::DisplayManager() = default; - -DisplayManager::~DisplayManager() = default; - -double DisplayManager::GetMainDisplayRefreshRate() const { - std::scoped_lock lock(displays_mutex_); - if (displays_.empty()) { - return kUnknownDisplayRefreshRate; - } else { - return displays_[0].GetRefreshRate(); - } -} - -void DisplayManager::HandleDisplayUpdates(DisplayUpdateType update_type, - std::vector displays) { - std::scoped_lock lock(displays_mutex_); - CheckDisplayConfiguration(displays); - switch (update_type) { - case DisplayUpdateType::kStartup: - FML_CHECK(displays_.empty()); - displays_ = displays; - return; - default: - FML_CHECK(false) << "Unknown DisplayUpdateType."; - } -} - -void DisplayManager::CheckDisplayConfiguration( - std::vector displays) const { - FML_CHECK(!displays.empty()); - if (displays.size() > 1) { - for (auto& display : displays) { - FML_CHECK(display.GetDisplayId().has_value()); - } - } -} - -} // namespace flutter diff --git a/shell/common/display_manager.h b/shell/common/display_manager.h deleted file mode 100644 index aa4bbadbb8618..0000000000000 --- a/shell/common/display_manager.h +++ /dev/null @@ -1,59 +0,0 @@ -// 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_DISPLAY_MANAGER_H_ -#define FLUTTER_SHELL_COMMON_DISPLAY_MANAGER_H_ - -#include -#include - -#include "flutter/shell/common/display.h" - -namespace flutter { - -/// The update type parameter that is passed to -/// `DisplayManager::HandleDisplayUpdates`. -enum class DisplayUpdateType { - /// `flutter::Display`s that were active during start-up. A display is - /// considered active if: - /// 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 -}; - -/// Manages lifecycle of the connected displays. This class is thread-safe. -class DisplayManager { - public: - DisplayManager(); - - ~DisplayManager(); - - /// Returns the display refresh rate of the main display. In cases where there - /// is only one display connected, it will return that. We do not yet support - /// cases where there are multiple displays. - /// - /// When there are no registered displays, it returns - /// `kUnknownDisplayRefreshRate`. - double GetMainDisplayRefreshRate() const; - - /// Handles the display updates. - void HandleDisplayUpdates(DisplayUpdateType update_type, - std::vector displays); - - private: - /// Guards `displays_` vector. - mutable std::mutex displays_mutex_; - std::vector displays_; - - /// Checks that the provided display configuration is valid. Currently this - /// ensures that all the displays have an id in the case there are multiple - /// displays. In case where there is a single display, it is valid for the - /// display to not have an id. - void CheckDisplayConfiguration(std::vector displays) const; -}; - -} // namespace flutter - -#endif // FLUTTER_SHELL_COMMON_DISPLAY_MANAGER_H_ diff --git a/shell/common/engine.cc b/shell/common/engine.cc index d2384c03579cf..f7171b2fe6c1f 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -97,6 +97,10 @@ Engine::Engine(Delegate& delegate, Engine::~Engine() = default; +float Engine::GetDisplayRefreshRate() const { + return animator_->GetDisplayRefreshRate(); +} + fml::WeakPtr Engine::GetWeakPtr() const { return weak_factory_.GetWeakPtr(); } diff --git a/shell/common/engine.h b/shell/common/engine.h index b8d98aa766291..e4de94846e62f 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -24,7 +24,6 @@ #include "flutter/runtime/runtime_controller.h" #include "flutter/runtime/runtime_delegate.h" #include "flutter/shell/common/animator.h" -#include "flutter/shell/common/display_manager.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/pointer_data_dispatcher.h" #include "flutter/shell/common/rasterizer.h" @@ -329,6 +328,30 @@ class Engine final : public RuntimeDelegate, /// ~Engine() override; + //---------------------------------------------------------------------------- + /// @brief Gets the refresh rate in frames per second of the vsync waiter + /// used by the animator managed by this engine. This information + /// is purely advisory and is not used by any component. It is + /// only used by the tooling to visualize frame performance. + /// + /// @attention The display refresh rate is useless for frame scheduling + /// because it can vary and more accurate frame specific + /// information is given to the engine by the vsync waiter + /// already. However, this call is used by the tooling to ask very + /// high level questions about display refresh rate. For example, + /// "Is the display 60 or 120Hz?". This information is quite + /// unreliable (not available immediately on launch on some + /// platforms), variable and advisory. It must not be used by any + /// component that claims to use it to perform accurate frame + /// scheduling. + /// + /// @return The display refresh rate in frames per second. This may change + /// from frame to frame, throughout the lifecycle of the + /// application, and, may not be available immediately upon + /// application launch. + /// + float GetDisplayRefreshRate() const; + //---------------------------------------------------------------------------- /// @return The pointer to this instance of the engine. The engine may /// only be accessed safely on the UI task runner. diff --git a/shell/common/rasterizer.h b/shell/common/rasterizer.h index b658f18e0674c..faf0f5ee9b895 100644 --- a/shell/common/rasterizer.h +++ b/shell/common/rasterizer.h @@ -68,9 +68,7 @@ class Rasterizer final : public SnapshotDelegate { /// virtual void OnFrameRasterized(const FrameTiming& frame_timing) = 0; - /// Time limit for a smooth frame. - /// - /// See: `DisplayManager::GetMainDisplayRefreshRate`. + /// Time limit for a smooth frame. See `Engine::GetDisplayRefreshRate`. virtual fml::Milliseconds GetFrameBudget() = 0; /// Target time for the latest frame. See also `Shell::OnAnimatorBeginFrame` diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 1503519ca7f4d..7b4c641b4e76f 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -333,8 +333,6 @@ Shell::Shell(DartVMRef vm, TaskRunners task_runners, Settings settings) FML_DCHECK(task_runners_.IsValid()); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); - display_manager_ = std::make_unique(); - // Generate a WeakPtrFactory for use with the raster thread. This does not // need to wait on a latch because it can only ever be used from the raster // thread from this class, so we have ordering guarantees. @@ -576,6 +574,14 @@ bool Shell::Setup(std::unique_ptr platform_view, PersistentCache::GetCacheForProcess()->Purge(); } + // TODO(gw280): The WeakPtr here asserts that we are derefing it on the + // same thread as it was created on. Shell is constructed on the platform + // thread but we need to call into the Engine on the UI thread, so we need + // to use getUnsafe() here to avoid failing the assertion. + // + // https://github.com/flutter/flutter/issues/42947 + display_refresh_rate_ = weak_engine_.getUnsafe()->GetDisplayRefreshRate(); + return true; } @@ -1254,9 +1260,8 @@ void Shell::OnFrameRasterized(const FrameTiming& timing) { } fml::Milliseconds Shell::GetFrameBudget() { - double display_refresh_rate = display_manager_->GetMainDisplayRefreshRate(); - if (display_refresh_rate > 0) { - return fml::RefreshRateToFrameBudget(display_refresh_rate); + if (display_refresh_rate_ > 0) { + return fml::RefreshRateToFrameBudget(display_refresh_rate_.load()); } else { return fml::kDefaultFrameBudget; } @@ -1447,15 +1452,11 @@ bool Shell::OnServiceProtocolGetDisplayRefreshRate( FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); response->SetObject(); response->AddMember("type", "DisplayRefreshRate", response->GetAllocator()); - response->AddMember("fps", display_manager_->GetMainDisplayRefreshRate(), + response->AddMember("fps", engine_->GetDisplayRefreshRate(), response->GetAllocator()); return true; } -double Shell::GetMainDisplayRefreshRate() { - return display_manager_->GetMainDisplayRefreshRate(); -} - bool Shell::OnServiceProtocolGetSkSLs( const ServiceProtocol::Handler::ServiceProtocolMap& params, rapidjson::Document* response) { @@ -1617,9 +1618,4 @@ std::shared_ptr Shell::GetIsGpuDisabledSyncSwitch() const { return is_gpu_disabled_sync_switch_; } -void Shell::OnDisplayUpdates(DisplayUpdateType update_type, - std::vector displays) { - display_manager_->HandleDisplayUpdates(update_type, displays); -} - } // namespace flutter diff --git a/shell/common/shell.h b/shell/common/shell.h index 74ad1c280e3ae..368e55116778b 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -30,7 +30,6 @@ #include "flutter/runtime/dart_vm_lifecycle.h" #include "flutter/runtime/service_protocol.h" #include "flutter/shell/common/animator.h" -#include "flutter/shell/common/display_manager.h" #include "flutter/shell/common/engine.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/rasterizer.h" @@ -370,17 +369,6 @@ class Shell final : public PlatformView::Delegate, /// DartVM* GetDartVM(); - //---------------------------------------------------------------------------- - /// @brief Notifies the display manager of the updates. - /// - void OnDisplayUpdates(DisplayUpdateType update_type, - std::vector displays); - - //---------------------------------------------------------------------------- - /// @brief Queries the `DisplayManager` for the main display refresh rate. - /// - double GetMainDisplayRefreshRate(); - private: using ServiceProtocolHandler = std::function unreported_timings_; - /// Manages the displays. This class is thread safe, can be accessed from any - /// of the threads. - std::unique_ptr display_manager_; + // A cache of `Engine::GetDisplayRefreshRate` (only callable in the UI thread) + // so we can access it from `Rasterizer` (in the raster thread). + // + // The atomic is for extra thread safety as this is written in the UI thread + // and read from the raster thread. + std::atomic display_refresh_rate_ = 0.0f; // protects expected_frame_size_ which is set on platform thread and read on // raster thread diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 1bd8fdfaae658..7947d42cf6125 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -134,4 +134,8 @@ void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time, } } +float VsyncWaiter::GetDisplayRefreshRate() const { + return kUnknownRefreshRateFPS; +} + } // namespace flutter diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index b248449268c28..ab2d857415652 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h @@ -30,6 +30,12 @@ class VsyncWaiter : public std::enable_shared_from_this { /// See also |PointerDataDispatcher::ScheduleSecondaryVsyncCallback|. void ScheduleSecondaryCallback(const fml::closure& callback); + static constexpr float kUnknownRefreshRateFPS = 0.0; + + // Get the display's maximum refresh rate in the unit of frame per second. + // Return kUnknownRefreshRateFPS if the refresh rate is unknown. + virtual float GetDisplayRefreshRate() const; + protected: // On some backends, the |FireCallback| needs to be made from a static C // method. diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index cc81849549f05..8e1448c13d89c 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -75,8 +75,6 @@ AndroidShellHolder::AndroidShellHolder( ); } weak_platform_view = platform_view_android->GetWeakPtr(); - shell.OnDisplayUpdates(DisplayUpdateType::kStartup, - {Display(jni_facade->GetDisplayRefreshRate())}); return platform_view_android; }; diff --git a/shell/platform/android/jni/jni_mock.h b/shell/platform/android/jni/jni_mock.h index 65f790d972ee8..f4310f6beeefc 100644 --- a/shell/platform/android/jni/jni_mock.h +++ b/shell/platform/android/jni/jni_mock.h @@ -93,8 +93,6 @@ class JNIMock final : public PlatformViewAndroidJNI { FlutterViewComputePlatformResolvedLocale, (std::vector supported_locales_data), (override)); - - MOCK_METHOD(double, GetDisplayRefreshRate, (), (override)); }; } // namespace flutter diff --git a/shell/platform/android/jni/platform_view_android_jni.h b/shell/platform/android/jni/platform_view_android_jni.h index e06079c1f5c4f..7b58439bc5e36 100644 --- a/shell/platform/android/jni/platform_view_android_jni.h +++ b/shell/platform/android/jni/platform_view_android_jni.h @@ -193,8 +193,6 @@ class PlatformViewAndroidJNI { virtual std::unique_ptr> FlutterViewComputePlatformResolvedLocale( std::vector supported_locales_data) = 0; - - virtual double GetDisplayRefreshRate() = 0; }; } // namespace flutter diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index 3b8a61bf39c0c..e641e8bf919c1 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -1315,21 +1315,4 @@ PlatformViewAndroidJNIImpl::FlutterViewComputePlatformResolvedLocale( return out; } -double PlatformViewAndroidJNIImpl::GetDisplayRefreshRate() { - JNIEnv* env = fml::jni::AttachCurrentThread(); - - auto java_object = java_object_.get(env); - if (java_object.is_null()) { - return kUnknownDisplayRefreshRate; - } - - jclass clazz = env->GetObjectClass(java_object.obj()); - if (clazz == nullptr) { - return kUnknownDisplayRefreshRate; - } - - jfieldID fid = env->GetStaticFieldID(clazz, "refreshRateFPS", "F"); - return static_cast(env->GetStaticFloatField(clazz, fid)); -} - } // namespace flutter diff --git a/shell/platform/android/platform_view_android_jni_impl.h b/shell/platform/android/platform_view_android_jni_impl.h index 313a9ee921e6c..e26c891960488 100644 --- a/shell/platform/android/platform_view_android_jni_impl.h +++ b/shell/platform/android/platform_view_android_jni_impl.h @@ -78,8 +78,6 @@ class PlatformViewAndroidJNIImpl final : public PlatformViewAndroidJNI { FlutterViewComputePlatformResolvedLocale( std::vector supported_locales_data) override; - double GetDisplayRefreshRate() override; - private: // Reference to FlutterJNI object. const fml::jni::JavaObjectWeakGlobalRef java_object_; diff --git a/shell/platform/android/vsync_waiter_android.cc b/shell/platform/android/vsync_waiter_android.cc index 2851eec5f58e5..b38fd64548fba 100644 --- a/shell/platform/android/vsync_waiter_android.cc +++ b/shell/platform/android/vsync_waiter_android.cc @@ -38,6 +38,19 @@ void VsyncWaiterAndroid::AwaitVSync() { }); } +float VsyncWaiterAndroid::GetDisplayRefreshRate() const { + JNIEnv* env = fml::jni::AttachCurrentThread(); + if (g_vsync_waiter_class == nullptr) { + return kUnknownRefreshRateFPS; + } + jclass clazz = g_vsync_waiter_class->obj(); + if (clazz == nullptr) { + return kUnknownRefreshRateFPS; + } + jfieldID fid = env->GetStaticFieldID(clazz, "refreshRateFPS", "F"); + return env->GetStaticFloatField(clazz, fid); +} + // static void VsyncWaiterAndroid::OnNativeVsync(JNIEnv* env, jclass jcaller, diff --git a/shell/platform/android/vsync_waiter_android.h b/shell/platform/android/vsync_waiter_android.h index 96fcc8c0e16ec..be086581bd506 100644 --- a/shell/platform/android/vsync_waiter_android.h +++ b/shell/platform/android/vsync_waiter_android.h @@ -22,6 +22,8 @@ class VsyncWaiterAndroid final : public VsyncWaiter { ~VsyncWaiterAndroid() override; + float GetDisplayRefreshRate() const override; + private: // |VsyncWaiter| void AwaitVSync() override; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 347b853270e89..61966fd85be37 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -27,7 +27,6 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/connection_collection.h" #import "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" #import "flutter/shell/platform/darwin/ios/framework/Source/profiler_metrics_ios.h" -#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h" #import "flutter/shell/platform/darwin/ios/ios_surface.h" #import "flutter/shell/platform/darwin/ios/platform_view_ios.h" #import "flutter/shell/platform/darwin/ios/rendering_api_selection.h" @@ -540,7 +539,6 @@ - (BOOL)createShell:(NSString*)entrypoint } else { [self setupChannels]; [self onLocaleUpdated:nil]; - [self initializeDisplays]; if (!_platformViewsController) { _platformViewsController.reset(new flutter::FlutterPlatformViewsController()); } @@ -555,12 +553,6 @@ - (BOOL)createShell:(NSString*)entrypoint return _shell != nullptr; } -- (void)initializeDisplays { - double refresh_rate = [[[DisplayLinkManager alloc] init] displayRefreshRate]; - auto display = flutter::Display(refresh_rate); - _shell->OnDisplayUpdates(flutter::DisplayUpdateType::kStartup, {display}); -} - - (BOOL)run { return [self runWithEntrypoint:FlutterDefaultDartEntrypoint libraryURI:nil 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 cd34590e11db0..7cd01d2dfad5f 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h @@ -10,33 +10,7 @@ #include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/shell/common/vsync_waiter.h" -@interface DisplayLinkManager : NSObject - -- (instancetype)init; - -//------------------------------------------------------------------------------ -/// @brief The display refresh rate used for reporting purposes. The engine does not care -/// about this for frame scheduling. It is only used by tools for instrumentation. The -/// engine uses the duration field of the link per frame for frame scheduling. -/// -/// @attention Do not use the this call in frame scheduling. It is only meant for reporting. -/// -/// @return The refresh rate in frames per second. -/// -- (double)displayRefreshRate; - -@end - -@interface VSyncClient : NSObject - -- (instancetype)initWithTaskRunner:(fml::RefPtr)task_runner - callback:(flutter::VsyncWaiter::Callback)callback; - -- (void)await; - -- (void)invalidate; - -@end +@class VSyncClient; namespace flutter { @@ -52,6 +26,9 @@ class VsyncWaiterIOS final : public VsyncWaiter { // |VsyncWaiter| void AwaitVSync() override; + // |VsyncWaiter| + float GetDisplayRefreshRate() const override; + FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiterIOS); }; 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 e94fbd6063219..8d5d8138d3fdb 100644 --- a/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm +++ b/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm @@ -15,6 +15,28 @@ #include "flutter/fml/logging.h" #include "flutter/fml/trace_event.h" +@interface VSyncClient : NSObject + +- (instancetype)initWithTaskRunner:(fml::RefPtr)task_runner + callback:(flutter::VsyncWaiter::Callback)callback; + +- (void)await; + +- (void)invalidate; + +//------------------------------------------------------------------------------ +/// @brief The display refresh rate used for reporting purposes. The engine does not care +/// about this for frame scheduling. It is only used by tools for instrumentation. The +/// engine uses the duration field of the link per frame for frame scheduling. +/// +/// @attention Do not use the this call in frame scheduling. It is only meant for reporting. +/// +/// @return The refresh rate in frames per second. +/// +- (float)displayRefreshRate; + +@end + namespace flutter { VsyncWaiterIOS::VsyncWaiterIOS(flutter::TaskRunners task_runners) @@ -35,11 +57,16 @@ [client_.get() await]; } +// |VsyncWaiter| +float VsyncWaiterIOS::GetDisplayRefreshRate() const { + return [client_.get() displayRefreshRate]; +} + } // namespace flutter @implementation VSyncClient { flutter::VsyncWaiter::Callback callback_; - CADisplayLink* display_link_; + fml::scoped_nsobject display_link_; } - (instancetype)initWithTaskRunner:(fml::RefPtr)task_runner @@ -48,11 +75,14 @@ - (instancetype)initWithTaskRunner:(fml::RefPtr)task_runner if (self) { callback_ = std::move(callback); - display_link_ = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)]; - display_link_.paused = YES; + display_link_ = fml::scoped_nsobject { + [[CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)] retain] + }; + display_link_.get().paused = YES; task_runner->PostTask([client = [self retain]]() { - [client->display_link_ addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [client->display_link_.get() addToRunLoop:[NSRunLoop currentRunLoop] + forMode:NSRunLoopCommonModes]; [client release]; }); } @@ -60,52 +90,9 @@ - (instancetype)initWithTaskRunner:(fml::RefPtr)task_runner return self; } -- (void)await { - display_link_.paused = NO; -} - -- (void)onDisplayLink:(CADisplayLink*)link { - TRACE_EVENT0("flutter", "VSYNC"); - - CFTimeInterval delay = CACurrentMediaTime() - link.timestamp; - fml::TimePoint frame_start_time = fml::TimePoint::Now() - fml::TimeDelta::FromSecondsF(delay); - fml::TimePoint frame_target_time = frame_start_time + fml::TimeDelta::FromSecondsF(link.duration); - - display_link_.paused = YES; - - callback_(frame_start_time, frame_target_time); -} - -- (void)invalidate { - [display_link_ invalidate]; -} - -- (void)dealloc { - [self invalidate]; - - [super dealloc]; -} - -@end - -@implementation DisplayLinkManager { - CADisplayLink* display_link_; -} - -- (instancetype)init { - self = [super init]; - - if (self) { - display_link_ = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)]; - display_link_.paused = YES; - } - - return self; -} - -- (double)displayRefreshRate { +- (float)displayRefreshRate { if (@available(iOS 10.3, *)) { - auto preferredFPS = display_link_.preferredFramesPerSecond; // iOS 10.0 + auto preferredFPS = display_link_.get().preferredFramesPerSecond; // iOS 10.0 // From Docs: // The default value for preferredFramesPerSecond is 0. When this value is 0, the preferred @@ -122,12 +109,29 @@ - (double)displayRefreshRate { } } +- (void)await { + display_link_.get().paused = NO; +} + - (void)onDisplayLink:(CADisplayLink*)link { - // no-op. + TRACE_EVENT0("flutter", "VSYNC"); + + CFTimeInterval delay = CACurrentMediaTime() - link.timestamp; + fml::TimePoint frame_start_time = fml::TimePoint::Now() - fml::TimeDelta::FromSecondsF(delay); + fml::TimePoint frame_target_time = frame_start_time + fml::TimeDelta::FromSecondsF(link.duration); + + display_link_.get().paused = YES; + + callback_(frame_start_time, frame_target_time); +} + +- (void)invalidate { + // [CADisplayLink invalidate] is thread-safe. + [display_link_.get() invalidate]; } - (void)dealloc { - [display_link_ invalidate]; + [self invalidate]; [super dealloc]; } diff --git a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm index 6210378110db0..36b24f3424ddc 100644 --- a/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/macos/framework/Source/FlutterEngine.mm @@ -281,7 +281,6 @@ - (BOOL)runWithEntrypoint:(NSString*)entrypoint { [self sendUserLocales]; [self updateWindowMetrics]; - [self updateDisplayConfig]; return YES; } @@ -318,31 +317,6 @@ - (NSOpenGLContext*)resourceContext { return _resourceContext; } -- (void)updateDisplayConfig { - if (!_engine) { - return; - } - - CVDisplayLinkRef displayLinkRef; - CGDirectDisplayID mainDisplayID = CGMainDisplayID(); - CVDisplayLinkCreateWithCGDisplay(mainDisplayID, &displayLinkRef); - CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef); - if (!(nominal.flags & kCVTimeIsIndefinite)) { - double refreshRate = static_cast(nominal.timeScale) / nominal.timeValue; - - FlutterEngineDisplay display; - display.struct_size = sizeof(display); - display.display_id = mainDisplayID; - display.refresh_rate = round(refreshRate); - - std::vector displays = {display}; - FlutterEngineNotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup, - displays.data(), displays.size()); - } - - CVDisplayLinkRelease(displayLinkRef); -} - - (void)updateWindowMetrics { if (!_engine) { return; diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 03fe2e90d8d4a..bf2c1b9d44831 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -1956,58 +1955,3 @@ FlutterEngineResult FlutterEnginePostCallbackOnAllNativeThreads( "Internal error while attempting to post " "tasks to all threads."); } - -namespace { -static bool ValidDisplayConfiguration(const FlutterEngineDisplay* displays, - size_t display_count) { - std::set display_ids; - for (size_t i = 0; i < display_count; i++) { - if (displays[i].single_display && display_count != 1) { - return false; - } - display_ids.insert(displays[i].display_id); - } - - return display_ids.size() == display_count; -} -} // namespace - -FlutterEngineResult FlutterEngineNotifyDisplayUpdate( - FLUTTER_API_SYMBOL(FlutterEngine) raw_engine, - const FlutterEngineDisplaysUpdateType update_type, - const FlutterEngineDisplay* embedder_displays, - size_t display_count) { - if (raw_engine == nullptr) { - return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); - } - - if (!ValidDisplayConfiguration(embedder_displays, display_count)) { - return LOG_EMBEDDER_ERROR( - kInvalidArguments, - "Invalid FlutterEngineDisplay configuration specified."); - } - - auto engine = reinterpret_cast(raw_engine); - - switch (update_type) { - case kFlutterEngineDisplaysUpdateTypeStartup: { - std::vector displays; - for (size_t i = 0; i < display_count; i++) { - flutter::Display display = - flutter::Display(embedder_displays[i].refresh_rate); - if (!embedder_displays[i].single_display) { - display = flutter::Display(embedder_displays[i].display_id, - embedder_displays[i].refresh_rate); - } - displays.push_back(display); - } - engine->GetShell().OnDisplayUpdates(flutter::DisplayUpdateType::kStartup, - displays); - return kSuccess; - } - default: - return LOG_EMBEDDER_ERROR( - kInvalidArguments, - "Invalid FlutterEngineDisplaysUpdateType type specified."); - } -} diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 8ed2dadfac37f..97df3d2468308 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -963,56 +963,6 @@ typedef const FlutterLocale* (*FlutterComputePlatformResolvedLocaleCallback)( const FlutterLocale** /* supported_locales*/, size_t /* Number of locales*/); -/// Display refers to a graphics hardware system consisting of a framebuffer, -/// typically a monitor or a screen. This ID is unique per display and is -/// stable until the Flutter application restarts. -typedef uint64_t FlutterEngineDisplayId; - -typedef struct { - /// This size of this struct. Must be sizeof(FlutterDisplay). - size_t struct_size; - - FlutterEngineDisplayId display_id; - - /// This is set to true if the embedder only has one display. In cases where - /// this is set to true, the value of display_id is ignored. In cases where - /// this is not set to true, it is expected that a valid display_id be - /// provided. - bool single_display; - - /// This represents the refresh period in frames per second. This value may be - /// zero if the device is not running or unavaliable or unknown. - double refresh_rate; -} FlutterEngineDisplay; - -/// The update type parameter that is passed to -/// `FlutterEngineNotifyDisplayUpdate`. -typedef enum { - /// `FlutterEngineDisplay`s that were active during start-up. A display is - /// considered active if: - /// 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. - kFlutterEngineDisplaysUpdateTypeStartup, - kFlutterEngineDisplaysUpdateTypeCount, -} FlutterEngineDisplaysUpdateType; - -//------------------------------------------------------------------------------ -/// @brief Posts updates corresponding to display changes to a running engine -/// instance. -/// -/// @param[in] update_type The type of update pushed to the engine. -/// @param[in] displays The displays affected by this update. -/// @param[in] display_count Size of the displays array, must be at least 1. -/// -/// @return the result of the call made to the engine. -/// -FlutterEngineResult FlutterEngineNotifyDisplayUpdate( - FLUTTER_API_SYMBOL(FlutterEngine) engine, - FlutterEngineDisplaysUpdateType update_type, - const FlutterEngineDisplay* displays, - size_t display_count); - typedef int64_t FlutterEngineDartPort; typedef enum { diff --git a/shell/platform/embedder/tests/embedder_unittests_gl.cc b/shell/platform/embedder/tests/embedder_unittests_gl.cc index 9a93e448f98ae..d376522ce27b0 100644 --- a/shell/platform/embedder/tests/embedder_unittests_gl.cc +++ b/shell/platform/embedder/tests/embedder_unittests_gl.cc @@ -3127,230 +3127,5 @@ TEST_F(EmbedderTest, PresentInfoContainsValidFBOId) { frame_latch.Wait(); } -TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithDisplayId) { - auto& context = GetEmbedderContext(ContextType::kOpenGLContext); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetCompositor(); - builder.SetDartEntrypoint("empty_scene"); - fml::AutoResetWaitableEvent latch; - context.AddNativeCallback( - "SignalNativeTest", - CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); })); - - auto engine = builder.LaunchEngine(); - - ASSERT_TRUE(engine.is_valid()); - - FlutterEngineDisplay display; - display.struct_size = sizeof(FlutterEngineDisplay); - display.display_id = 1; - display.refresh_rate = 20; - - std::vector displays = {display}; - - const FlutterEngineResult result = FlutterEngineNotifyDisplayUpdate( - engine.get(), kFlutterEngineDisplaysUpdateTypeStartup, displays.data(), - displays.size()); - ASSERT_EQ(result, kSuccess); - - flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell(); - ASSERT_EQ(shell.GetMainDisplayRefreshRate(), display.refresh_rate); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 800; - event.height = 600; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - - latch.Wait(); -} - -TEST_F(EmbedderTest, SetSingleDisplayConfigurationWithoutDisplayId) { - auto& context = GetEmbedderContext(ContextType::kOpenGLContext); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetCompositor(); - builder.SetDartEntrypoint("empty_scene"); - fml::AutoResetWaitableEvent latch; - context.AddNativeCallback( - "SignalNativeTest", - CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); })); - - auto engine = builder.LaunchEngine(); - - ASSERT_TRUE(engine.is_valid()); - - FlutterEngineDisplay display; - display.struct_size = sizeof(FlutterEngineDisplay); - display.single_display = true; - display.refresh_rate = 20; - - std::vector displays = {display}; - - const FlutterEngineResult result = FlutterEngineNotifyDisplayUpdate( - engine.get(), kFlutterEngineDisplaysUpdateTypeStartup, displays.data(), - displays.size()); - ASSERT_EQ(result, kSuccess); - - flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell(); - ASSERT_EQ(shell.GetMainDisplayRefreshRate(), display.refresh_rate); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 800; - event.height = 600; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - - latch.Wait(); -} - -TEST_F(EmbedderTest, SetValidMultiDisplayConfiguration) { - auto& context = GetEmbedderContext(ContextType::kOpenGLContext); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetCompositor(); - builder.SetDartEntrypoint("empty_scene"); - fml::AutoResetWaitableEvent latch; - context.AddNativeCallback( - "SignalNativeTest", - CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); })); - - auto engine = builder.LaunchEngine(); - - ASSERT_TRUE(engine.is_valid()); - - FlutterEngineDisplay display_1; - display_1.struct_size = sizeof(FlutterEngineDisplay); - display_1.display_id = 1; - display_1.single_display = false; - display_1.refresh_rate = 20; - - FlutterEngineDisplay display_2; - display_2.struct_size = sizeof(FlutterEngineDisplay); - display_2.display_id = 2; - display_2.single_display = false; - display_2.refresh_rate = 60; - - std::vector displays = {display_1, display_2}; - - const FlutterEngineResult result = FlutterEngineNotifyDisplayUpdate( - engine.get(), kFlutterEngineDisplaysUpdateTypeStartup, displays.data(), - displays.size()); - ASSERT_EQ(result, kSuccess); - - flutter::Shell& shell = ToEmbedderEngine(engine.get())->GetShell(); - ASSERT_EQ(shell.GetMainDisplayRefreshRate(), display_1.refresh_rate); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 800; - event.height = 600; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - - latch.Wait(); -} - -TEST_F(EmbedderTest, MultipleDisplaysWithSingleDisplayTrueIsInvalid) { - auto& context = GetEmbedderContext(ContextType::kOpenGLContext); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetCompositor(); - builder.SetDartEntrypoint("empty_scene"); - fml::AutoResetWaitableEvent latch; - context.AddNativeCallback( - "SignalNativeTest", - CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); })); - - auto engine = builder.LaunchEngine(); - - ASSERT_TRUE(engine.is_valid()); - - FlutterEngineDisplay display_1; - display_1.struct_size = sizeof(FlutterEngineDisplay); - display_1.display_id = 1; - display_1.single_display = true; - display_1.refresh_rate = 20; - - FlutterEngineDisplay display_2; - display_2.struct_size = sizeof(FlutterEngineDisplay); - display_2.display_id = 2; - display_2.single_display = true; - display_2.refresh_rate = 60; - - std::vector displays = {display_1, display_2}; - - const FlutterEngineResult result = FlutterEngineNotifyDisplayUpdate( - engine.get(), kFlutterEngineDisplaysUpdateTypeStartup, displays.data(), - displays.size()); - ASSERT_NE(result, kSuccess); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 800; - event.height = 600; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - - latch.Wait(); -} - -TEST_F(EmbedderTest, MultipleDisplaysWithSameDisplayIdIsInvalid) { - auto& context = GetEmbedderContext(ContextType::kOpenGLContext); - - EmbedderConfigBuilder builder(context); - builder.SetOpenGLRendererConfig(SkISize::Make(800, 600)); - builder.SetCompositor(); - builder.SetDartEntrypoint("empty_scene"); - fml::AutoResetWaitableEvent latch; - context.AddNativeCallback( - "SignalNativeTest", - CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); })); - - auto engine = builder.LaunchEngine(); - - ASSERT_TRUE(engine.is_valid()); - - FlutterEngineDisplay display_1; - display_1.struct_size = sizeof(FlutterEngineDisplay); - display_1.display_id = 1; - display_1.single_display = false; - display_1.refresh_rate = 20; - - FlutterEngineDisplay display_2; - display_2.struct_size = sizeof(FlutterEngineDisplay); - display_2.display_id = 1; - display_2.single_display = false; - display_2.refresh_rate = 60; - - std::vector displays = {display_1, display_2}; - - const FlutterEngineResult result = FlutterEngineNotifyDisplayUpdate( - engine.get(), kFlutterEngineDisplaysUpdateTypeStartup, displays.data(), - displays.size()); - ASSERT_NE(result, kSuccess); - - FlutterWindowMetricsEvent event = {}; - event.struct_size = sizeof(event); - event.width = 800; - event.height = 600; - event.pixel_ratio = 1.0; - ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), - kSuccess); - - latch.Wait(); -} - } // namespace testing } // namespace flutter