From 7f1b81e2c1379048af22fcd4cac06725821b77e0 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Thu, 4 Aug 2022 18:10:36 -0700 Subject: [PATCH 01/10] [Windows] Add callback for when next frame is drawn --- .../windows/client_wrapper/flutter_engine.cc | 12 +++++ .../flutter_engine_unittests.cc | 33 ++++++++++++++ .../include/flutter/flutter_engine.h | 9 ++++ .../testing/stub_flutter_windows_api.cc | 8 ++++ .../testing/stub_flutter_windows_api.h | 3 ++ shell/platform/windows/fixtures/main.dart | 27 +++++++++++ shell/platform/windows/flutter_windows.cc | 7 +++ .../windows/flutter_windows_engine.cc | 15 +++++++ .../platform/windows/flutter_windows_engine.h | 6 +++ .../flutter_windows_engine_unittests.cc | 15 +++++++ .../windows/flutter_windows_unittests.cc | 45 +++++++++++++++++++ .../platform/windows/public/flutter_windows.h | 11 +++++ 12 files changed, 191 insertions(+) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index f35c20cd9e5a2..5daf7d6165474 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -88,6 +88,18 @@ FlutterDesktopPluginRegistrarRef FlutterEngine::GetRegistrarForPlugin( return FlutterDesktopEngineGetPluginRegistrar(engine_, plugin_name.c_str()); } +void FlutterEngine::SetNextFrameCallback(const std::function& callback) { + next_frame_callback_ = callback; + FlutterDesktopEngineSetNextFrameCallback( + engine_, + [](void* user_data) { + FlutterEngine* that = static_cast(user_data); + that->next_frame_callback_(); + that->next_frame_callback_ = nullptr; + }, + this); +} + FlutterDesktopEngineRef FlutterEngine::RelinquishEngine() { owns_engine_ = false; return engine_; diff --git a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc index 6f09bb960aac5..a1274998eadce 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc @@ -46,6 +46,13 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { // |flutter::testing::StubFlutterWindowsApi| uint64_t EngineProcessMessages() override { return 99; } + // |flutter::testing::StubFlutterWindowsApi| + void EngineSetNextFrameCallback(VoidCallback callback, + void* user_data) override { + next_frame_callback_ = callback; + next_frame_user_data_ = user_data; + } + // |flutter::testing::StubFlutterWindowsApi| void EngineReloadSystemFonts() override { reload_fonts_called_ = true; } @@ -61,12 +68,20 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { return dart_entrypoint_arguments_; } + bool has_next_frame_callback() { return next_frame_callback_ != nullptr; } + void run_next_frame_callback() { + next_frame_callback_(next_frame_user_data_); + next_frame_callback_ = nullptr; + } + private: bool create_called_ = false; bool run_called_ = false; bool destroy_called_ = false; bool reload_fonts_called_ = false; std::vector dart_entrypoint_arguments_; + VoidCallback next_frame_callback_ = nullptr; + void* next_frame_user_data_ = nullptr; }; } // namespace @@ -168,4 +183,22 @@ TEST(FlutterEngineTest, DartEntrypointArgs) { EXPECT_TRUE(arguments[1] == arguments_ref[1]); } +TEST(FlutterEngineTest, SetNextFrameCallback) { + DartProject project(L"data"); + testing::ScopedStubFlutterWindowsApi scoped_api_stub( + std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + + FlutterEngine engine(DartProject(L"fake/project/path")); + + bool success = false; + engine.SetNextFrameCallback([&success]() { success = true; }); + + EXPECT_TRUE(test_api->has_next_frame_callback()); + + test_api->run_next_frame_callback(); + + EXPECT_TRUE(success); +} + } // namespace flutter diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index 897fccfe0eb94..7a337e8d8610e 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -78,6 +78,12 @@ class FlutterEngine : public PluginRegistry { // This pointer will remain valid for the lifetime of this instance. BinaryMessenger* messenger() { return messenger_.get(); } + // Schedule a callback to be called after the next frame is drawn. + // + // This must be called from the platform thread. The callback is executed only + // once on the platform thread. + void SetNextFrameCallback(const std::function& callback); + private: // For access to RelinquishEngine. friend class FlutterViewController; @@ -101,6 +107,9 @@ class FlutterEngine : public PluginRegistry { // or if RelinquishEngine has been called (since the view controller will // run the engine if it hasn't already been run). bool has_been_run_ = false; + + // The callback to execute once the next frame is drawn. + std::function next_frame_callback_ = nullptr; }; } // namespace flutter diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index a829326a618c0..488cc49b8935b 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -111,6 +111,14 @@ uint64_t FlutterDesktopEngineProcessMessages(FlutterDesktopEngineRef engine) { return 0; } +void FlutterDesktopEngineSetNextFrameCallback(FlutterDesktopEngineRef engine, + VoidCallback callback, + void* user_data) { + if (s_stub_implementation) { + s_stub_implementation->EngineSetNextFrameCallback(callback, user_data); + } +} + void FlutterDesktopEngineReloadSystemFonts(FlutterDesktopEngineRef engine) { if (s_stub_implementation) { s_stub_implementation->EngineReloadSystemFonts(); diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index 125fb3614fbec..20df2f57f5b78 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -62,6 +62,9 @@ class StubFlutterWindowsApi { // Called for FlutterDesktopEngineProcessMessages. virtual uint64_t EngineProcessMessages() { return 0; } + virtual void EngineSetNextFrameCallback(VoidCallback callback, + void* user_data) {} + // Called for FlutterDesktopEngineReloadSystemFonts. virtual void EngineReloadSystemFonts() {} diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart index 2799c2f47ecf5..e4de5e4d08018 100644 --- a/shell/platform/windows/fixtures/main.dart +++ b/shell/platform/windows/fixtures/main.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui' as ui; + // Signals a waiting latch in the native test. void signal() native 'Signal'; @@ -33,3 +35,28 @@ void verifyNativeFunctionWithReturn() { bool value = signalBoolReturn(); signalBoolValue(value); } + +@pragma('vm:entry-point') +void drawHelloWorld() { + ui.PlatformDispatcher.instance.onBeginFrame = (Duration duration) { + final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle()) + ..addText('Hello world'); + final ui.Paragraph paragraph = paragraphBuilder.build(); + + paragraph.layout(const ui.ParagraphConstraints(width: 800.0)); + + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + + canvas.drawParagraph(paragraph, ui.Offset.zero); + + final ui.Picture picture = recorder.endRecording(); + final ui.SceneBuilder sceneBuilder = ui.SceneBuilder() + ..addPicture(ui.Offset.zero, picture) + ..pop(); + + ui.window.render(sceneBuilder.build()); + }; + + ui.PlatformDispatcher.instance.scheduleFrame(); +} \ No newline at end of file diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 87c6c8bd55df4..c26157e7c71c7 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -176,6 +176,13 @@ FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar( EngineFromHandle(engine)->texture_registrar()); } +void FlutterDesktopEngineSetNextFrameCallback(FlutterDesktopEngineRef engine, + VoidCallback callback, + void* user_data) { + EngineFromHandle(engine)->SetNextFrameCallback( + [callback, user_data]() { callback(user_data); }); +} + HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view) { return ViewFromHandle(view)->GetPlatformWindow(); } diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 972b9bb94b254..d3b91af10508e 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -506,6 +506,21 @@ void FlutterWindowsEngine::ScheduleFrame() { embedder_api_.ScheduleFrame(engine_); } +void FlutterWindowsEngine::SetNextFrameCallback(const fml::closure& callback) { + next_frame_callback_ = callback; + + embedder_api_.SetNextFrameCallback( + engine_, + [](void* user_data) { + // Embedder callback runs on raster thread. Switch back to platform thread. + FlutterWindowsEngine* that = + static_cast(user_data); + + that->task_runner_->PostTask(std::move(that->next_frame_callback_)); + }, + this); +} + void FlutterWindowsEngine::SendSystemLocales() { std::vector languages = GetPreferredLanguageInfo(); std::vector flutter_locales; diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index c3c0d6109d922..ad554d0fbcc84 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -177,6 +177,9 @@ class FlutterWindowsEngine { // Informs the engine that a new frame is needed to redraw the content. void ScheduleFrame(); + // Set the callback that is called when the next frame is drawn. + void SetNextFrameCallback(const fml::closure& callback); + // Attempts to register the texture with the given |texture_id|. bool RegisterExternalTexture(int64_t texture_id); @@ -294,6 +297,9 @@ class FlutterWindowsEngine { // The root isolate creation callback. fml::closure root_isolate_create_callback_; + + // The on frame drawn callback. + fml::closure next_frame_callback_; }; } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 191740f213ebd..87315efad58f5 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -389,5 +389,20 @@ TEST(FlutterWindowsEngine, ScheduleFrame) { EXPECT_TRUE(called); } +TEST(FlutterWindowsEngine, SetNextFrameCallback) { + std::unique_ptr engine = GetTestEngine(); + EngineModifier modifier(engine.get()); + + bool called = false; + modifier.embedder_api().SetNextFrameCallback = MOCK_ENGINE_PROC( + SetNextFrameCallback, ([&called](auto engine, auto callback, auto data) { + called = true; + return kSuccess; + })); + + engine->SetNextFrameCallback([]() {}); + EXPECT_TRUE(called); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_unittests.cc b/shell/platform/windows/flutter_windows_unittests.cc index 4479e4cd02617..00fb00308b436 100644 --- a/shell/platform/windows/flutter_windows_unittests.cc +++ b/shell/platform/windows/flutter_windows_unittests.cc @@ -149,5 +149,50 @@ TEST_F(WindowsTest, VerifyNativeFunctionWithReturn) { EXPECT_TRUE(bool_value_passed); } +// Verify the next frame callback is executed. +TEST_F(WindowsTest, NextFrameCallback) { + struct Captures { + fml::AutoResetWaitableEvent latch; + std::thread::id thread_id; + }; + Captures captures; + + CreateNewThread("test_platform_thread")->PostTask([&]() { + captures.thread_id = std::this_thread::get_id(); + + auto& context = GetContext(); + WindowsConfigBuilder builder(context); + builder.SetDartEntrypoint("drawHelloWorld"); + + ViewControllerPtr controller{builder.Run()}; + ASSERT_NE(controller, nullptr); + + auto engine = FlutterDesktopViewControllerGetEngine(controller.get()); + + FlutterDesktopEngineSetNextFrameCallback( + engine, + [](void* user_data) { + auto captures = static_cast(user_data); + + // Callback should execute on platform thread. + ASSERT_EQ(std::this_thread::get_id(), captures->thread_id); + + // Signal the test passed and end the Windows message loop. + captures->latch.Signal(); + ::PostQuitMessage(0); + }, + &captures); + + // Pump messages for the Windows platform runner. + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + }); + + captures.latch.Wait(); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h index ca1655f1e9f40..c5306beba0730 100644 --- a/shell/platform/windows/public/flutter_windows.h +++ b/shell/platform/windows/public/flutter_windows.h @@ -17,6 +17,8 @@ extern "C" { #endif +typedef void (*VoidCallback)(void* /* user data */); + // Opaque reference to a Flutter window controller. typedef struct FlutterDesktopViewControllerState* FlutterDesktopViewControllerRef; @@ -185,6 +187,15 @@ FlutterDesktopEngineGetMessenger(FlutterDesktopEngineRef engine); FLUTTER_EXPORT FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar(FlutterDesktopEngineRef engine); +// Schedule a callback to be called after the next frame is drawn. +// +// This must be called from the platform thread. The callback is executed only +// once on the platform thread. +FLUTTER_EXPORT void FlutterDesktopEngineSetNextFrameCallback( + FlutterDesktopEngineRef engine, + VoidCallback callback, + void* user_data); + // ========== View ========== // Return backing HWND for manipulation in host application. From 5e454aad1f999d3f8a30a27d9149b773bed41053 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 11:38:28 -0700 Subject: [PATCH 02/10] Add comment --- .../windows/client_wrapper/testing/stub_flutter_windows_api.h | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index 20df2f57f5b78..adc117a838f18 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -62,6 +62,7 @@ class StubFlutterWindowsApi { // Called for FlutterDesktopEngineProcessMessages. virtual uint64_t EngineProcessMessages() { return 0; } + // Called for FlutterDesktopEngineSetNextFrameCallback. virtual void EngineSetNextFrameCallback(VoidCallback callback, void* user_data) {} From e98ed646870f49296aa63fd2e37b4c01465140a9 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 11:43:18 -0700 Subject: [PATCH 03/10] Improve comment --- shell/platform/windows/flutter_windows_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/flutter_windows_unittests.cc b/shell/platform/windows/flutter_windows_unittests.cc index 00fb00308b436..e378a78428d61 100644 --- a/shell/platform/windows/flutter_windows_unittests.cc +++ b/shell/platform/windows/flutter_windows_unittests.cc @@ -183,7 +183,7 @@ TEST_F(WindowsTest, NextFrameCallback) { }, &captures); - // Pump messages for the Windows platform runner. + // Pump messages for the Windows platform task runner. ::MSG msg; while (::GetMessage(&msg, nullptr, 0, 0)) { ::TranslateMessage(&msg); From 694acbddc3177e21384f518f721febaa8ca277be Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 13:21:01 -0700 Subject: [PATCH 04/10] Fix formatting issue --- shell/platform/windows/client_wrapper/flutter_engine.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 5daf7d6165474..89ad0a7b8a16c 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -88,7 +88,8 @@ FlutterDesktopPluginRegistrarRef FlutterEngine::GetRegistrarForPlugin( return FlutterDesktopEngineGetPluginRegistrar(engine_, plugin_name.c_str()); } -void FlutterEngine::SetNextFrameCallback(const std::function& callback) { +void FlutterEngine::SetNextFrameCallback( + const std::function& callback) { next_frame_callback_ = callback; FlutterDesktopEngineSetNextFrameCallback( engine_, From 326c292529358633c29f06d925e6aa13757cc192 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 13:22:54 -0700 Subject: [PATCH 05/10] Fix second formatting issue --- shell/platform/windows/fixtures/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart index e4de5e4d08018..6d3d10a172589 100644 --- a/shell/platform/windows/fixtures/main.dart +++ b/shell/platform/windows/fixtures/main.dart @@ -59,4 +59,4 @@ void drawHelloWorld() { }; ui.PlatformDispatcher.instance.scheduleFrame(); -} \ No newline at end of file +} From f7ed3b9b4b9739c63690427a9f02f35c9d9c30f0 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 13:31:46 -0700 Subject: [PATCH 06/10] Fix more formatting --- .../windows/client_wrapper/include/flutter/flutter_engine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index 7a337e8d8610e..42a9c6019b8fd 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -79,7 +79,7 @@ class FlutterEngine : public PluginRegistry { BinaryMessenger* messenger() { return messenger_.get(); } // Schedule a callback to be called after the next frame is drawn. - // + // // This must be called from the platform thread. The callback is executed only // once on the platform thread. void SetNextFrameCallback(const std::function& callback); From 8c23308240167c8af8c13576a808f60d5ca0507a Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 13:45:50 -0700 Subject: [PATCH 07/10] Address feedback --- shell/platform/windows/client_wrapper/flutter_engine.cc | 6 +++--- shell/platform/windows/flutter_windows_engine.cc | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 89ad0a7b8a16c..59724141ad8f9 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -94,9 +94,9 @@ void FlutterEngine::SetNextFrameCallback( FlutterDesktopEngineSetNextFrameCallback( engine_, [](void* user_data) { - FlutterEngine* that = static_cast(user_data); - that->next_frame_callback_(); - that->next_frame_callback_ = nullptr; + FlutterEngine* self = static_cast(user_data); + self->next_frame_callback_(); + self->next_frame_callback_ = nullptr; }, this); } diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index d3b91af10508e..ae6ce8124ca25 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -513,10 +513,10 @@ void FlutterWindowsEngine::SetNextFrameCallback(const fml::closure& callback) { engine_, [](void* user_data) { // Embedder callback runs on raster thread. Switch back to platform thread. - FlutterWindowsEngine* that = + FlutterWindowsEngine* self = static_cast(user_data); - that->task_runner_->PostTask(std::move(that->next_frame_callback_)); + self->task_runner_->PostTask(std::move(self->next_frame_callback_)); }, this); } From b74309ce81f1ebc53228c9ac7c6868af5637f63e Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 14:50:58 -0700 Subject: [PATCH 08/10] Prefer move over const ref --- shell/platform/windows/client_wrapper/flutter_engine.cc | 5 ++--- .../windows/client_wrapper/include/flutter/flutter_engine.h | 2 +- shell/platform/windows/flutter_windows_engine.cc | 4 ++-- shell/platform/windows/flutter_windows_engine.h | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 59724141ad8f9..00ccbbcff7cab 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -88,9 +88,8 @@ FlutterDesktopPluginRegistrarRef FlutterEngine::GetRegistrarForPlugin( return FlutterDesktopEngineGetPluginRegistrar(engine_, plugin_name.c_str()); } -void FlutterEngine::SetNextFrameCallback( - const std::function& callback) { - next_frame_callback_ = callback; +void FlutterEngine::SetNextFrameCallback(std::function callback) { + next_frame_callback_ = std::move(callback); FlutterDesktopEngineSetNextFrameCallback( engine_, [](void* user_data) { diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index 42a9c6019b8fd..63a820ce2d6bd 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -82,7 +82,7 @@ class FlutterEngine : public PluginRegistry { // // This must be called from the platform thread. The callback is executed only // once on the platform thread. - void SetNextFrameCallback(const std::function& callback); + void SetNextFrameCallback(std::function callback); private: // For access to RelinquishEngine. diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index ae6ce8124ca25..7e08f0071bf51 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -506,8 +506,8 @@ void FlutterWindowsEngine::ScheduleFrame() { embedder_api_.ScheduleFrame(engine_); } -void FlutterWindowsEngine::SetNextFrameCallback(const fml::closure& callback) { - next_frame_callback_ = callback; +void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) { + next_frame_callback_ = std::move(callback); embedder_api_.SetNextFrameCallback( engine_, diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index ad554d0fbcc84..3ead42238f616 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -178,7 +178,7 @@ class FlutterWindowsEngine { void ScheduleFrame(); // Set the callback that is called when the next frame is drawn. - void SetNextFrameCallback(const fml::closure& callback); + void SetNextFrameCallback(fml::closure callback); // Attempts to register the texture with the given |texture_id|. bool RegisterExternalTexture(int64_t texture_id); From 34c2d3c4df8c30916166c3f4e557bea025a71861 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 15:18:36 -0700 Subject: [PATCH 09/10] Check frame is scheduled before frame is drawn --- shell/platform/windows/fixtures/main.dart | 4 ++++ .../platform/windows/flutter_windows_unittests.cc | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart index 6d3d10a172589..e2d76aa6171e7 100644 --- a/shell/platform/windows/fixtures/main.dart +++ b/shell/platform/windows/fixtures/main.dart @@ -13,6 +13,9 @@ void signalBoolValue(bool value) native 'SignalBoolValue'; // Signals a waiting latch in the native test, which returns a value to the fixture. bool signalBoolReturn() native 'SignalBoolReturn'; +// Notify the native test that the first frame has been scheduled. +void notifyFirstFrameScheduled() native 'NotifyFirstFrameScheduled'; + void main() { } @@ -59,4 +62,5 @@ void drawHelloWorld() { }; ui.PlatformDispatcher.instance.scheduleFrame(); + notifyFirstFrameScheduled(); } diff --git a/shell/platform/windows/flutter_windows_unittests.cc b/shell/platform/windows/flutter_windows_unittests.cc index e378a78428d61..720c7b3fbc0a7 100644 --- a/shell/platform/windows/flutter_windows_unittests.cc +++ b/shell/platform/windows/flutter_windows_unittests.cc @@ -152,7 +152,8 @@ TEST_F(WindowsTest, VerifyNativeFunctionWithReturn) { // Verify the next frame callback is executed. TEST_F(WindowsTest, NextFrameCallback) { struct Captures { - fml::AutoResetWaitableEvent latch; + fml::AutoResetWaitableEvent frame_scheduled_latch; + fml::AutoResetWaitableEvent frame_drawn_latch; std::thread::id thread_id; }; Captures captures; @@ -164,6 +165,12 @@ TEST_F(WindowsTest, NextFrameCallback) { WindowsConfigBuilder builder(context); builder.SetDartEntrypoint("drawHelloWorld"); + auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + ASSERT_FALSE(captures.frame_drawn_latch.IsSignaledForTest()); + captures.frame_scheduled_latch.Signal(); + }); + context.AddNativeFunction("NotifyFirstFrameScheduled", native_entry); + ViewControllerPtr controller{builder.Run()}; ASSERT_NE(controller, nullptr); @@ -174,11 +181,13 @@ TEST_F(WindowsTest, NextFrameCallback) { [](void* user_data) { auto captures = static_cast(user_data); + ASSERT_TRUE(captures->frame_scheduled_latch.IsSignaledForTest()); + // Callback should execute on platform thread. ASSERT_EQ(std::this_thread::get_id(), captures->thread_id); // Signal the test passed and end the Windows message loop. - captures->latch.Signal(); + captures->frame_drawn_latch.Signal(); ::PostQuitMessage(0); }, &captures); @@ -191,7 +200,7 @@ TEST_F(WindowsTest, NextFrameCallback) { } }); - captures.latch.Wait(); + captures.frame_drawn_latch.Wait(); } } // namespace testing From 8bab3ff53ad3c2a48342478cd7341d148cd37371 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Mon, 15 Aug 2022 15:28:45 -0700 Subject: [PATCH 10/10] Format --- shell/platform/windows/flutter_windows_engine.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 7e08f0071bf51..6c3d728f7e4ff 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -512,7 +512,8 @@ void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) { embedder_api_.SetNextFrameCallback( engine_, [](void* user_data) { - // Embedder callback runs on raster thread. Switch back to platform thread. + // Embedder callback runs on raster thread. Switch back to platform + // thread. FlutterWindowsEngine* self = static_cast(user_data);