diff --git a/shell/common/rasterizer.cc b/shell/common/rasterizer.cc index d121b90527fab..db5ef54055cc8 100644 --- a/shell/common/rasterizer.cc +++ b/shell/common/rasterizer.cc @@ -641,7 +641,7 @@ RasterStatus Rasterizer::DrawToSurfaceUnsafe( frame_timings_recorder.RecordRasterStart(fml::TimePoint::Now()); std::unique_ptr damage; - // when leaf layer tracing is enabled we wish to repaing the whole frame for + // when leaf layer tracing is enabled we wish to repaint the whole frame for // accurate performance metrics. if (frame->framebuffer_info().supports_partial_repaint && !layer_tree.is_leaf_layer_tracing_enabled()) { diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index d69507b5f4c24..0868901fce3d6 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -2683,6 +2683,36 @@ FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) "Could not schedule frame."); } +FlutterEngineResult FlutterEngineSetNextFrameCallback( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + VoidCallback callback, + void* user_data) { + if (engine == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, "Invalid engine handle."); + } + + if (callback == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, + "Next frame callback was null."); + } + + flutter::EmbedderEngine* embedder_engine = + reinterpret_cast(engine); + + fml::WeakPtr weak_platform_view = + embedder_engine->GetShell().GetPlatformView(); + + if (!weak_platform_view) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency, + "Platform view unavailable."); + } + + weak_platform_view->SetNextFrameCallback( + [callback, user_data]() { callback(user_data); }); + + return kSuccess; +} + FlutterEngineResult FlutterEngineGetProcAddresses( FlutterEngineProcTable* table) { if (!table) { @@ -2734,6 +2764,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses( FlutterEnginePostCallbackOnAllNativeThreads); SET_PROC(NotifyDisplayUpdate, FlutterEngineNotifyDisplayUpdate); SET_PROC(ScheduleFrame, FlutterEngineScheduleFrame); + SET_PROC(SetNextFrameCallback, FlutterEngineSetNextFrameCallback); #undef SET_PROC return kSuccess; diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 43fd5bd5b1cde..88a3014f0218f 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -2510,6 +2510,26 @@ FLUTTER_EXPORT FlutterEngineResult FlutterEngineScheduleFrame(FLUTTER_API_SYMBOL(FlutterEngine) engine); +//------------------------------------------------------------------------------ +/// @brief 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 from the raster thread; embedders must +/// re-thread if necessary. Performing blocking calls +/// in this callback may introduce application jank. +/// +/// @param[in] engine A running engine instance. +/// @param[in] callback The callback to execute. +/// @param[in] user_data A baton passed by the engine to the callback. This +/// baton is not interpreted by the engine in any way. +/// +/// @return The result of the call. +/// +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineSetNextFrameCallback( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + VoidCallback callback, + void* user_data); + #endif // !FLUTTER_ENGINE_NO_PROTOTYPES // Typedefs for the function pointers in FlutterEngineProcTable. @@ -2628,6 +2648,10 @@ typedef FlutterEngineResult (*FlutterEngineNotifyDisplayUpdateFnPtr)( size_t display_count); typedef FlutterEngineResult (*FlutterEngineScheduleFrameFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine); +typedef FlutterEngineResult (*FlutterEngineSetNextFrameCallbackFnPtr)( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + VoidCallback callback, + void* user_data); /// Function-pointer-based versions of the APIs above. typedef struct { @@ -2673,6 +2697,7 @@ typedef struct { PostCallbackOnAllNativeThreads; FlutterEngineNotifyDisplayUpdateFnPtr NotifyDisplayUpdate; FlutterEngineScheduleFrameFnPtr ScheduleFrame; + FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback; } FlutterEngineProcTable; //------------------------------------------------------------------------------ diff --git a/shell/platform/embedder/tests/embedder_unittests.cc b/shell/platform/embedder/tests/embedder_unittests.cc index d2b0c96b3c99b..74c3782c037fc 100644 --- a/shell/platform/embedder/tests/embedder_unittests.cc +++ b/shell/platform/embedder/tests/embedder_unittests.cc @@ -2126,6 +2126,44 @@ TEST_F(EmbedderTest, CanScheduleFrame) { check_latch.Wait(); } +TEST_F(EmbedderTest, CanSetNextFrameCallback) { + auto& context = GetEmbedderContext(EmbedderTestContextType::kSoftwareContext); + EmbedderConfigBuilder builder(context); + builder.SetSoftwareRendererConfig(); + builder.SetDartEntrypoint("draw_solid_red"); + + auto engine = builder.LaunchEngine(); + ASSERT_TRUE(engine.is_valid()); + + // Register the callback that is executed once the next frame is drawn. + fml::AutoResetWaitableEvent callback_latch; + VoidCallback callback = [](void* user_data) { + fml::AutoResetWaitableEvent* callback_latch = + static_cast(user_data); + + callback_latch->Signal(); + }; + + auto result = FlutterEngineSetNextFrameCallback(engine.get(), callback, + &callback_latch); + ASSERT_EQ(result, kSuccess); + + // Send a window metrics events so frames may be scheduled. + FlutterWindowMetricsEvent event = {}; + event.struct_size = sizeof(event); + event.width = 800; + event.height = 600; + event.pixel_ratio = 1.0; + event.physical_view_inset_top = 0.0; + event.physical_view_inset_right = 0.0; + event.physical_view_inset_bottom = 0.0; + event.physical_view_inset_left = 0.0; + ASSERT_EQ(FlutterEngineSendWindowMetricsEvent(engine.get(), &event), + kSuccess); + + callback_latch.Wait(); +} + #if defined(FML_OS_MACOSX) static void MockThreadConfigSetter(const fml::Thread::ThreadConfig& config) { 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 464c233fa9bfa..688361740ebc8 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -22,7 +22,8 @@ namespace flutter { // // In the future, this will be the API surface used for all interactions with // the engine, rather than having them duplicated on FlutterViewController. -// For now it is only used in the rare where you need a headless Flutter engine. +// For now it is only used in the rare case where you need a headless Flutter +// engine. class FlutterEngine : public PluginRegistry { public: // Creates a new engine for running the given project.