66
77#include < gmodule.h>
88
9+ #include < atomic>
910#include < cstring>
1011
1112#include " flutter/shell/platform/linux/fl_binary_messenger_private.h"
2021// Unique number associated with platform tasks.
2122static constexpr size_t kPlatformTaskRunnerIdentifier = 1 ;
2223
24+ static constexpr int kMicrosecondsPerNanosecond = 1000 ;
25+
2326struct _FlEngine {
2427 GObject parent_instance;
2528
@@ -44,6 +47,9 @@ struct _FlEngine {
4447 FlEngineUpdateSemanticsNodeHandler update_semantics_node_handler;
4548 gpointer update_semantics_node_handler_data;
4649 GDestroyNotify update_semantics_node_handler_destroy_notify;
50+
51+ // Stored baton for plumbing vsync callbacks.
52+ intptr_t vsync_baton;
4753};
4854
4955G_DEFINE_QUARK (fl_engine_error_quark, fl_engine_error)
@@ -214,6 +220,57 @@ static bool fl_engine_gl_make_resource_current(void* user_data) {
214220 return result;
215221}
216222
223+ static void fl_engine_handle_frame_clock_update (GdkFrameClock* clk,
224+ void * user_data) {
225+ FlEngine* self = static_cast <FlEngine*>(user_data);
226+ if (self->vsync_baton != 0 ) {
227+ // Note: it's crucial to reset the vsync_baton before we call OnVsync, since
228+ // OnVsync (either synchronous or ran on another thread) might request
229+ // another vsync callback inside it.
230+ auto btn = self->vsync_baton ;
231+ self->vsync_baton = 0 ;
232+ gint64 frame_time = gdk_frame_clock_get_frame_time (clk);
233+ gint64 refresh_interval;
234+ gint64 presentation_time;
235+ gdk_frame_clock_get_refresh_info (clk, frame_time, &refresh_interval,
236+ &presentation_time);
237+ if (presentation_time == 0 ) {
238+ // GDK could not predict next presentation due to lack of history.
239+ // A fallback is used.
240+ presentation_time = frame_time + refresh_interval;
241+ }
242+ self->embedder_api .OnVsync (self->engine , btn,
243+ frame_time * kMicrosecondsPerNanosecond ,
244+ presentation_time * kMicrosecondsPerNanosecond );
245+ }
246+ }
247+
248+ static gboolean fl_engine_request_vsync (FlEngine* self) {
249+ GdkFrameClock* clk = gtk_widget_get_frame_clock (
250+ GTK_WIDGET (fl_renderer_get_view (self->renderer )));
251+ if (fl_renderer_is_blocking_main_thread (self->renderer )) {
252+ // Layout updates happens inside the "layout" phase of frame clock; if
253+ // blocking is in progress, then we never advance to the next "update" phase
254+ // where the vsync callback is usually called. Hence we just issue the
255+ // callback immediately if blocking is in progress.
256+ fl_engine_handle_frame_clock_update (clk, self);
257+ } else {
258+ gdk_frame_clock_request_phase (clk, GDK_FRAME_CLOCK_PHASE_UPDATE);
259+ }
260+ return false ;
261+ }
262+
263+ static void fl_engine_vsync_callback (void * user_data, intptr_t btn) {
264+ FlEngine* self = static_cast <FlEngine*>(user_data);
265+ // Thread safety: only one of vsync_callback or handle_frame_clock_update can
266+ // execute at one time. This is because VSync callback can only be requested
267+ // upon the completion of the previous VSync callback.
268+ self->vsync_baton = btn;
269+ // Run frame clock operations on the main thread to synchronize the accesses.
270+ std::function<void ()> delegate = [=] { fl_engine_request_vsync (self); };
271+ fl_task_runner_post_task (self->task_runner , std::move (delegate), 0 );
272+ }
273+
217274// Called by the engine to determine if it is on the GTK thread.
218275static bool fl_engine_runs_task_on_current_thread (void * user_data) {
219276 FlEngine* self = static_cast <FlEngine*>(user_data);
@@ -225,8 +282,11 @@ static void fl_engine_post_task(FlutterTask task,
225282 uint64_t target_time_nanos,
226283 void * user_data) {
227284 FlEngine* self = static_cast <FlEngine*>(user_data);
285+ std::function<void ()> delegate = [=] {
286+ self->embedder_api .RunTask (self->engine , &task);
287+ };
228288
229- fl_task_runner_post_task (self->task_runner , task , target_time_nanos);
289+ fl_task_runner_post_task (self->task_runner , delegate , target_time_nanos);
230290}
231291
232292// Called when a platform message is received from the engine.
@@ -326,6 +386,7 @@ static void fl_engine_class_init(FlEngineClass* klass) {
326386
327387static void fl_engine_init (FlEngine* self) {
328388 self->thread = g_thread_self ();
389+ self->vsync_baton = 0 ;
329390
330391 self->embedder_api .struct_size = sizeof (FlutterEngineProcTable);
331392 FlutterEngineGetProcAddresses (&self->embedder_api );
@@ -401,6 +462,11 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
401462 dart_entrypoint_args != nullptr ? g_strv_length (dart_entrypoint_args) : 0 ;
402463 args.dart_entrypoint_argv =
403464 reinterpret_cast <const char * const *>(dart_entrypoint_args);
465+ args.vsync_callback = fl_engine_vsync_callback;
466+ GdkFrameClock* clk = gtk_widget_get_frame_clock (
467+ GTK_WIDGET (fl_renderer_get_view (self->renderer )));
468+ g_signal_connect (clk, " update" ,
469+ G_CALLBACK (fl_engine_handle_frame_clock_update), self);
404470
405471 FlutterCompositor compositor = {};
406472 compositor.struct_size = sizeof (FlutterCompositor);
@@ -683,8 +749,3 @@ FlTaskRunner* fl_engine_get_task_runner(FlEngine* self) {
683749 g_return_val_if_fail (FL_IS_ENGINE (self), nullptr );
684750 return self->task_runner ;
685751}
686-
687- void fl_engine_execute_task (FlEngine* self, FlutterTask* task) {
688- g_return_if_fail (FL_IS_ENGINE (self));
689- self->embedder_api .RunTask (self->engine , task);
690- }
0 commit comments