Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 96d7502

Browse files
authored
Add scheduleWarmUpFrame (#50570)
This PR adds `PlatformDispatcher.scheduleWarmUpFrame`. This PR is needed for the follow up changes: * The framework will switch to using this function to render warmup frames in flutter/flutter#143290. * Then the engine will finally be able to switch to multiview pipeline with no regression on startup timing in #49950. For why the warm up frame must involve the engine to render, see flutter/flutter#142851. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent 2c3a86e commit 96d7502

18 files changed

+405
-7
lines changed

lib/ui/dart_ui.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ typedef CanvasPath Path;
9898
V(NativeStringAttribute::initSpellOutStringAttribute) \
9999
V(PlatformConfigurationNativeApi::DefaultRouteName) \
100100
V(PlatformConfigurationNativeApi::ScheduleFrame) \
101+
V(PlatformConfigurationNativeApi::EndWarmUpFrame) \
101102
V(PlatformConfigurationNativeApi::Render) \
102103
V(PlatformConfigurationNativeApi::UpdateSemantics) \
103104
V(PlatformConfigurationNativeApi::SetNeedsReportTimings) \

lib/ui/platform_dispatcher.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -801,11 +801,47 @@ class PlatformDispatcher {
801801
///
802802
/// * [SchedulerBinding], the Flutter framework class which manages the
803803
/// scheduling of frames.
804+
/// * [scheduleWarmUpFrame], which should only be used to schedule warm up
805+
/// frames.
804806
void scheduleFrame() => _scheduleFrame();
805807

806808
@Native<Void Function()>(symbol: 'PlatformConfigurationNativeApi::ScheduleFrame')
807809
external static void _scheduleFrame();
808810

811+
/// Schedule a frame to run as soon as possible, rather than waiting for the
812+
/// engine to request a frame in response to a system "Vsync" signal.
813+
///
814+
/// The application can call this method as soon as it starts up so that the
815+
/// first frame (which is likely to be quite expensive) can start a few extra
816+
/// milliseconds earlier. Using it in other situations might lead to
817+
/// unintended results, such as screen tearing. Depending on platforms and
818+
/// situations, the warm up frame might or might not be actually rendered onto
819+
/// the screen.
820+
///
821+
/// For more introduction to the warm up frame, see
822+
/// [SchedulerBinding.scheduleWarmUpFrame].
823+
///
824+
/// This method uses the provided callbacks as the begin frame callback and
825+
/// the draw frame callback instead of [onBeginFrame] and [onDrawFrame].
826+
///
827+
/// See also:
828+
///
829+
/// * [SchedulerBinding.scheduleWarmUpFrame], which uses this method, and
830+
/// introduces the warm up frame in more details.
831+
/// * [scheduleFrame], which schedules the frame at the next appropriate
832+
/// opportunity and should be used to render regular frames.
833+
void scheduleWarmUpFrame({required VoidCallback beginFrame, required VoidCallback drawFrame}) {
834+
// We use timers here to ensure that microtasks flush in between.
835+
Timer.run(beginFrame);
836+
Timer.run(() {
837+
drawFrame();
838+
_endWarmUpFrame();
839+
});
840+
}
841+
842+
@Native<Void Function()>(symbol: 'PlatformConfigurationNativeApi::EndWarmUpFrame')
843+
external static void _endWarmUpFrame();
844+
809845
/// Additional accessibility features that may be enabled by the platform.
810846
AccessibilityFeatures get accessibilityFeatures => _configuration.accessibilityFeatures;
811847

lib/ui/window/platform_configuration.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,11 @@ void PlatformConfigurationNativeApi::ScheduleFrame() {
589589
UIDartState::Current()->platform_configuration()->client()->ScheduleFrame();
590590
}
591591

592+
void PlatformConfigurationNativeApi::EndWarmUpFrame() {
593+
UIDartState::ThrowIfUIOperationsProhibited();
594+
UIDartState::Current()->platform_configuration()->client()->EndWarmUpFrame();
595+
}
596+
592597
void PlatformConfigurationNativeApi::UpdateSemantics(SemanticsUpdate* update) {
593598
UIDartState::ThrowIfUIOperationsProhibited();
594599
UIDartState::Current()->platform_configuration()->client()->UpdateSemantics(

lib/ui/window/platform_configuration.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ class PlatformConfigurationClient {
6565
///
6666
virtual void ScheduleFrame() = 0;
6767

68+
//--------------------------------------------------------------------------
69+
/// @brief Called when a warm up frame has ended.
70+
///
71+
/// For more introduction, see `Animator::EndWarmUpFrame`.
72+
///
73+
virtual void EndWarmUpFrame() = 0;
74+
6875
//--------------------------------------------------------------------------
6976
/// @brief Updates the client's rendering on the GPU with the newly
7077
/// provided Scene.
@@ -557,6 +564,8 @@ class PlatformConfigurationNativeApi {
557564

558565
static void ScheduleFrame();
559566

567+
static void EndWarmUpFrame();
568+
560569
static void Render(int64_t view_id,
561570
Scene* scene,
562571
double width,

lib/web_ui/lib/platform_dispatcher.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ abstract class PlatformDispatcher {
9090

9191
void scheduleFrame();
9292

93+
void scheduleWarmUpFrame({required VoidCallback beginFrame, required VoidCallback drawFrame});
94+
9395
AccessibilityFeatures get accessibilityFeatures;
9496

9597
VoidCallback? get onAccessibilityFeaturesChanged;

lib/web_ui/lib/src/engine/initialization.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ Future<void> initializeEngineServices({
187187
// TODO(yjbanov): technically Flutter flushes microtasks between
188188
// onBeginFrame and onDrawFrame. We don't, which hasn't
189189
// been an issue yet, but eventually we'll have to
190-
// implement it properly.
190+
// implement it properly. (Also see the to-do in
191+
// `EnginePlatformDispatcher.scheduleWarmUpFrame`).
191192
EnginePlatformDispatcher.instance.invokeOnDrawFrame();
192193
}
193194
});

lib/web_ui/lib/src/engine/platform_dispatcher.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,19 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
782782
scheduleFrameCallback!();
783783
}
784784

785+
@override
786+
void scheduleWarmUpFrame({required ui.VoidCallback beginFrame, required ui.VoidCallback drawFrame}) {
787+
Timer.run(beginFrame);
788+
// We use timers here to ensure that microtasks flush in between.
789+
//
790+
// TODO(dkwingsmt): This logic was moved from the framework and is different
791+
// from how Web renders a regular frame, which doesn't flush microtasks
792+
// between the callbacks at all (see `initializeEngineServices`). We might
793+
// want to change this. See the to-do in `initializeEngineServices` and
794+
// https://github.com/flutter/engine/pull/50570#discussion_r1496671676
795+
Timer.run(drawFrame);
796+
}
797+
785798
/// Updates the application's rendering on the GPU with the newly provided
786799
/// [Scene]. This function must be called within the scope of the
787800
/// [onBeginFrame] or [onDrawFrame] callbacks being invoked. If this function

lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,23 @@ void testMain() {
417417
dispatcher.dispose();
418418
expect(dispatcher.accessibilityPlaceholder.isConnected, isFalse);
419419
});
420+
421+
test('scheduleWarmupFrame should call both callbacks', () async {
422+
bool beginFrameCalled = false;
423+
final Completer<void> drawFrameCalled = Completer<void>();
424+
dispatcher.scheduleWarmUpFrame(beginFrame: () {
425+
expect(drawFrameCalled.isCompleted, false);
426+
expect(beginFrameCalled, false);
427+
beginFrameCalled = true;
428+
}, drawFrame: () {
429+
expect(beginFrameCalled, true);
430+
expect(drawFrameCalled.isCompleted, false);
431+
drawFrameCalled.complete();
432+
});
433+
await drawFrameCalled.future;
434+
expect(beginFrameCalled, true);
435+
expect(drawFrameCalled.isCompleted, true);
436+
});
420437
});
421438
}
422439

runtime/runtime_controller.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,11 @@ void RuntimeController::ScheduleFrame() {
340340
client_.ScheduleFrame();
341341
}
342342

343+
// |PlatformConfigurationClient|
344+
void RuntimeController::EndWarmUpFrame() {
345+
client_.EndWarmUpFrame();
346+
}
347+
343348
// |PlatformConfigurationClient|
344349
void RuntimeController::Render(Scene* scene, double width, double height) {
345350
// TODO(dkwingsmt): Currently only supports a single window.

runtime/runtime_controller.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,9 @@ class RuntimeController : public PlatformConfigurationClient {
657657
// |PlatformConfigurationClient|
658658
void ScheduleFrame() override;
659659

660+
// |PlatformConfigurationClient|
661+
void EndWarmUpFrame() override;
662+
660663
// |PlatformConfigurationClient|
661664
void Render(Scene* scene, double width, double height) override;
662665

0 commit comments

Comments
 (0)