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

Commit 299f0d9

Browse files
committed
Allow embedders to specify a render task runner description.
Embedders may use this to specify a thread whose event loop is managed by them instead of the engine. In addition, specifying the same task runner for both the platform and render task runners allows embedders to effectively perform GPU rendering operations on the platform thread. To affect this change, the following non breaking changes to the API have been made: * The `FlutterCustomTaskRunners` struct now has a new field `render_task_runner` for the specification of a custom render task runner. * The `FlutterTaskRunnerDescription` has a new field `identifier`. Embedders must supply a unique identifier for each task runner they specify. In addition, when describing multiple task runners that run their tasks on the same thread, their identifiers must match. * The embedder may need to process tasks during `FlutterEngineRun` and `FlutterEngineShutdown`. However, the embedder doesn't have the Flutter engine handle before `FlutterEngineRun` and is supposed to relinquish handle right before `FlutterEngineShutdown`. Since the embedder needs the Flutter engine handle to service tasks on other threads while these calls are underway, there exist opportunities for deadlock. To work around this scenario, three new calls have been added that allow more deliberate management of the Flutter engine instance. * `FlutterEngineRun` can be replaced with `FlutterEngineInitialize` and `FlutterEngineRunInitialized`. The embedder can obtain a handle to the engine after the first call but the engine will not post any tasks to custom task runners specified by the embedder till the `FlutterEngineRunInitialized` call. Embedders can guard the Flutter engine handle behind a mutex for safe task runner interop. * `FlutterEngineShutdown` can be preceded by the `FlutterEngineDeinitialize` call. After this call the Flutter engine will no longer post tasks onto embedder managed task runners. It is still embedder responsibility to collect the Flutter engine handle via `FlutterEngineShutdown`. * To maintain backwards compatibility with the old APIs, `FlutterEngineRun` is now just a convenience for `FlutterEngineInitialize` and `FlutterEngineRunInitilaized`. `FlutterEngineShutdown` now implicitly calls `FlutterEngineDeinitialize` as well. This allows existing users who don't care are custom task runner interop to keep using the old APIs. * Adds complete test coverage for both old and new paths. Fixes flutter/flutter#42460 Prerequisite for flutter/flutter#17579
1 parent 835838c commit 299f0d9

File tree

10 files changed

+573
-77
lines changed

10 files changed

+573
-77
lines changed

shell/platform/embedder/embedder.cc

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,22 @@ FlutterEngineResult FlutterEngineRun(size_t version,
557557
void* user_data,
558558
FLUTTER_API_SYMBOL(FlutterEngine) *
559559
engine_out) {
560+
auto result =
561+
FlutterEngineInitialize(version, config, args, user_data, engine_out);
562+
563+
if (result != kSuccess) {
564+
return result;
565+
}
566+
567+
return FlutterEngineRunInitialized(*engine_out);
568+
}
569+
570+
FlutterEngineResult FlutterEngineInitialize(size_t version,
571+
const FlutterRendererConfig* config,
572+
const FlutterProjectArgs* args,
573+
void* user_data,
574+
FLUTTER_API_SYMBOL(FlutterEngine) *
575+
engine_out) {
560576
// Step 0: Figure out arguments for shell creation.
561577
if (version != FLUTTER_ENGINE_VERSION) {
562578
return LOG_EMBEDDER_ERROR(kInvalidLibraryVersion);
@@ -861,26 +877,6 @@ FlutterEngineResult FlutterEngineRun(size_t version,
861877
return LOG_EMBEDDER_ERROR(kInvalidArguments);
862878
}
863879

864-
// Step 1: Create the engine.
865-
auto embedder_engine =
866-
std::make_unique<flutter::EmbedderEngine>(std::move(thread_host), //
867-
std::move(task_runners), //
868-
settings, //
869-
on_create_platform_view, //
870-
on_create_rasterizer, //
871-
external_texture_callback //
872-
);
873-
874-
if (!embedder_engine->IsValid()) {
875-
return LOG_EMBEDDER_ERROR(kInvalidArguments);
876-
}
877-
878-
// Step 2: Setup the rendering surface.
879-
if (!embedder_engine->NotifyCreated()) {
880-
return LOG_EMBEDDER_ERROR(kInvalidArguments);
881-
}
882-
883-
// Step 3: Run the engine.
884880
auto run_configuration =
885881
flutter::RunConfiguration::InferFromSettings(settings);
886882

@@ -895,23 +891,77 @@ FlutterEngineResult FlutterEngineRun(size_t version,
895891
return LOG_EMBEDDER_ERROR(kInvalidArguments);
896892
}
897893

898-
if (!embedder_engine->Run(std::move(run_configuration))) {
899-
return LOG_EMBEDDER_ERROR(kInvalidArguments);
900-
}
894+
// Create the engine but don't launch the shell or run the root isolate.
895+
auto embedder_engine = std::make_unique<flutter::EmbedderEngine>(
896+
std::move(thread_host), //
897+
std::move(task_runners), //
898+
std::move(settings), //
899+
std::move(run_configuration), //
900+
on_create_platform_view, //
901+
on_create_rasterizer, //
902+
external_texture_callback //
903+
);
901904

902-
// Finally! Release the ownership of the embedder engine to the caller.
905+
// Release the ownership of the embedder engine to the caller.
903906
*engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(
904907
embedder_engine.release());
905908
return kSuccess;
906909
}
907910

908-
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine)
909-
engine) {
911+
FlutterEngineResult FlutterEngineRunInitialized(
912+
FLUTTER_API_SYMBOL(FlutterEngine) engine) {
913+
if (!engine) {
914+
return LOG_EMBEDDER_ERROR(kInvalidArguments);
915+
}
916+
917+
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
918+
919+
// The engine must not already be running. Initialize may only be called once
920+
// on an engine instance.
921+
if (embedder_engine->IsValid()) {
922+
return LOG_EMBEDDER_ERROR(kInvalidArguments);
923+
}
924+
925+
// Step 1: Launch the shell.
926+
if (!embedder_engine->LaunchShell()) {
927+
FML_LOG(ERROR) << "Could not launch the engine using supplied "
928+
"initialization arguments.";
929+
return LOG_EMBEDDER_ERROR(kInvalidArguments);
930+
}
931+
932+
// Step 2: Tell the platform view to initialize itself.
933+
if (!embedder_engine->NotifyCreated()) {
934+
return LOG_EMBEDDER_ERROR(kInvalidArguments);
935+
}
936+
937+
// Step 3: Launch the root isolate.
938+
if (!embedder_engine->RunRootIsolate()) {
939+
return LOG_EMBEDDER_ERROR(kInvalidArguments);
940+
}
941+
942+
return kSuccess;
943+
}
944+
945+
FLUTTER_EXPORT
946+
FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine)
947+
engine) {
910948
if (engine == nullptr) {
911949
return LOG_EMBEDDER_ERROR(kInvalidArguments);
912950
}
951+
913952
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
914953
embedder_engine->NotifyDestroyed();
954+
embedder_engine->CollectShell();
955+
return kSuccess;
956+
}
957+
958+
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine)
959+
engine) {
960+
auto result = FlutterEngineDeinitialize(engine);
961+
if (result != kSuccess) {
962+
return result;
963+
}
964+
auto embedder_engine = reinterpret_cast<flutter::EmbedderEngine*>(engine);
915965
delete embedder_engine;
916966
return kSuccess;
917967
}

shell/platform/embedder/embedder.h

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,14 +603,24 @@ typedef struct {
603603
///
604604
/// @attention This field is required.
605605
FlutterTaskRunnerPostTaskCallback post_task_callback;
606+
/// A unique identifier for the task runner. If multiple task runners service
607+
/// tasks on the same thread, their identifiers must match.
608+
size_t identifier;
606609
} FlutterTaskRunnerDescription;
607610

608611
typedef struct {
609612
/// The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
610613
size_t struct_size;
611614
/// Specify the task runner for the thread on which the `FlutterEngineRun`
612-
/// call is made.
615+
/// call is made. The same task runner description can be specified for both
616+
/// the render and platform task runners. This makes the Flutter engine use
617+
/// the same thread for both task runners.
613618
const FlutterTaskRunnerDescription* platform_task_runner;
619+
/// Specify the task runner for the thread on which the render tasks will be
620+
/// run. The same task runner description can be specified for both the render
621+
/// and platform task runners. This makes the Flutter engine use the same
622+
/// thread for both task runners.
623+
const FlutterTaskRunnerDescription* render_task_runner;
614624
} FlutterCustomTaskRunners;
615625

616626
typedef struct {
@@ -950,6 +960,32 @@ typedef struct {
950960
const FlutterCompositor* compositor;
951961
} FlutterProjectArgs;
952962

963+
//------------------------------------------------------------------------------
964+
/// @brief Initialize and run a Flutter engine instance and return a handle
965+
/// to it. This is a convenience method for the the pair of calls to
966+
/// `FlutterEngineInitialize` and `FlutterEngineRunInitialized`.
967+
///
968+
/// @note This method of running a Flutter engine works well except in
969+
/// cases where the embedder specifies custom task runners via
970+
/// `FlutterProjectArgs::custom_task_runners`. In such cases, the
971+
/// engine may need the embedder to post tasks back to it before
972+
/// `FlutterEngineRun` has returned. Embedders can only post tasks
973+
/// to the engine if they have a handle to the engine. In such
974+
/// cases, embedders are advised to get the engine handle via the
975+
/// `FlutterInitializeCall`. Then they can call
976+
/// `FlutterEngineRunInitialized` knowing that they will be able to
977+
/// service custom tasks on other threads with the engine handle.
978+
///
979+
/// @param[in] version The Flutter embedder API version. Must be
980+
/// FLUTTER_ENGINE_VERSION.
981+
/// @param[in] config The renderer configuration.
982+
/// @param[in] args The Flutter project arguments.
983+
/// @param user_data A user data baton passed back to embedders in
984+
/// callbacks.
985+
/// @param[out] engine_out The engine handle on successful engine creation.
986+
///
987+
/// @return The result of the call to run the Flutter engine.
988+
///
953989
FLUTTER_EXPORT
954990
FlutterEngineResult FlutterEngineRun(size_t version,
955991
const FlutterRendererConfig* config,
@@ -958,10 +994,81 @@ FlutterEngineResult FlutterEngineRun(size_t version,
958994
FLUTTER_API_SYMBOL(FlutterEngine) *
959995
engine_out);
960996

997+
//------------------------------------------------------------------------------
998+
/// @brief Shuts down a Flutter engine instance. the engine handle is no
999+
/// longer valid for any calls in the embedder API after this point.
1000+
/// Making additional calls with this handle is undefined behavior.
1001+
///
1002+
/// @note This de-initializes the Flutter engine instance (via an implicit
1003+
/// call to `FlutterEngineDeinitialize`) if necessary.
1004+
///
1005+
/// @param[in] engine The Flutter engine instance to collect.
1006+
///
1007+
/// @return The result of the call to shutdown the Flutter engine instance.
1008+
///
9611009
FLUTTER_EXPORT
9621010
FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine)
9631011
engine);
9641012

1013+
//------------------------------------------------------------------------------
1014+
/// @brief Initialize a Flutter engine instance. This does not run the
1015+
/// Flutter application code till the `FlutterEngineRunInitialized`
1016+
/// call is made. Besides Flutter application code, no tasks are
1017+
/// scheduled on embedder managed task runners either. This allows
1018+
/// embedders providing custom task runners to the Flutter engine to
1019+
/// obtain a handle to the Flutter engine before the engine can post
1020+
/// tasks on these task runners.
1021+
///
1022+
/// @param[in] version The Flutter embedder API version. Must be
1023+
/// FLUTTER_ENGINE_VERSION.
1024+
/// @param[in] config The renderer configuration.
1025+
/// @param[in] args The Flutter project arguments.
1026+
/// @param user_data A user data baton passed back to embedders in
1027+
/// callbacks.
1028+
/// @param[out] engine_out The engine handle on successful engine creation.
1029+
///
1030+
/// @return The result of the call to initialize the Flutter engine.
1031+
///
1032+
FLUTTER_EXPORT
1033+
FlutterEngineResult FlutterEngineInitialize(size_t version,
1034+
const FlutterRendererConfig* config,
1035+
const FlutterProjectArgs* args,
1036+
void* user_data,
1037+
FLUTTER_API_SYMBOL(FlutterEngine) *
1038+
engine_out);
1039+
1040+
//------------------------------------------------------------------------------
1041+
/// @brief Stops running the Flutter engine instance. After this call, the
1042+
/// embedder is also guaranteed that no more calls to post tasks
1043+
/// onto custom task runners specified by the embedder are made. The
1044+
/// Flutter engine handle still needs to be collected via a call to
1045+
/// `FlutterEngineShutdown`.
1046+
///
1047+
/// @param[in] engine The running engine instance to de-initialize.
1048+
///
1049+
/// @return The result of the call to de-initialize the Flutter engine.
1050+
///
1051+
FLUTTER_EXPORT
1052+
FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine)
1053+
engine);
1054+
1055+
//------------------------------------------------------------------------------
1056+
/// @brief Runs an initialized engine instance. An engine can be
1057+
/// initialized via `FlutterEngineInitialize`. An initialized
1058+
/// instance can only be run once. During and after this call,
1059+
/// custom task runners supplied by the embedder are expected to
1060+
/// start servicing tasks.
1061+
///
1062+
/// @param[in] engine An initialized engine instance that has not previously
1063+
/// been run.
1064+
///
1065+
/// @return The result of the call to run the initialized Flutter
1066+
/// engine instance.
1067+
///
1068+
FLUTTER_EXPORT
1069+
FlutterEngineResult FlutterEngineRunInitialized(
1070+
FLUTTER_API_SYMBOL(FlutterEngine) engine);
1071+
9651072
FLUTTER_EXPORT
9661073
FlutterEngineResult FlutterEngineSendWindowMetricsEvent(
9671074
FLUTTER_API_SYMBOL(FlutterEngine) engine,

shell/platform/embedder/embedder_engine.cc

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,73 @@
99

1010
namespace flutter {
1111

12+
struct ShellArgs {
13+
Settings settings;
14+
Shell::CreateCallback<PlatformView> on_create_platform_view;
15+
Shell::CreateCallback<Rasterizer> on_create_rasterizer;
16+
ShellArgs(Settings p_settings,
17+
Shell::CreateCallback<PlatformView> p_on_create_platform_view,
18+
Shell::CreateCallback<Rasterizer> p_on_create_rasterizer)
19+
: settings(std::move(p_settings)),
20+
on_create_platform_view(std::move(p_on_create_platform_view)),
21+
on_create_rasterizer(std::move(p_on_create_rasterizer)) {}
22+
};
23+
1224
EmbedderEngine::EmbedderEngine(
1325
std::unique_ptr<EmbedderThreadHost> thread_host,
1426
flutter::TaskRunners task_runners,
1527
flutter::Settings settings,
28+
RunConfiguration run_configuration,
1629
Shell::CreateCallback<PlatformView> on_create_platform_view,
1730
Shell::CreateCallback<Rasterizer> on_create_rasterizer,
1831
EmbedderExternalTextureGL::ExternalTextureCallback
1932
external_texture_callback)
2033
: thread_host_(std::move(thread_host)),
2134
task_runners_(task_runners),
22-
shell_(Shell::Create(task_runners_,
23-
std::move(settings),
24-
on_create_platform_view,
25-
on_create_rasterizer)),
26-
external_texture_callback_(external_texture_callback) {
27-
if (!shell_) {
28-
return;
35+
run_configuration_(std::move(run_configuration)),
36+
shell_args_(std::make_unique<ShellArgs>(std::move(settings),
37+
on_create_platform_view,
38+
on_create_rasterizer)),
39+
external_texture_callback_(external_texture_callback) {}
40+
41+
EmbedderEngine::~EmbedderEngine() = default;
42+
43+
bool EmbedderEngine::LaunchShell() {
44+
if (!shell_args_) {
45+
FML_DLOG(ERROR) << "Invalid shell arguments.";
46+
return false;
47+
}
48+
49+
if (shell_) {
50+
FML_DLOG(ERROR) << "Shell already initialized";
2951
}
3052

31-
is_valid_ = true;
53+
shell_ = Shell::Create(task_runners_, shell_args_->settings,
54+
shell_args_->on_create_platform_view,
55+
shell_args_->on_create_rasterizer);
56+
57+
// Reset the args no matter what. We are never going to use them to initialize
58+
// a shell again.
59+
shell_args_.reset();
60+
61+
return IsValid();
3262
}
3363

34-
EmbedderEngine::~EmbedderEngine() = default;
64+
bool EmbedderEngine::CollectShell() {
65+
shell_.reset();
66+
return IsValid();
67+
}
68+
69+
bool EmbedderEngine::RunRootIsolate() {
70+
if (!IsValid() || !run_configuration_.IsValid()) {
71+
return false;
72+
}
73+
shell_->RunEngine(std::move(run_configuration_));
74+
return true;
75+
}
3576

3677
bool EmbedderEngine::IsValid() const {
37-
return is_valid_;
78+
return static_cast<bool>(shell_);
3879
}
3980

4081
const TaskRunners& EmbedderEngine::GetTaskRunners() const {
@@ -59,14 +100,6 @@ bool EmbedderEngine::NotifyDestroyed() {
59100
return true;
60101
}
61102

62-
bool EmbedderEngine::Run(RunConfiguration run_configuration) {
63-
if (!IsValid() || !run_configuration.IsValid()) {
64-
return false;
65-
}
66-
shell_->RunEngine(std::move(run_configuration));
67-
return true;
68-
}
69-
70103
bool EmbedderEngine::SetViewportMetrics(flutter::ViewportMetrics metrics) {
71104
if (!IsValid()) {
72105
return false;
@@ -187,6 +220,10 @@ bool EmbedderEngine::OnVsyncEvent(intptr_t baton,
187220
}
188221

189222
bool EmbedderEngine::ReloadSystemFonts() {
223+
if (!IsValid()) {
224+
return false;
225+
}
226+
190227
return shell_->ReloadSystemFonts();
191228
}
192229

@@ -200,7 +237,10 @@ bool EmbedderEngine::PostRenderThreadTask(fml::closure task) {
200237
}
201238

202239
bool EmbedderEngine::RunTask(const FlutterTask* task) {
203-
if (!IsValid() || task == nullptr) {
240+
// The shell doesn't need to be running or valid for access to the thread
241+
// host. This is why there is no `IsValid` check here. This allows embedders
242+
// to perform custom task runner interop before the shell is running.
243+
if (task == nullptr) {
204244
return false;
205245
}
206246
return thread_host_->PostTask(reinterpret_cast<int64_t>(task->runner),

0 commit comments

Comments
 (0)