|
6 | 6 |
|
7 | 7 | #include <gmodule.h>
|
8 | 8 |
|
| 9 | +#include <atomic> |
9 | 10 | #include <cstring>
|
10 | 11 |
|
11 | 12 | #include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
|
@@ -44,6 +45,9 @@ struct _FlEngine {
|
44 | 45 | FlEngineUpdateSemanticsNodeHandler update_semantics_node_handler;
|
45 | 46 | gpointer update_semantics_node_handler_data;
|
46 | 47 | GDestroyNotify update_semantics_node_handler_destroy_notify;
|
| 48 | + |
| 49 | + // Stored baton for plumbing vsync callbacks. |
| 50 | + intptr_t vsync_baton; |
47 | 51 | };
|
48 | 52 |
|
49 | 53 | G_DEFINE_QUARK(fl_engine_error_quark, fl_engine_error)
|
@@ -214,6 +218,48 @@ static bool fl_engine_gl_make_resource_current(void* user_data) {
|
214 | 218 | return result;
|
215 | 219 | }
|
216 | 220 |
|
| 221 | +static gboolean fl_engine_request_vsync(gpointer user_data) { |
| 222 | + FlEngine* self = static_cast<FlEngine*>(user_data); |
| 223 | + GdkFrameClock* clk = gtk_widget_get_frame_clock( |
| 224 | + GTK_WIDGET(fl_renderer_get_view(self->renderer))); |
| 225 | + gdk_frame_clock_request_phase(clk, GDK_FRAME_CLOCK_PHASE_UPDATE); |
| 226 | + return false; |
| 227 | +} |
| 228 | + |
| 229 | +static void fl_engine_vsync_callback(void* user_data, intptr_t btn) { |
| 230 | + FlEngine* self = static_cast<FlEngine*>(user_data); |
| 231 | + // Thread safety: only one of vsync_callback or handle_frame_clock_update can |
| 232 | + // execute at one time. This is because VSync callback can only be requested |
| 233 | + // upon the completion of the previous VSync callback. |
| 234 | + self->vsync_baton = btn; |
| 235 | + // Run frame clock operations on the main thread to synchronize the accesses. |
| 236 | + g_idle_add(fl_engine_request_vsync, user_data); |
| 237 | +} |
| 238 | + |
| 239 | +static void fl_engine_handle_frame_clock_update(GdkFrameClock* clk, |
| 240 | + void* user_data) { |
| 241 | + FlEngine* self = static_cast<FlEngine*>(user_data); |
| 242 | + if (self->vsync_baton != 0) { |
| 243 | + // Note: it's crucial to reset the vsync_baton before we call OnVsync, since |
| 244 | + // OnVsync (either synchronous or ran on another thread) might request |
| 245 | + // another vsync callback inside it. |
| 246 | + auto btn = self->vsync_baton; |
| 247 | + self->vsync_baton = 0; |
| 248 | + gint64 frame_time = gdk_frame_clock_get_frame_time(clk); |
| 249 | + gint64 refresh_interval; |
| 250 | + gint64 presentation_time; |
| 251 | + gdk_frame_clock_get_refresh_info(clk, frame_time, &refresh_interval, |
| 252 | + &presentation_time); |
| 253 | + if (presentation_time == 0) { |
| 254 | + // GDK could not predict next presentation due to lack of history. |
| 255 | + // A fallback is used. |
| 256 | + presentation_time = frame_time + refresh_interval; |
| 257 | + } |
| 258 | + self->embedder_api.OnVsync(self->engine, btn, frame_time * 1000, |
| 259 | + presentation_time * 1000); |
| 260 | + } |
| 261 | +} |
| 262 | + |
217 | 263 | // Called by the engine to determine if it is on the GTK thread.
|
218 | 264 | static bool fl_engine_runs_task_on_current_thread(void* user_data) {
|
219 | 265 | FlEngine* self = static_cast<FlEngine*>(user_data);
|
@@ -326,6 +372,7 @@ static void fl_engine_class_init(FlEngineClass* klass) {
|
326 | 372 |
|
327 | 373 | static void fl_engine_init(FlEngine* self) {
|
328 | 374 | self->thread = g_thread_self();
|
| 375 | + self->vsync_baton = 0; |
329 | 376 |
|
330 | 377 | self->embedder_api.struct_size = sizeof(FlutterEngineProcTable);
|
331 | 378 | FlutterEngineGetProcAddresses(&self->embedder_api);
|
@@ -401,6 +448,11 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
|
401 | 448 | dart_entrypoint_args != nullptr ? g_strv_length(dart_entrypoint_args) : 0;
|
402 | 449 | args.dart_entrypoint_argv =
|
403 | 450 | reinterpret_cast<const char* const*>(dart_entrypoint_args);
|
| 451 | + args.vsync_callback = fl_engine_vsync_callback; |
| 452 | + GdkFrameClock* clk = gtk_widget_get_frame_clock( |
| 453 | + GTK_WIDGET(fl_renderer_get_view(self->renderer))); |
| 454 | + g_signal_connect(clk, "update", |
| 455 | + G_CALLBACK(fl_engine_handle_frame_clock_update), self); |
404 | 456 |
|
405 | 457 | FlutterCompositor compositor = {};
|
406 | 458 | compositor.struct_size = sizeof(FlutterCompositor);
|
|
0 commit comments