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

Commit d8b3b6b

Browse files
committed
Implement smooth resizing for Linux
flutter/flutter#81677
1 parent ae2793f commit d8b3b6b

File tree

9 files changed

+386
-56
lines changed

9 files changed

+386
-56
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc
14721472
FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec_test.cc
14731473
FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc
14741474
FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc
1475+
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.cc
1476+
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.h
14751477
FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.cc
14761478
FILE: ../../../flutter/shell/platform/linux/fl_text_input_plugin.h
14771479
FILE: ../../../flutter/shell/platform/linux/fl_value.cc

shell/platform/linux/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ source_set("flutter_linux_sources") {
114114
"fl_standard_message_codec.cc",
115115
"fl_standard_method_codec.cc",
116116
"fl_string_codec.cc",
117+
"fl_task_runner.cc",
118+
"fl_task_runner.h",
117119
"fl_text_input_plugin.cc",
118120
"fl_value.cc",
119121
"fl_view.cc",

shell/platform/linux/fl_engine.cc

Lines changed: 17 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
#include "flutter/shell/platform/linux/fl_settings_plugin.h"
1818
#include "flutter/shell/platform/linux/public/flutter_linux/fl_plugin_registry.h"
1919

20-
static constexpr int kMicrosecondsPerNanosecond = 1000;
21-
2220
// Unique number associated with platform tasks.
2321
static constexpr size_t kPlatformTaskRunnerIdentifier = 1;
2422

@@ -32,6 +30,7 @@ struct _FlEngine {
3230
FlRenderer* renderer;
3331
FlBinaryMessenger* binary_messenger;
3432
FlSettingsPlugin* settings_plugin;
33+
FlTaskRunner* task_runner;
3534
FlutterEngineAOTData aot_data;
3635
FLUTTER_API_SYMBOL(FlutterEngine) engine;
3736
FlutterEngineProcTable embedder_api;
@@ -143,40 +142,6 @@ static void setup_locales(FlEngine* self) {
143142
}
144143
}
145144

146-
// Callback to run a Flutter task in the GLib main loop.
147-
static gboolean flutter_source_dispatch(GSource* source,
148-
GSourceFunc callback,
149-
gpointer user_data) {
150-
FlutterSource* fl_source = reinterpret_cast<FlutterSource*>(source);
151-
FlEngine* self = fl_source->engine;
152-
153-
FlutterEngineResult result =
154-
self->embedder_api.RunTask(self->engine, &fl_source->task);
155-
if (result != kSuccess) {
156-
g_warning("Failed to run Flutter task\n");
157-
}
158-
159-
return G_SOURCE_REMOVE;
160-
}
161-
162-
// Called when the engine is disposed.
163-
static void engine_weak_notify_cb(gpointer user_data,
164-
GObject* where_the_object_was) {
165-
FlutterSource* source = reinterpret_cast<FlutterSource*>(user_data);
166-
source->engine = nullptr;
167-
g_source_destroy(reinterpret_cast<GSource*>(source));
168-
}
169-
170-
// Called when a flutter source completes.
171-
static void flutter_source_finalize(GSource* source) {
172-
FlutterSource* fl_source = reinterpret_cast<FlutterSource*>(source);
173-
if (fl_source->engine != nullptr) {
174-
g_object_weak_unref(G_OBJECT(fl_source->engine), engine_weak_notify_cb,
175-
fl_source);
176-
fl_source->engine = nullptr;
177-
}
178-
}
179-
180145
// Called when engine needs a backing store for a specific #FlutterLayer.
181146
static bool compositor_create_backing_store_callback(
182147
const FlutterBackingStoreConfig* config,
@@ -204,16 +169,6 @@ static bool compositor_present_layers_callback(const FlutterLayer** layers,
204169
layers_count);
205170
}
206171

207-
// Table of functions for Flutter GLib main loop integration.
208-
static GSourceFuncs flutter_source_funcs = {
209-
nullptr, // prepare
210-
nullptr, // check
211-
flutter_source_dispatch, // dispatch
212-
flutter_source_finalize, // finalize
213-
nullptr,
214-
nullptr // Internal usage
215-
};
216-
217172
// Flutter engine rendering callbacks.
218173

219174
static void* fl_engine_gl_proc_resolver(void* user_data, const char* name) {
@@ -278,15 +233,7 @@ static void fl_engine_post_task(FlutterTask task,
278233
void* user_data) {
279234
FlEngine* self = static_cast<FlEngine*>(user_data);
280235

281-
g_autoptr(GSource) source =
282-
g_source_new(&flutter_source_funcs, sizeof(FlutterSource));
283-
FlutterSource* fl_source = reinterpret_cast<FlutterSource*>(source);
284-
fl_source->engine = self;
285-
g_object_weak_ref(G_OBJECT(self), engine_weak_notify_cb, fl_source);
286-
fl_source->task = task;
287-
g_source_set_ready_time(source,
288-
target_time_nanos / kMicrosecondsPerNanosecond);
289-
g_source_attach(source, nullptr);
236+
fl_task_runner_post_task(self->task_runner, task, target_time_nanos);
290237
}
291238

292239
// Called when a platform message is received from the engine.
@@ -362,6 +309,9 @@ static void fl_engine_dispose(GObject* object) {
362309
g_clear_object(&self->binary_messenger);
363310
g_clear_object(&self->settings_plugin);
364311

312+
fl_task_runner_stop(self->task_runner);
313+
g_clear_object(&self->task_runner);
314+
365315
if (self->platform_message_handler_destroy_notify) {
366316
self->platform_message_handler_destroy_notify(
367317
self->platform_message_handler_data);
@@ -407,6 +357,11 @@ G_MODULE_EXPORT FlEngine* fl_engine_new_headless(FlDartProject* project) {
407357
return fl_engine_new(project, FL_RENDERER(renderer));
408358
}
409359

360+
static void fl_engine_execute_task(FlutterTask task, gpointer user_data) {
361+
FlEngine* self = FL_ENGINE(user_data);
362+
self->embedder_api.RunTask(self->engine, &task);
363+
}
364+
410365
gboolean fl_engine_start(FlEngine* self, GError** error) {
411366
g_return_val_if_fail(FL_IS_ENGINE(self), FALSE);
412367

@@ -502,6 +457,8 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
502457
self->settings_plugin = fl_settings_plugin_new(self->binary_messenger);
503458
fl_settings_plugin_start(self->settings_plugin);
504459

460+
self->task_runner = fl_task_runner_new(fl_engine_execute_task, self);
461+
505462
result = self->embedder_api.UpdateSemanticsEnabled(self->engine, TRUE);
506463
if (result != kSuccess)
507464
g_warning("Failed to enable accessibility features on Flutter engine");
@@ -722,3 +679,8 @@ G_MODULE_EXPORT FlBinaryMessenger* fl_engine_get_binary_messenger(
722679
g_return_val_if_fail(FL_IS_ENGINE(self), nullptr);
723680
return self->binary_messenger;
724681
}
682+
683+
FlTaskRunner* fl_engine_get_task_runner(FlEngine* self) {
684+
g_return_val_if_fail(FL_IS_ENGINE(self), nullptr);
685+
return self->task_runner;
686+
}

shell/platform/linux/fl_engine_private.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "flutter/shell/platform/embedder/embedder.h"
1111
#include "flutter/shell/platform/linux/fl_renderer.h"
12+
#include "flutter/shell/platform/linux/fl_task_runner.h"
1213
#include "flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h"
1314
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
1415

@@ -226,6 +227,8 @@ GBytes* fl_engine_send_platform_message_finish(FlEngine* engine,
226227
GAsyncResult* result,
227228
GError** error);
228229

230+
FlTaskRunner* fl_engine_get_task_runner(FlEngine* engine);
231+
229232
G_END_DECLS
230233

231234
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_ENGINE_PRIVATE_H_

shell/platform/linux/fl_renderer.cc

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,50 @@
99

1010
#include "flutter/shell/platform/embedder/embedder.h"
1111
#include "flutter/shell/platform/linux/fl_backing_store_provider.h"
12+
#include "flutter/shell/platform/linux/fl_engine_private.h"
1213

1314
G_DEFINE_QUARK(fl_renderer_error_quark, fl_renderer_error)
1415

1516
typedef struct {
1617
FlView* view;
1718

19+
// target dimension for resizing
20+
int target_width;
21+
int target_height;
22+
23+
// whether the renderer waits for frame render
24+
bool blocking_main_thread;
25+
26+
// true if frame was completed; resizing is not synchronized until first frame
27+
// was rendered
28+
bool had_first_frame;
29+
1830
GdkGLContext* main_context;
1931
GdkGLContext* resource_context;
2032
} FlRendererPrivate;
2133

2234
G_DEFINE_TYPE_WITH_PRIVATE(FlRenderer, fl_renderer, G_TYPE_OBJECT)
2335

24-
static void fl_renderer_class_init(FlRendererClass* klass) {}
36+
static void fl_renderer_unblock_main_thread(FlRenderer* self) {
37+
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
38+
fl_renderer_get_instance_private(self));
39+
if (priv->blocking_main_thread) {
40+
priv->blocking_main_thread = false;
41+
42+
FlTaskRunner* runner =
43+
fl_engine_get_task_runner(fl_view_get_engine(priv->view));
44+
fl_task_runner_release_main_thread(runner);
45+
}
46+
}
47+
48+
static void fl_renderer_dispose(GObject* self) {
49+
fl_renderer_unblock_main_thread(FL_RENDERER(self));
50+
G_OBJECT_CLASS(fl_renderer_parent_class)->dispose(self);
51+
}
52+
53+
static void fl_renderer_class_init(FlRendererClass* klass) {
54+
G_OBJECT_CLASS(klass)->dispose = fl_renderer_dispose;
55+
}
2556

2657
static void fl_renderer_init(FlRenderer* self) {}
2758

@@ -109,9 +140,42 @@ gboolean fl_renderer_collect_backing_store(
109140
backing_store);
110141
}
111142

143+
void fl_renderer_wait_for_frame(FlRenderer* self,
144+
int target_width,
145+
int target_height) {
146+
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
147+
fl_renderer_get_instance_private(self));
148+
149+
priv->target_width = target_width;
150+
priv->target_height = target_height;
151+
152+
if (priv->had_first_frame && !priv->blocking_main_thread) {
153+
priv->blocking_main_thread = true;
154+
FlTaskRunner* runner =
155+
fl_engine_get_task_runner(fl_view_get_engine(priv->view));
156+
fl_task_runner_block_main_thread(runner);
157+
}
158+
}
159+
112160
gboolean fl_renderer_present_layers(FlRenderer* self,
113161
const FlutterLayer** layers,
114162
size_t layers_count) {
163+
FlRendererPrivate* priv = reinterpret_cast<FlRendererPrivate*>(
164+
fl_renderer_get_instance_private(self));
165+
166+
// ignore incoming frame with wrong dimensions in trivial case with just one
167+
// layer
168+
if (priv->blocking_main_thread && layers_count == 1 &&
169+
layers[0]->offset.x == 0 && layers[0]->offset.y == 0 &&
170+
(layers[0]->size.width != priv->target_width ||
171+
layers[0]->size.height != priv->target_height)) {
172+
return true;
173+
}
174+
175+
priv->had_first_frame = true;
176+
177+
fl_renderer_unblock_main_thread(self);
178+
115179
return FL_RENDERER_GET_CLASS(self)->present_layers(self, layers,
116180
layers_count);
117181
}

shell/platform/linux/fl_renderer.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,20 @@ gboolean fl_renderer_present_layers(FlRenderer* renderer,
242242
const FlutterLayer** layers,
243243
size_t layers_count);
244244

245+
/**
246+
* fl_renderer_wait_for_frame
247+
* @renderer: an #FlRenderer.
248+
* @target_width: width of frame being waited for
249+
* @target_height: height of frame being waited for
250+
*
251+
* Holds the thread until frame with requested dimensions is presented.
252+
* While waiting for frame flutter platform and raster tasks are being
253+
* processed.
254+
*/
255+
void fl_renderer_wait_for_frame(FlRenderer* renderer,
256+
int target_width,
257+
int target_height);
258+
245259
G_END_DECLS
246260

247261
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_

0 commit comments

Comments
 (0)