Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 67 additions & 6 deletions shell/platform/linux/fl_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <gmodule.h>

#include <atomic>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leftover, will remove it later.

#include <cstring>

#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
Expand All @@ -20,6 +21,8 @@
// Unique number associated with platform tasks.
static constexpr size_t kPlatformTaskRunnerIdentifier = 1;

static constexpr int kMicrosecondsPerNanosecond = 1000;

struct _FlEngine {
GObject parent_instance;

Expand All @@ -44,6 +47,9 @@ struct _FlEngine {
FlEngineUpdateSemanticsNodeHandler update_semantics_node_handler;
gpointer update_semantics_node_handler_data;
GDestroyNotify update_semantics_node_handler_destroy_notify;

// Stored baton for plumbing vsync callbacks.
intptr_t vsync_baton;
};

G_DEFINE_QUARK(fl_engine_error_quark, fl_engine_error)
Expand Down Expand Up @@ -214,6 +220,57 @@ static bool fl_engine_gl_make_resource_current(void* user_data) {
return result;
}

static void fl_engine_handle_frame_clock_update(GdkFrameClock* clk,
void* user_data) {
FlEngine* self = static_cast<FlEngine*>(user_data);
if (self->vsync_baton != 0) {
// Note: it's crucial to reset the vsync_baton before we call OnVsync, since
// OnVsync (either synchronous or ran on another thread) might request
// another vsync callback inside it.
auto btn = self->vsync_baton;
self->vsync_baton = 0;
gint64 frame_time = gdk_frame_clock_get_frame_time(clk);
gint64 refresh_interval;
gint64 presentation_time;
gdk_frame_clock_get_refresh_info(clk, frame_time, &refresh_interval,
&presentation_time);
if (presentation_time == 0) {
// GDK could not predict next presentation due to lack of history.
// A fallback is used.
presentation_time = frame_time + refresh_interval;
}
self->embedder_api.OnVsync(self->engine, btn,
frame_time * kMicrosecondsPerNanosecond,
presentation_time * kMicrosecondsPerNanosecond);
}
}

static gboolean fl_engine_request_vsync(FlEngine* self) {
GdkFrameClock* clk = gtk_widget_get_frame_clock(
GTK_WIDGET(fl_renderer_get_view(self->renderer)));
if (fl_renderer_is_blocking_main_thread(self->renderer)) {
// Layout updates happens inside the "layout" phase of frame clock; if
// blocking is in progress, then we never advance to the next "update" phase
// where the vsync callback is usually called. Hence we just issue the
// callback immediately if blocking is in progress.
fl_engine_handle_frame_clock_update(clk, self);
} else {
gdk_frame_clock_request_phase(clk, GDK_FRAME_CLOCK_PHASE_UPDATE);
}
return false;
}

static void fl_engine_vsync_callback(void* user_data, intptr_t btn) {
FlEngine* self = static_cast<FlEngine*>(user_data);
// Thread safety: only one of vsync_callback or handle_frame_clock_update can
// execute at one time. This is because VSync callback can only be requested
// upon the completion of the previous VSync callback.
self->vsync_baton = btn;
// Run frame clock operations on the main thread to synchronize the accesses.
std::function<void()> delegate = [=] { fl_engine_request_vsync(self); };
fl_task_runner_post_task(self->task_runner, std::move(delegate), 0);
}

// Called by the engine to determine if it is on the GTK thread.
static bool fl_engine_runs_task_on_current_thread(void* user_data) {
FlEngine* self = static_cast<FlEngine*>(user_data);
Expand All @@ -225,8 +282,11 @@ static void fl_engine_post_task(FlutterTask task,
uint64_t target_time_nanos,
void* user_data) {
FlEngine* self = static_cast<FlEngine*>(user_data);
std::function<void()> delegate = [=] {
self->embedder_api.RunTask(self->engine, &task);
};

fl_task_runner_post_task(self->task_runner, task, target_time_nanos);
fl_task_runner_post_task(self->task_runner, delegate, target_time_nanos);
}

// Called when a platform message is received from the engine.
Expand Down Expand Up @@ -326,6 +386,7 @@ static void fl_engine_class_init(FlEngineClass* klass) {

static void fl_engine_init(FlEngine* self) {
self->thread = g_thread_self();
self->vsync_baton = 0;

self->embedder_api.struct_size = sizeof(FlutterEngineProcTable);
FlutterEngineGetProcAddresses(&self->embedder_api);
Expand Down Expand Up @@ -401,6 +462,11 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
dart_entrypoint_args != nullptr ? g_strv_length(dart_entrypoint_args) : 0;
args.dart_entrypoint_argv =
reinterpret_cast<const char* const*>(dart_entrypoint_args);
args.vsync_callback = fl_engine_vsync_callback;
GdkFrameClock* clk = gtk_widget_get_frame_clock(
GTK_WIDGET(fl_renderer_get_view(self->renderer)));
g_signal_connect(clk, "update",
G_CALLBACK(fl_engine_handle_frame_clock_update), self);

FlutterCompositor compositor = {};
compositor.struct_size = sizeof(FlutterCompositor);
Expand Down Expand Up @@ -683,8 +749,3 @@ FlTaskRunner* fl_engine_get_task_runner(FlEngine* self) {
g_return_val_if_fail(FL_IS_ENGINE(self), nullptr);
return self->task_runner;
}

void fl_engine_execute_task(FlEngine* self, FlutterTask* task) {
g_return_if_fail(FL_IS_ENGINE(self));
self->embedder_api.RunTask(self->engine, task);
}
9 changes: 0 additions & 9 deletions shell/platform/linux/fl_engine_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,15 +244,6 @@ GBytes* fl_engine_send_platform_message_finish(FlEngine* engine,
*/
FlTaskRunner* fl_engine_get_task_runner(FlEngine* engine);

/**
* fl_engine_execute_task:
* @engine: an #FlEngine.
* @task: a #FlutterTask to execute.
*
* Executes given Flutter task.
*/
void fl_engine_execute_task(FlEngine* engine, FlutterTask* task);

G_END_DECLS

#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_
7 changes: 7 additions & 0 deletions shell/platform/linux/fl_renderer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,10 @@ gboolean fl_renderer_present_layers(FlRenderer* self,
return FL_RENDERER_GET_CLASS(self)->present_layers(self, layers,
layers_count);
}

gboolean fl_renderer_is_blocking_main_thread(FlRenderer* self) {
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
fl_renderer_get_instance_private(self));

return priv->blocking_main_thread;
}
9 changes: 9 additions & 0 deletions shell/platform/linux/fl_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,15 @@ void fl_renderer_wait_for_frame(FlRenderer* renderer,
int target_width,
int target_height);

/**
* fl_renderer_is_blocking_main_thread:
* @renderer: an #FlRenderer.
*
* Returns %TRUE if we are currently blocking the main thread waiting for a
* frame to arrive.
*/
gboolean fl_renderer_is_blocking_main_thread(FlRenderer* self);

G_END_DECLS

#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_
8 changes: 4 additions & 4 deletions shell/platform/linux/fl_task_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ struct _FlTaskRunner {
typedef struct _FlTaskRunnerTask {
// absolute time of task (based on g_get_monotonic_time)
gint64 task_time_micros;
FlutterTask task;
std::function<void()> delegate;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FlTaskRunnerTask is freed using g_free. The implicit c++ destructor is not called at any point. So you'll leak any heap allocation that std::function might have. Again, it would be better to just have a callback and gpointer instead.

} FlTaskRunnerTask;

G_DEFINE_TYPE(FlTaskRunner, fl_task_runner, G_TYPE_OBJECT)
Expand Down Expand Up @@ -54,7 +54,7 @@ static void fl_task_runner_process_expired_tasks_locked(FlTaskRunner* self) {
l = expired_tasks;
while (l != nullptr && self->engine) {
FlTaskRunnerTask* task = static_cast<FlTaskRunnerTask*>(l->data);
fl_engine_execute_task(self->engine, &task->task);
task->delegate();
l = l->next;
}

Expand Down Expand Up @@ -167,13 +167,13 @@ FlTaskRunner* fl_task_runner_new(FlEngine* engine) {
}

void fl_task_runner_post_task(FlTaskRunner* self,
FlutterTask task,
std::function<void()> delegate,
uint64_t target_time_nanos) {
g_autoptr(GMutexLocker) locker = g_mutex_locker_new(&self->mutex);
(void)locker; // unused variable

FlTaskRunnerTask* runner_task = g_new0(FlTaskRunnerTask, 1);
runner_task->task = task;
runner_task->delegate = std::move(delegate);
runner_task->task_time_micros =
target_time_nanos / kMicrosecondsPerNanosecond;

Expand Down
5 changes: 3 additions & 2 deletions shell/platform/linux/fl_task_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TASK_RUNNER_H_

#include <glib-object.h>
#include <functional>

#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
Expand All @@ -27,14 +28,14 @@ FlTaskRunner* fl_task_runner_new(FlEngine* engine);
/**
* fl_task_runner_post_task:
* @task_runner: an #FlTaskRunner.
* @task: Flutter task being scheduled
* @delegate: The callback to be scheduled
* @target_time_nanos: absolute time in nanoseconds
*
* Posts a Flutter task to be executed on main thread. This function is thread
* safe and may be called from any thread.
*/
void fl_task_runner_post_task(FlTaskRunner* task_runner,
FlutterTask task,
std::function<void()> delegate,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the embedder code is complied as C++, the actual code is C. So I don't think using std::function here is a way to go. You can perhaps replace it with a callback that takes gpointer and a gpointer value.

uint64_t target_time_nanos);

/**
Expand Down