diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 4a12e0362a33a..3c107f6b35b5c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2189,6 +2189,8 @@ ORIGIN: ../../../flutter/shell/common/thread_host.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/variable_refresh_rate_display.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/variable_refresh_rate_display.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/variable_refresh_rate_reporter.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/common/viewport_metrics_updater.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/shell/common/viewport_metrics_updater.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/vsync_waiter.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/vsync_waiter.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/shell/common/vsync_waiter_fallback.cc + ../../../flutter/LICENSE @@ -4804,6 +4806,8 @@ 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_reporter.h +FILE: ../../../flutter/shell/common/viewport_metrics_updater.cc +FILE: ../../../flutter/shell/common/viewport_metrics_updater.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 19009938162fc..8fbd4a3d2cf3d 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -120,6 +120,8 @@ source_set("common") { "switches.h", "thread_host.cc", "thread_host.h", + "viewport_metrics_updater.cc", + "viewport_metrics_updater.h", "vsync_waiter.cc", "vsync_waiter.h", "vsync_waiter_fallback.cc", diff --git a/shell/common/engine.cc b/shell/common/engine.cc index 9107be5bacb92..5ef7322a33717 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -18,6 +18,7 @@ #include "flutter/shell/common/animator.h" #include "flutter/shell/common/platform_view.h" #include "flutter/shell/common/shell.h" +#include "flutter/shell/common/viewport_metrics_updater.h" #include "rapidjson/document.h" #include "third_party/dart/runtime/include/dart_tools_api.h" @@ -58,6 +59,8 @@ Engine::Engine( task_runners_(task_runners), weak_factory_(this) { pointer_data_dispatcher_ = dispatcher_maker(*this); + viewport_metrics_updater_ = std::unique_ptr( + new ViewportMetricsUpdater(*this)); } Engine::Engine(Delegate& delegate, @@ -287,6 +290,30 @@ tonic::DartErrorHandleType Engine::GetUIIsolateLastError() { } void Engine::SetViewportMetrics(const ViewportMetrics& metrics) { + viewport_metrics_updater_->UpdateViewportMetrics(metrics); +} + +VsyncWaiterProcessStage Engine::GetVsyncWaiterProcessStage() { + std::shared_ptr waiter = animator_->GetVsyncWaiter().lock(); + if (!waiter) { + return VsyncWaiterProcessStage::kProcessingComplete; + } + return waiter->GetProcessStage(); +} + +fml::TimePoint Engine::GetVsyncWaiterFrameTargetTime() { + std::shared_ptr waiter = animator_->GetVsyncWaiter().lock(); + if (!waiter) { + return fml::TimePoint::Now(); + } + return waiter->GetVsyncFrameTargetTime(); +} + +void Engine::PostTaskOnUITaskRunner(const fml::closure& callback) { + task_runners_.GetUITaskRunner()->PostTask(callback); +} + +void Engine::DoUpdateViewportMetrics(const ViewportMetrics& metrics) { runtime_controller_->SetViewportMetrics(metrics); ScheduleFrame(); } diff --git a/shell/common/engine.h b/shell/common/engine.h index d4b4340658eae..3ee4c4e23680d 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -32,6 +32,7 @@ #include "flutter/shell/common/rasterizer.h" #include "flutter/shell/common/run_configuration.h" #include "flutter/shell/common/shell_io_manager.h" +#include "flutter/shell/common/viewport_metrics_updater.h" #include "third_party/skia/include/core/SkPicture.h" namespace flutter { @@ -72,7 +73,9 @@ namespace flutter { /// name and it does happen to be one of the older classes in the /// repository. /// -class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { +class Engine final : public RuntimeDelegate, + PointerDataDispatcher::Delegate, + ViewportMetricsUpdater::Delegate { public: //---------------------------------------------------------------------------- /// @brief Indicates the result of the call to `Engine::Run`. @@ -795,10 +798,22 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { void DoDispatchPacket(std::unique_ptr packet, uint64_t trace_flow_id) override; - // |PointerDataDispatcher::Delegate| + // |PointerDataDispatcher::Delegate|, |ViewportMetricsUpdater::Delegate| void ScheduleSecondaryVsyncCallback(uintptr_t id, const fml::closure& callback) override; + // |ViewportMetricsUpdater::Delegate| + void DoUpdateViewportMetrics(const ViewportMetrics& metrics) override; + + // |ViewportMetricsUpdater::Delegate| + VsyncWaiterProcessStage GetVsyncWaiterProcessStage() override; + + // |ViewportMetricsUpdater::Delegate| + fml::TimePoint GetVsyncWaiterFrameTargetTime() override; + + // |ViewportMetricsUpdater::Delegate| + void PostTaskOnUITaskRunner(const fml::closure& callback) override; + //---------------------------------------------------------------------------- /// @brief Get the last Entrypoint that was used in the RunConfiguration /// when |Engine::Run| was called. @@ -958,6 +973,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { // is destructed first. std::unique_ptr pointer_data_dispatcher_; + std::unique_ptr viewport_metrics_updater_; + std::string last_entry_point_; std::string last_entry_point_library_; std::vector last_entry_point_args_; diff --git a/shell/common/viewport_metrics_updater.cc b/shell/common/viewport_metrics_updater.cc new file mode 100644 index 0000000000000..b04fb5bb07c40 --- /dev/null +++ b/shell/common/viewport_metrics_updater.cc @@ -0,0 +1,65 @@ +// 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/viewport_metrics_updater.h" +#include "flutter/fml/trace_event.h" +#include "flutter/shell/common/vsync_waiter.h" + +namespace flutter { + +ViewportMetricsUpdater::ViewportMetricsUpdater(Delegate& delegate) + : delegate_(delegate), weak_factory_(this) {} +ViewportMetricsUpdater::~ViewportMetricsUpdater() = default; + +void ViewportMetricsUpdater::UpdateViewportMetrics( + const ViewportMetrics& metrics) { + TRACE_EVENT0("flutter", "ViewportMetricsUpdater::UpdateViewportMetrics"); + if (!has_set_valid_viewport_metrics_) { + if (metrics.physical_width > 0 && metrics.physical_height > 0) { + has_set_valid_viewport_metrics_ = true; + delegate_.DoUpdateViewportMetrics(metrics); + } + return; + } + + VsyncWaiterProcessStage stage = delegate_.GetVsyncWaiterProcessStage(); + switch (stage) { + case VsyncWaiterProcessStage::kAwaiting: { + fml::TimePoint frame_target_time = + delegate_.GetVsyncWaiterFrameTargetTime(); + if (frame_target_time > fml::TimePoint::Now()) { + delegate_.DoUpdateViewportMetrics(metrics); + } else { + TRACE_EVENT0("flutter", + "ViewportMetricsUpdaterScheduleSecondaryVsyncCallback"); + delegate_.ScheduleSecondaryVsyncCallback( + reinterpret_cast(this), + [updater = weak_factory_.GetWeakPtr(), metrics] { + if (updater) { + updater->delegate_.DoUpdateViewportMetrics(metrics); + } + }); + } + break; + } + case VsyncWaiterProcessStage::kProcessing: { + TRACE_EVENT0("flutter", + "ViewportMetricsUpdaterRegisterTaskOnUITaskRunner"); + delegate_.PostTaskOnUITaskRunner( + [updater = weak_factory_.GetWeakPtr(), metrics]() { + if (updater) { + TRACE_EVENT0("flutter", "UpdateViewportMetricsUsingPostUITask"); + updater->delegate_.DoUpdateViewportMetrics(metrics); + } + }); + break; + } + case VsyncWaiterProcessStage::kProcessingComplete: { + delegate_.DoUpdateViewportMetrics(metrics); + break; + } + } +} + +} // namespace flutter diff --git a/shell/common/viewport_metrics_updater.h b/shell/common/viewport_metrics_updater.h new file mode 100644 index 0000000000000..dd52a409ac6c6 --- /dev/null +++ b/shell/common/viewport_metrics_updater.h @@ -0,0 +1,61 @@ +// 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_VIEWPORT_METRICS_UPDATER_H +#define FLUTTER_VIEWPORT_METRICS_UPDATER_H + +#include +#include "flutter/fml/closure.h" +#include "flutter/fml/macros.h" +#include "flutter/fml/memory/weak_ptr.h" +#include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/shell/common/vsync_waiter.h" + +namespace flutter { + +class ViewportMetricsUpdater; + +class ViewportMetricsUpdater { + public: + class Delegate { + public: + /// Actually update the metrics data using Engine's `runtime_controller_`. + virtual void DoUpdateViewportMetrics(const ViewportMetrics& metrics) = 0; + + /// Get current process stage of current vsync waiter. And updater will use + /// different strategies to update the viewport metrics data to receiver. + virtual VsyncWaiterProcessStage GetVsyncWaiterProcessStage() = 0; + + /// Get current vsync waiter's frame target time. + virtual fml::TimePoint GetVsyncWaiterFrameTargetTime() = 0; + + /// Post a task to UI TaskRunner. + virtual void PostTaskOnUITaskRunner(const fml::closure& callback) = 0; + + /// Schedule secondary vsync callback to execute after the main vsync + /// process callback. + virtual void ScheduleSecondaryVsyncCallback( + uintptr_t id, + const fml::closure& callback) = 0; + }; + + explicit ViewportMetricsUpdater(Delegate& delegate); + + ~ViewportMetricsUpdater(); + + void UpdateViewportMetrics(const ViewportMetrics& metrics); + + private: + Delegate& delegate_; + + bool has_set_valid_viewport_metrics_ = false; + + // WeakPtrFactory must be the last member. + fml::WeakPtrFactory weak_factory_; + FML_DISALLOW_COPY_AND_ASSIGN(ViewportMetricsUpdater); +}; + +} // namespace flutter + +#endif // FLUTTER_VIEWPORT_METRICS_UPDATER_H diff --git a/shell/common/vsync_waiter.cc b/shell/common/vsync_waiter.cc index 5ee04ef530b08..d324fa4083087 100644 --- a/shell/common/vsync_waiter.cc +++ b/shell/common/vsync_waiter.cc @@ -5,8 +5,8 @@ #include "flutter/shell/common/vsync_waiter.h" #include "flow/frame_timings.h" -#include "flutter/fml/task_runner.h" -#include "flutter/fml/trace_event.h" +#include "flutter/fml/make_copyable.h" +#include "flutter/fml/synchronization/waitable_event.h" #include "fml/logging.h" #include "fml/message_loop_task_queues.h" #include "fml/task_queue_id.h" @@ -19,9 +19,30 @@ static constexpr const char* kVsyncFlowName = "VsyncFlow"; static constexpr const char* kVsyncTraceName = "VsyncProcessCallback"; VsyncWaiter::VsyncWaiter(const TaskRunners& task_runners) - : task_runners_(task_runners) {} + : task_runners_(task_runners) { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), + fml::MakeCopyable([this, &latch]() mutable { + weak_factory_on_ui_ = nullptr; + weak_factory_on_ui_ = + std::make_unique>(this); + latch.Signal(); + })); + latch.Wait(); +} -VsyncWaiter::~VsyncWaiter() = default; +VsyncWaiter::~VsyncWaiter() { + fml::AutoResetWaitableEvent latch; + fml::TaskRunner::RunNowOrPostTask( + task_runners_.GetUITaskRunner(), + fml::MakeCopyable([weak_factory_on_ui_ = std::move(weak_factory_on_ui_), + &latch]() mutable { + weak_factory_on_ui_.reset(); + latch.Signal(); + })); + latch.Wait(); +}; // Public method invoked by the animator. void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) { @@ -48,6 +69,7 @@ void VsyncWaiter::AsyncWaitForVsync(const Callback& callback) { } } AwaitVSync(); + SetProcessStage(VsyncWaiterProcessStage::kAwaiting); } void VsyncWaiter::ScheduleSecondaryCallback(uintptr_t id, @@ -82,6 +104,17 @@ void VsyncWaiter::ScheduleSecondaryCallback(uintptr_t id, } } AwaitVSyncForSecondaryCallback(); + SetProcessStage(VsyncWaiterProcessStage::kAwaiting); +} + +VsyncWaiterProcessStage VsyncWaiter::GetProcessStage() { + std::scoped_lock lock(stage_mutex_); + return stage_; +} + +fml::TimePoint VsyncWaiter::GetVsyncFrameTargetTime() { + std::scoped_lock lock(frame_target_time_mutex_); + return frame_target_time_; } void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time, @@ -101,11 +134,15 @@ void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time, secondary_callbacks_.clear(); } + SetProcessStage(VsyncWaiterProcessStage::kProcessing); + SetFrameTargetTime(frame_target_time); + if (!callback && secondary_callbacks.empty()) { // This means that the vsync waiter implementation fired a callback for a // request we did not make. This is a paranoid check but we still want to // make sure we catch misbehaving vsync implementations. TRACE_EVENT_INSTANT0("flutter", "MismatchedFrameCallback"); + SetProcessStage(VsyncWaiterProcessStage::kProcessingComplete); return; } @@ -146,6 +183,23 @@ void VsyncWaiter::FireCallback(fml::TimePoint frame_start_time, for (auto& secondary_callback : secondary_callbacks) { task_runners_.GetUITaskRunner()->PostTask(secondary_callback); } + + task_runners_.GetUITaskRunner()->PostTask( + [waiter = weak_factory_on_ui_->GetWeakPtr()] { + if (waiter) { + waiter->SetProcessStage(VsyncWaiterProcessStage::kProcessingComplete); + } + }); +} + +void VsyncWaiter::SetProcessStage(VsyncWaiterProcessStage stage) { + std::scoped_lock lock(stage_mutex_); + stage_ = stage; +} + +void VsyncWaiter::SetFrameTargetTime(fml::TimePoint frame_target_time) { + std::scoped_lock lock(frame_target_time_mutex_); + frame_target_time_ = frame_target_time; } void VsyncWaiter::PauseDartMicroTasks() { diff --git a/shell/common/vsync_waiter.h b/shell/common/vsync_waiter.h index df70342c399e4..20ef27325df2a 100644 --- a/shell/common/vsync_waiter.h +++ b/shell/common/vsync_waiter.h @@ -16,6 +16,13 @@ namespace flutter { +/// A Enum using for indicating the vsync waiter's current process stage. +enum VsyncWaiterProcessStage { + kAwaiting, + kProcessing, + kProcessingComplete, +}; + /// Abstract Base Class that represents a platform specific mechanism for /// getting callbacks when a vsync event happens. class VsyncWaiter : public std::enable_shared_from_this { @@ -32,6 +39,14 @@ class VsyncWaiter : public std::enable_shared_from_this { /// |Animator::ScheduleMaybeClearTraceFlowIds|. void ScheduleSecondaryCallback(uintptr_t id, const fml::closure& callback); + /// Get current frame's target time. + fml::TimePoint GetVsyncFrameTargetTime(); + + /// Get current stage. + /// + /// See also |VsyncWaiterProcessStage| + VsyncWaiterProcessStage GetProcessStage(); + protected: // On some backends, the |FireCallback| needs to be made from a static C // method. @@ -75,10 +90,18 @@ class VsyncWaiter : public std::enable_shared_from_this { std::mutex callback_mutex_; Callback callback_; std::unordered_map secondary_callbacks_; + std::mutex stage_mutex_; + VsyncWaiterProcessStage stage_ = VsyncWaiterProcessStage::kProcessingComplete; + std::mutex frame_target_time_mutex_; + fml::TimePoint frame_target_time_; + void SetProcessStage(VsyncWaiterProcessStage stage); + void SetFrameTargetTime(fml::TimePoint frame_target_time); void PauseDartMicroTasks(); static void ResumeDartMicroTasks(fml::TaskQueueId ui_task_queue_id); + /// The weak factory using on ui task runner. + std::unique_ptr> weak_factory_on_ui_; FML_DISALLOW_COPY_AND_ASSIGN(VsyncWaiter); }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 1831b4d2f546b..64310e6e8a0a3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1664,7 +1664,11 @@ - (void)setupKeyboardAnimationVsyncClient { [flutterViewController updateViewportMetricsIfNeeded]; } } else { - fml::TimeDelta timeElapsed = recorder.get()->GetVsyncTargetTime() - + // Because updateViewportMetrics will work in next frame and the frame will + // present in next next frame, so here should add a time of one frame interval + // to get correct animation target time. + fml::TimeDelta frameInterval = recorder->GetVsyncTargetTime() - recorder->GetVsyncStartTime(); + fml::TimeDelta timeElapsed = recorder.get()->GetVsyncTargetTime() + frameInterval - flutterViewController.get().keyboardAnimationStartTime; flutterViewController.get()->_viewportMetrics.physical_view_inset_bottom = diff --git a/shell/platform/darwin/ios/framework/Source/VsyncWaiterIosTest.mm b/shell/platform/darwin/ios/framework/Source/VsyncWaiterIosTest.mm index dda84f9ac158e..b1038b4eea4f9 100644 --- a/shell/platform/darwin/ios/framework/Source/VsyncWaiterIosTest.mm +++ b/shell/platform/darwin/ios/framework/Source/VsyncWaiterIosTest.mm @@ -136,11 +136,16 @@ - (void)testAwaitAndPauseWillWorkCorrectly { [vsyncClient release]; } -- (void)testRefreshRateUpdatedTo80WhenThraedsMerge { - auto platform_thread_task_runner = CreateNewThread("Platform"); - auto raster_thread_task_runner = CreateNewThread("Raster"); - auto ui_thread_task_runner = CreateNewThread("UI"); - auto io_thread_task_runner = CreateNewThread("IO"); +- (void)testRefreshRateUpdatedTo80WhenThreadsMerge { + auto platform_thread = std::make_unique("Platform"); + auto raster_thread = std::make_unique("Raster"); + auto ui_thread = std::make_unique("UI"); + auto io_thread = std::make_unique("IO"); + + auto platform_thread_task_runner = platform_thread->GetTaskRunner(); + auto raster_thread_task_runner = raster_thread->GetTaskRunner(); + auto ui_thread_task_runner = ui_thread->GetTaskRunner(); + auto io_thread_task_runner = io_thread->GetTaskRunner(); auto task_runners = flutter::TaskRunners("test", platform_thread_task_runner, raster_thread_task_runner, ui_thread_task_runner, io_thread_task_runner);