From ccae44ffd06607ba679bccbc1cd0b5c931e11661 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 28 Jun 2021 10:16:01 -0700 Subject: [PATCH] Surface frame number identifier through window (flutter/engine#26785) --- .../src/flutter/lib/ui/fixtures/ui_test.dart | 26 ++++++++++++++++++- engine/src/flutter/lib/ui/hooks.dart | 3 ++- .../flutter/lib/ui/platform_dispatcher.dart | 23 ++++++++++++++++ engine/src/flutter/lib/ui/window.dart | 22 ++++++++++++++++ .../lib/ui/window/platform_configuration.cc | 4 ++- .../lib/ui/window/platform_configuration.h | 7 ++++- .../lib/src/engine/platform_dispatcher.dart | 2 ++ .../lib/src/ui/platform_dispatcher.dart | 5 ++++ .../flutter/lib/web_ui/lib/src/ui/window.dart | 13 ++++++++++ .../src/flutter/runtime/runtime_controller.cc | 5 ++-- .../src/flutter/runtime/runtime_controller.h | 2 +- engine/src/flutter/shell/common/animator.cc | 3 ++- engine/src/flutter/shell/common/animator.h | 3 ++- engine/src/flutter/shell/common/engine.cc | 4 +-- engine/src/flutter/shell/common/engine.h | 6 ++++- engine/src/flutter/shell/common/shell.cc | 5 ++-- engine/src/flutter/shell/common/shell.h | 3 ++- 17 files changed, 121 insertions(+), 15 deletions(-) diff --git a/engine/src/flutter/lib/ui/fixtures/ui_test.dart b/engine/src/flutter/lib/ui/fixtures/ui_test.dart index e599cece7e5..81a307600a6 100644 --- a/engine/src/flutter/lib/ui/fixtures/ui_test.dart +++ b/engine/src/flutter/lib/ui/fixtures/ui_test.dart @@ -317,6 +317,11 @@ void hooksTests() { } } + void expectNotEquals(Object? value, Object? expected) { + if (value == expected) { + throw 'Expected $value to not be $expected.'; + } + } test('onMetricsChanged preserves callback zone', () { late Zone originalZone; @@ -479,7 +484,7 @@ void hooksTests() { }; }); - _callHook('_beginFrame', 1, 1234); + _callHook('_beginFrame', 2, 1234, 1); expectIdentical(runZone, innerZone); expectEquals(start, const Duration(microseconds: 1234)); }); @@ -625,6 +630,25 @@ void hooksTests() { expectEquals(platformBrightness, Brightness.dark); }); + test('onFrameDataChanged preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late int frameNumber; + + runZoned(() { + innerZone = Zone.current; + window.onFrameDataChanged = () { + runZone = Zone.current; + frameNumber = window.frameData.frameNumber; + }; + }); + + _callHook('_beginFrame', 2, 0, 2); + expectNotEquals(runZone, null); + expectIdentical(runZone, innerZone); + expectEquals(frameNumber, 2); + }); + _finish(); } diff --git a/engine/src/flutter/lib/ui/hooks.dart b/engine/src/flutter/lib/ui/hooks.dart index 74c704042e2..0c6c48dea13 100644 --- a/engine/src/flutter/lib/ui/hooks.dart +++ b/engine/src/flutter/lib/ui/hooks.dart @@ -110,8 +110,9 @@ void _dispatchSemanticsAction(int id, int action, ByteData? args) { @pragma('vm:entry-point') // ignore: unused_element -void _beginFrame(int microseconds) { +void _beginFrame(int microseconds, int frameNumber) { PlatformDispatcher.instance._beginFrame(microseconds); + PlatformDispatcher.instance._updateFrameData(frameNumber); } @pragma('vm:entry-point') diff --git a/engine/src/flutter/lib/ui/platform_dispatcher.dart b/engine/src/flutter/lib/ui/platform_dispatcher.dart index c6244103c97..51c0ceaeb9c 100644 --- a/engine/src/flutter/lib/ui/platform_dispatcher.dart +++ b/engine/src/flutter/lib/ui/platform_dispatcher.dart @@ -877,6 +877,29 @@ class PlatformDispatcher { _onSemanticsActionZone = Zone.current; } + // Called from the engine via hooks.dart. + void _updateFrameData(int frameNumber) { + final FrameData previous = _frameData; + if (previous.frameNumber == frameNumber) { + return; + } + _frameData = FrameData._(frameNumber: frameNumber); + _invoke(onFrameDataChanged, _onFrameDataChangedZone); + } + + /// The [FrameData] object for the current frame. + FrameData get frameData => _frameData; + FrameData _frameData = const FrameData._(); + + /// A callback that is invoked when the window updates the [FrameData]. + VoidCallback? get onFrameDataChanged => _onFrameDataChanged; + VoidCallback? _onFrameDataChanged; + Zone _onFrameDataChangedZone = Zone.root; + set onFrameDataChanged(VoidCallback? callback) { + _onFrameDataChanged = callback; + _onFrameDataChangedZone = Zone.current; + } + // Called from the engine, via hooks.dart void _dispatchSemanticsAction(int id, int action, ByteData? args) { _invoke3( diff --git a/engine/src/flutter/lib/ui/window.dart b/engine/src/flutter/lib/ui/window.dart index f96e8283a96..426f78be2d4 100644 --- a/engine/src/flutter/lib/ui/window.dart +++ b/engine/src/flutter/lib/ui/window.dart @@ -633,6 +633,15 @@ class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onSemanticsEnabledChanged = callback; } + /// The [FrameData] object for the current frame. + FrameData get frameData => platformDispatcher.frameData; + + /// A callback that is invoked when the window updates the [FrameData]. + VoidCallback? get onFrameDataChanged => platformDispatcher.onFrameDataChanged; + set onFrameDataChanged(VoidCallback? callback) { + platformDispatcher.onFrameDataChanged = callback; + } + /// A callback that is invoked whenever the user requests an action to be /// performed. /// @@ -849,3 +858,16 @@ enum Brightness { /// belonging to the application, including top level application windows like /// this one. final SingletonFlutterWindow window = SingletonFlutterWindow._(0, PlatformDispatcher.instance); + +/// Additional data available on each flutter frame. +class FrameData { + const FrameData._({this.frameNumber = -1}); + + /// The number of the current frame. + /// + /// This number monotonically increases, but doesn't necessarily + /// start at a particular value. + /// + /// If not provided, defaults to -1. + final int frameNumber; +} diff --git a/engine/src/flutter/lib/ui/window/platform_configuration.cc b/engine/src/flutter/lib/ui/window/platform_configuration.cc index 18133a2e988..ecd0415e3e1 100644 --- a/engine/src/flutter/lib/ui/window/platform_configuration.cc +++ b/engine/src/flutter/lib/ui/window/platform_configuration.cc @@ -366,7 +366,8 @@ uint64_t PlatformConfiguration::RegisterKeyDataResponse( return response_id; } -void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { +void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime, + uint64_t frame_number) { std::shared_ptr dart_state = begin_frame_.dart_state().lock(); if (!dart_state) { @@ -379,6 +380,7 @@ void PlatformConfiguration::BeginFrame(fml::TimePoint frameTime) { tonic::LogIfError( tonic::DartInvoke(begin_frame_.Get(), { Dart_NewInteger(microseconds), + Dart_NewInteger(frame_number), })); UIDartState::Current()->FlushMicrotasksNow(); diff --git a/engine/src/flutter/lib/ui/window/platform_configuration.h b/engine/src/flutter/lib/ui/window/platform_configuration.h index a32b2def91f..e5ec3070a1f 100644 --- a/engine/src/flutter/lib/ui/window/platform_configuration.h +++ b/engine/src/flutter/lib/ui/window/platform_configuration.h @@ -364,7 +364,12 @@ class PlatformConfiguration final { /// began. May be used by animation interpolators, /// physics simulations, etc.. /// - void BeginFrame(fml::TimePoint frame_time); + /// @param[in] frame_number The frame number recorded by the animator. Used + /// by the framework to associate frame specific + /// debug information with frame timings and timeline + /// events. + /// + void BeginFrame(fml::TimePoint frame_time, uint64_t frame_number); //---------------------------------------------------------------------------- /// @brief Dart code cannot fully measure the time it takes for a diff --git a/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart index cfc6bdc5286..0792fcc5baf 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -941,6 +941,8 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } }); } + + ui.FrameData get frameData => const ui.FrameData.webOnly(); } bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData? data) { diff --git a/engine/src/flutter/lib/web_ui/lib/src/ui/platform_dispatcher.dart b/engine/src/flutter/lib/web_ui/lib/src/ui/platform_dispatcher.dart index f802d9fc4d1..76c70b79c16 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/ui/platform_dispatcher.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/ui/platform_dispatcher.dart @@ -98,6 +98,11 @@ abstract class PlatformDispatcher { set onSemanticsAction(SemanticsActionCallback? callback); String get defaultRouteName; + + FrameData get frameData; + + VoidCallback? get onFrameDataChanged => null; + set onFrameDataChanged(VoidCallback? callback) {} } class PlatformConfiguration { diff --git a/engine/src/flutter/lib/web_ui/lib/src/ui/window.dart b/engine/src/flutter/lib/web_ui/lib/src/ui/window.dart index 85f12e8fd78..173b0ffd575 100644 --- a/engine/src/flutter/lib/web_ui/lib/src/ui/window.dart +++ b/engine/src/flutter/lib/web_ui/lib/src/ui/window.dart @@ -102,6 +102,11 @@ abstract class SingletonFlutterWindow extends FlutterWindow { platformDispatcher.onSemanticsAction = callback; } + FrameData get frameData => const FrameData._(); + + VoidCallback? get onFrameDataChanged => null; + set onFrameDataChanged(VoidCallback? callback) {} + AccessibilityFeatures get accessibilityFeatures => platformDispatcher.accessibilityFeatures; VoidCallback? get onAccessibilityFeaturesChanged => @@ -241,3 +246,11 @@ class IsolateNameServer { } SingletonFlutterWindow get window => engine.window; + +class FrameData { + const FrameData._({this.frameNumber = -1}); + + const FrameData.webOnly() : frameNumber = -1; + + final int frameNumber; +} diff --git a/engine/src/flutter/runtime/runtime_controller.cc b/engine/src/flutter/runtime/runtime_controller.cc index 1d622bbe3c2..35ce7e3e80d 100644 --- a/engine/src/flutter/runtime/runtime_controller.cc +++ b/engine/src/flutter/runtime/runtime_controller.cc @@ -177,9 +177,10 @@ bool RuntimeController::SetAccessibilityFeatures(int32_t flags) { return false; } -bool RuntimeController::BeginFrame(fml::TimePoint frame_time) { +bool RuntimeController::BeginFrame(fml::TimePoint frame_time, + uint64_t frame_number) { if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { - platform_configuration->BeginFrame(frame_time); + platform_configuration->BeginFrame(frame_time, frame_number); return true; } diff --git a/engine/src/flutter/runtime/runtime_controller.h b/engine/src/flutter/runtime/runtime_controller.h index 00e07be7680..e1fa9946e2c 100644 --- a/engine/src/flutter/runtime/runtime_controller.h +++ b/engine/src/flutter/runtime/runtime_controller.h @@ -244,7 +244,7 @@ class RuntimeController : public PlatformConfigurationClient { /// @return If notification to begin frame rendering was delivered to the /// running isolate. /// - bool BeginFrame(fml::TimePoint frame_time); + bool BeginFrame(fml::TimePoint frame_time, uint64_t frame_number); //---------------------------------------------------------------------------- /// @brief Dart code cannot fully measure the time it takes for a diff --git a/engine/src/flutter/shell/common/animator.cc b/engine/src/flutter/shell/common/animator.cc index 75fdaec4f94..39cb93b88e9 100644 --- a/engine/src/flutter/shell/common/animator.cc +++ b/engine/src/flutter/shell/common/animator.cc @@ -149,7 +149,8 @@ void Animator::BeginFrame( { TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame", FrameParity()); - delegate_.OnAnimatorBeginFrame(frame_target_time); + uint64_t frame_number = frame_timings_recorder_->GetFrameNumber(); + delegate_.OnAnimatorBeginFrame(frame_target_time, frame_number); } if (!frame_scheduled_) { diff --git a/engine/src/flutter/shell/common/animator.h b/engine/src/flutter/shell/common/animator.h index bf35e5891f8..8061e223a5b 100644 --- a/engine/src/flutter/shell/common/animator.h +++ b/engine/src/flutter/shell/common/animator.h @@ -31,7 +31,8 @@ class Animator final { public: class Delegate { public: - virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time) = 0; + virtual void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, + uint64_t frame_number) = 0; virtual void OnAnimatorNotifyIdle(int64_t deadline) = 0; diff --git a/engine/src/flutter/shell/common/engine.cc b/engine/src/flutter/shell/common/engine.cc index e6e22d41f34..58707be2337 100644 --- a/engine/src/flutter/shell/common/engine.cc +++ b/engine/src/flutter/shell/common/engine.cc @@ -220,9 +220,9 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) { return Engine::RunStatus::Success; } -void Engine::BeginFrame(fml::TimePoint frame_time) { +void Engine::BeginFrame(fml::TimePoint frame_time, uint64_t frame_number) { TRACE_EVENT0("flutter", "Engine::BeginFrame"); - runtime_controller_->BeginFrame(frame_time); + runtime_controller_->BeginFrame(frame_time, frame_number); } void Engine::ReportTimings(std::vector timings) { diff --git a/engine/src/flutter/shell/common/engine.h b/engine/src/flutter/shell/common/engine.h index 78bc730743d..139a0e00f9f 100644 --- a/engine/src/flutter/shell/common/engine.h +++ b/engine/src/flutter/shell/common/engine.h @@ -500,7 +500,11 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// began. May be used by animation interpolators, /// physics simulations, etc.. /// - void BeginFrame(fml::TimePoint frame_time); + /// @param[in] frame_number The frame number recorded by the animator. Used + /// by the framework to associate frame specific + /// debug information with frame timings and timeline + /// events. + void BeginFrame(fml::TimePoint frame_time, uint64_t frame_number); //---------------------------------------------------------------------------- /// @brief Notifies the engine that the UI task runner is not expected to diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index 1a98703fe99..e423a175968 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -1101,7 +1101,8 @@ void Shell::OnPlatformViewSetNextFrameCallback(const fml::closure& closure) { } // |Animator::Delegate| -void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time) { +void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time, + uint64_t frame_number) { FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread()); @@ -1111,7 +1112,7 @@ void Shell::OnAnimatorBeginFrame(fml::TimePoint frame_target_time) { latest_frame_target_time_.emplace(frame_target_time); } if (engine_) { - engine_->BeginFrame(frame_target_time); + engine_->BeginFrame(frame_target_time, frame_number); } } diff --git a/engine/src/flutter/shell/common/shell.h b/engine/src/flutter/shell/common/shell.h index e39f78d633f..374a0968bbf 100644 --- a/engine/src/flutter/shell/common/shell.h +++ b/engine/src/flutter/shell/common/shell.h @@ -532,7 +532,8 @@ class Shell final : public PlatformView::Delegate, AssetResolver::AssetResolverType type) override; // |Animator::Delegate| - void OnAnimatorBeginFrame(fml::TimePoint frame_target_time) override; + void OnAnimatorBeginFrame(fml::TimePoint frame_target_time, + uint64_t frame_number) override; // |Animator::Delegate| void OnAnimatorNotifyIdle(int64_t deadline) override;