Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Implement smooth resizing for Linux #25884

Merged
merged 14 commits into from
May 7, 2021
Merged
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
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc
FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.cc
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.h
FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.cc
FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.h
FILE: ../../../flutter/shell/platform/linux/fl_value.cc
Expand Down
2 changes: 2 additions & 0 deletions shell/platform/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ source_set("flutter_linux_sources") {
"fl_standard_message_codec.cc",
"fl_standard_method_codec.cc",
"fl_string_codec.cc",
"fl_task_runner.cc",
"fl_task_runner.h",
"fl_text_input_plugin.cc",
"fl_value.cc",
"fl_view.cc",
Expand Down
77 changes: 15 additions & 62 deletions shell/platform/linux/fl_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
#include "flutter/shell/platform/linux/fl_settings_plugin.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"

static constexpr int kMicrosecondsPerNanosecond = 1000;

// Unique number associated with platform tasks.
static constexpr size_t kPlatformTaskRunnerIdentifier = 1;

Expand All @@ -32,6 +30,7 @@ struct _FlEngine {
FlRenderer* renderer;
FlBinaryMessenger* binary_messenger;
FlSettingsPlugin* settings_plugin;
FlTaskRunner* task_runner;
FlutterEngineAOTData aot_data;
FLUTTER_API_SYMBOL(FlutterEngine) engine;
FlutterEngineProcTable embedder_api;
Expand Down Expand Up @@ -59,13 +58,6 @@ G_DEFINE_TYPE_WITH_CODE(
G_IMPLEMENT_INTERFACE(fl_plugin_registry_get_type(),
fl_engine_plugin_registry_iface_init))

// Subclass of GSource that integrates Flutter tasks into the GLib main loop.
typedef struct {
GSource parent;
FlEngine* engine;
FlutterTask task;
} FlutterSource;

// Parse a locale into its components.
static void parse_locale(const gchar* locale,
gchar** language,
Expand Down Expand Up @@ -143,40 +135,6 @@ static void setup_locales(FlEngine* self) {
}
}

// Callback to run a Flutter task in the GLib main loop.
static gboolean flutter_source_dispatch(GSource* source,
GSourceFunc callback,
gpointer user_data) {
FlutterSource* fl_source = reinterpret_cast<FlutterSource*>(source);
FlEngine* self = fl_source->engine;

FlutterEngineResult result =
self->embedder_api.RunTask(self->engine, &fl_source->task);
if (result != kSuccess) {
g_warning("Failed to run Flutter task\n");
}

return G_SOURCE_REMOVE;
}

// Called when the engine is disposed.
static void engine_weak_notify_cb(gpointer user_data,
GObject* where_the_object_was) {
FlutterSource* source = reinterpret_cast<FlutterSource*>(user_data);
source->engine = nullptr;
g_source_destroy(reinterpret_cast<GSource*>(source));
}

// Called when a flutter source completes.
static void flutter_source_finalize(GSource* source) {
FlutterSource* fl_source = reinterpret_cast<FlutterSource*>(source);
if (fl_source->engine != nullptr) {
g_object_weak_unref(G_OBJECT(fl_source->engine), engine_weak_notify_cb,
fl_source);
fl_source->engine = nullptr;
}
}

// Called when engine needs a backing store for a specific #FlutterLayer.
static bool compositor_create_backing_store_callback(
const FlutterBackingStoreConfig* config,
Expand Down Expand Up @@ -204,16 +162,6 @@ static bool compositor_present_layers_callback(const FlutterLayer** layers,
layers_count);
}

// Table of functions for Flutter GLib main loop integration.
static GSourceFuncs flutter_source_funcs = {
nullptr, // prepare
nullptr, // check
flutter_source_dispatch, // dispatch
flutter_source_finalize, // finalize
nullptr,
nullptr // Internal usage
};

// Flutter engine rendering callbacks.

static void* fl_engine_gl_proc_resolver(void* user_data, const char* name) {
Expand Down Expand Up @@ -278,15 +226,7 @@ static void fl_engine_post_task(FlutterTask task,
void* user_data) {
FlEngine* self = static_cast<FlEngine*>(user_data);

g_autoptr(GSource) source =
g_source_new(&flutter_source_funcs, sizeof(FlutterSource));
FlutterSource* fl_source = reinterpret_cast<FlutterSource*>(source);
fl_source->engine = self;
g_object_weak_ref(G_OBJECT(self), engine_weak_notify_cb, fl_source);
fl_source->task = task;
g_source_set_ready_time(source,
target_time_nanos / kMicrosecondsPerNanosecond);
g_source_attach(source, nullptr);
fl_task_runner_post_task(self->task_runner, task, target_time_nanos);
}

// Called when a platform message is received from the engine.
Expand Down Expand Up @@ -361,6 +301,7 @@ static void fl_engine_dispose(GObject* object) {
g_clear_object(&self->renderer);
g_clear_object(&self->binary_messenger);
g_clear_object(&self->settings_plugin);
g_clear_object(&self->task_runner);

if (self->platform_message_handler_destroy_notify) {
self->platform_message_handler_destroy_notify(
Expand Down Expand Up @@ -410,6 +351,8 @@ G_MODULE_EXPORT FlEngine* fl_engine_new_headless(FlDartProject* project) {
gboolean fl_engine_start(FlEngine* self, GError** error) {
g_return_val_if_fail(FL_IS_ENGINE(self), FALSE);

self->task_runner = fl_task_runner_new(self);

FlutterRendererConfig config = {};
config.type = kOpenGL;
config.open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig);
Expand Down Expand Up @@ -722,3 +665,13 @@ G_MODULE_EXPORT FlBinaryMessenger* fl_engine_get_binary_messenger(
g_return_val_if_fail(FL_IS_ENGINE(self), nullptr);
return self->binary_messenger;
}

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);
}
19 changes: 19 additions & 0 deletions shell/platform/linux/fl_engine_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_renderer.h"
#include "flutter/shell/platform/linux/fl_task_runner.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"

Expand Down Expand Up @@ -226,6 +227,24 @@ GBytes* fl_engine_send_platform_message_finish(FlEngine* engine,
GAsyncResult* result,
GError** error);

/**
* fl_engine_get_task_runner:
* @engine: an #FlEngine.
* @result: a #FlTaskRunner.
*
* Returns: task runner responsible for scheduling Flutter tasks.
*/
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_
66 changes: 65 additions & 1 deletion shell/platform/linux/fl_renderer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,50 @@

#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_backing_store_provider.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"

G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error)

typedef struct {
FlView* view;

// target dimension for resizing
int target_width;
int target_height;

// whether the renderer waits for frame render
bool blocking_main_thread;

// true if frame was completed; resizing is not synchronized until first frame
// was rendered
bool had_first_frame;

GdkGLContext* main_context;
GdkGLContext* resource_context;
} FlRendererPrivate;

G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT)

static void fl_renderer_class_init(FlRendererClass* klass) {}
static void fl_renderer_unblock_main_thread(FlRenderer* self) {
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
fl_renderer_get_instance_private(self));
if (priv->blocking_main_thread) {
priv->blocking_main_thread = false;

FlTaskRunner* runner =
fl_engine_get_task_runner(fl_view_get_engine(priv->view));
fl_task_runner_release_main_thread(runner);
}
}

static void fl_renderer_dispose(GObject* self) {
fl_renderer_unblock_main_thread(FL_RENDERER(self));
G_OBJECT_CLASS(fl_renderer_parent_class)->dispose(self);
}

static void fl_renderer_class_init(FlRendererClass* klass) {
G_OBJECT_CLASS(klass)->dispose = fl_renderer_dispose;
}

static void fl_renderer_init(FlRenderer* self) {}

Expand Down Expand Up @@ -109,9 +140,42 @@ gboolean fl_renderer_collect_backing_store(
backing_store);
}

void fl_renderer_wait_for_frame(FlRenderer* self,
int target_width,
int target_height) {
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
fl_renderer_get_instance_private(self));

priv->target_width = target_width;
priv->target_height = target_height;

if (priv->had_first_frame && !priv->blocking_main_thread) {
priv->blocking_main_thread = true;
FlTaskRunner* runner =
fl_engine_get_task_runner(fl_view_get_engine(priv->view));
fl_task_runner_block_main_thread(runner);
}
}

gboolean fl_renderer_present_layers(FlRenderer* self,
const FlutterLayer** layers,
size_t layers_count) {
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
fl_renderer_get_instance_private(self));

// ignore incoming frame with wrong dimensions in trivial case with just one
// layer
if (priv->blocking_main_thread && layers_count == 1 &&
layers[0]->offset.x == 0 && layers[0]->offset.y == 0 &&
(layers[0]->size.width != priv->target_width ||
layers[0]->size.height != priv->target_height)) {
return true;
}

priv->had_first_frame = true;

fl_renderer_unblock_main_thread(self);

return FL_RENDERER_GET_CLASS(self)->present_layers(self, layers,
layers_count);
}
14 changes: 14 additions & 0 deletions shell/platform/linux/fl_renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,20 @@ gboolean fl_renderer_present_layers(FlRenderer* renderer,
const FlutterLayer** layers,
size_t layers_count);

/**
* fl_renderer_wait_for_frame:
* @renderer: an #FlRenderer.
* @target_width: width of frame being waited for
* @target_height: height of frame being waited for
*
* Holds the thread until frame with requested dimensions is presented.
* While waiting for frame Flutter platform and raster tasks are being
* processed.
*/
void fl_renderer_wait_for_frame(FlRenderer* renderer,
int target_width,
int target_height);

G_END_DECLS

#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_
Loading