diff --git a/engine/flutter/shell/platform/embedder/ace_embedder.cc b/engine/flutter/shell/platform/embedder/ace_embedder.cc index 19be62ba4a4181939e95d36ef3c10718984847da..dc32bfc5a2d8e72c2e73819a216af108b10180be 100644 --- a/engine/flutter/shell/platform/embedder/ace_embedder.cc +++ b/engine/flutter/shell/platform/embedder/ace_embedder.cc @@ -525,6 +525,21 @@ FlutterEngineResult FlutterEngineRun(const FlutterRendererConfig* config, void* user_data, FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + auto result = + FlutterEngineInitialize(config, args, user_data, engine_out); + + if (result != kSuccess) { + return result; + } + + return FlutterEngineRunInitialized(*engine_out); +} + +FlutterEngineResult FlutterEngineInitialize(const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * + engine_out) { // Step 0: Figure out arguments for shell creation. if (engine_out == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments); @@ -647,44 +662,77 @@ FlutterEngineResult FlutterEngineRun(const FlutterRendererConfig* config, return LOG_EMBEDDER_ERROR(kInvalidArguments); } - // Step 1: Create the engine. - auto embedder_engine = - std::make_unique(std::move(thread_host), // - std::move(task_runners), // - settings, // - on_create_platform_view, // - on_create_rasterizer, // - external_texture_callback // - ); + flutter::RunConfiguration run_configuration; + // Create the engine but don't launch the shell or run the root isolate. + auto embedder_engine = std::make_unique( + std::move(thread_host), // + std::move(task_runners), // + std::move(settings), // + std::move(run_configuration), // + on_create_platform_view, // + on_create_rasterizer, // + external_texture_callback // + ); + + // Release the ownership of the embedder engine to the caller. + *engine_out = reinterpret_cast( + embedder_engine.release()); + return kSuccess; +} + +FlutterEngineResult FlutterEngineRunInitialized( + FLUTTER_API_SYMBOL(FlutterEngine) engine) { + if (!engine) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + auto embedder_engine = reinterpret_cast(engine); + + // The engine must not already be running. Initialize may only be called once + // on an engine instance. + if (embedder_engine->IsValid()) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } - if (!embedder_engine->IsValid()) { + // Step 1: Launch the shell. + if (!embedder_engine->LaunchShell()) { + FML_LOG(ERROR) << "Could not launch the engine using supplied " + "initialization arguments."; return LOG_EMBEDDER_ERROR(kInvalidArguments); } - // Step 2: Setup the rendering surface. + // Step 2: Tell the platform view to initialize itself. if (!embedder_engine->NotifyCreated()) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } - // Step 3: Run the engine. - flutter::RunConfiguration run_configuration; - if (!embedder_engine->Run(std::move(run_configuration))) { + // Step 3: Launch the root isolate. + if (!embedder_engine->RunRootIsolate()) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } - // Finally! Release the ownership of the embedder engine to the caller. - *engine_out = reinterpret_cast( - embedder_engine.release()); return kSuccess; } -FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) - engine) { +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) + engine) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto embedder_engine = reinterpret_cast(engine); embedder_engine->NotifyDestroyed(); + embedder_engine->CollectShell(); + return kSuccess; +} + +FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) + engine) { + auto result = FlutterEngineDeinitialize(engine); + if (result != kSuccess) { + return result; + } + auto embedder_engine = reinterpret_cast(engine); delete embedder_engine; return kSuccess; } diff --git a/engine/flutter/shell/platform/embedder/ace_embedder_engine.cc b/engine/flutter/shell/platform/embedder/ace_embedder_engine.cc index 246a2519edc90442a462858be44d2c6e4bd7242f..4f2b121fbde831706c5a2e4c4e70af84e0774433 100644 --- a/engine/flutter/shell/platform/embedder/ace_embedder_engine.cc +++ b/engine/flutter/shell/platform/embedder/ace_embedder_engine.cc @@ -11,32 +11,73 @@ namespace flutter { +struct ShellArgs { + Settings settings; + Shell::CreateCallback on_create_platform_view; + Shell::CreateCallback on_create_rasterizer; + ShellArgs(Settings p_settings, + Shell::CreateCallback p_on_create_platform_view, + Shell::CreateCallback p_on_create_rasterizer) + : settings(std::move(p_settings)), + on_create_platform_view(std::move(p_on_create_platform_view)), + on_create_rasterizer(std::move(p_on_create_rasterizer)) {} +}; + EmbedderEngine::EmbedderEngine( std::unique_ptr thread_host, flutter::TaskRunners task_runners, flutter::Settings settings, + RunConfiguration run_configuration, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer, EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback) : thread_host_(std::move(thread_host)), task_runners_(task_runners), - shell_(Shell::Create(task_runners_, - std::move(settings), - on_create_platform_view, - on_create_rasterizer)), - external_texture_callback_(external_texture_callback) { - if (!shell_) { - return; + run_configuration_(std::move(run_configuration)), + shell_args_(std::make_unique(std::move(settings), + on_create_platform_view, + on_create_rasterizer)), + external_texture_callback_(external_texture_callback) {} + +EmbedderEngine::~EmbedderEngine() = default; + +bool EmbedderEngine::LaunchShell() { + if (!shell_args_) { + FML_DLOG(ERROR) << "Invalid shell arguments."; + return false; } - is_valid_ = true; + if (shell_) { + FML_DLOG(ERROR) << "Shell already initialized"; + } + + shell_ = Shell::Create(task_runners_, shell_args_->settings, + shell_args_->on_create_platform_view, + shell_args_->on_create_rasterizer); + + // Reset the args no matter what. They will never be used to initialize a + // shell again. + shell_args_.reset(); + + return IsValid(); } -EmbedderEngine::~EmbedderEngine() = default; +bool EmbedderEngine::CollectShell() { + shell_.reset(); + return IsValid(); +} + +bool EmbedderEngine::RunRootIsolate() { + if (!IsValid() || !run_configuration_.IsValid()) { + return false; + } + shell_->RunEngine(std::move(run_configuration_)); + return true; +} bool EmbedderEngine::IsValid() const { - return is_valid_; + return static_cast(shell_); } const TaskRunners& EmbedderEngine::GetTaskRunners() const { @@ -61,14 +102,6 @@ bool EmbedderEngine::NotifyDestroyed() { return true; } -bool EmbedderEngine::Run(RunConfiguration run_configuration) { - if (!IsValid() || !run_configuration.IsValid()) { - return false; - } - shell_->RunEngine(std::move(run_configuration)); - return true; -} - bool EmbedderEngine::SetIdleNotificationCallback( const IdleCallback& idle_notification_callback) { if (!IsValid()) { @@ -173,7 +206,10 @@ bool EmbedderEngine::PostRenderThreadTask(fml::closure task) { } bool EmbedderEngine::RunTask(const FlutterTask* task) { - if (!IsValid() || task == nullptr) { + // The shell doesn't need to be running or valid for access to the thread + // host. This is why there is no `IsValid` check here. This allows embedders + // to perform custom task runner interop before the shell is running. + if (task == nullptr) { return false; } return thread_host_->PostTask(reinterpret_cast(task->runner), diff --git a/engine/flutter/shell/platform/embedder/embedder.cc b/engine/flutter/shell/platform/embedder/embedder.cc index bc412d903bdc618a86d8b54598c356fe5acfd0aa..f53264fdd88b933e5ad8c4eb8f23346ac66223f1 100644 --- a/engine/flutter/shell/platform/embedder/embedder.cc +++ b/engine/flutter/shell/platform/embedder/embedder.cc @@ -550,6 +550,22 @@ FlutterEngineResult FlutterEngineRun(size_t version, void* user_data, FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + auto result = + FlutterEngineInitialize(config, args, user_data, engine_out); + + if (result != kSuccess) { + return result; + } + + return FlutterEngineRunInitialized(*engine_out); +} + +FlutterEngineResult FlutterEngineInitialize(size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * + engine_out) { // Step 0: Figure out arguments for shell creation. if (version != FLUTTER_ENGINE_VERSION) { return LOG_EMBEDDER_ERROR(kInvalidLibraryVersion); @@ -846,26 +862,6 @@ FlutterEngineResult FlutterEngineRun(size_t version, return LOG_EMBEDDER_ERROR(kInvalidArguments); } - // Step 1: Create the engine. - auto embedder_engine = - std::make_unique(std::move(thread_host), // - std::move(task_runners), // - settings, // - on_create_platform_view, // - on_create_rasterizer, // - external_texture_callback // - ); - - if (!embedder_engine->IsValid()) { - return LOG_EMBEDDER_ERROR(kInvalidArguments); - } - - // Step 2: Setup the rendering surface. - if (!embedder_engine->NotifyCreated()) { - return LOG_EMBEDDER_ERROR(kInvalidArguments); - } - - // Step 3: Run the engine. auto run_configuration = flutter::RunConfiguration::InferFromSettings(settings); @@ -880,23 +876,77 @@ FlutterEngineResult FlutterEngineRun(size_t version, return LOG_EMBEDDER_ERROR(kInvalidArguments); } - if (!embedder_engine->Run(std::move(run_configuration))) { - return LOG_EMBEDDER_ERROR(kInvalidArguments); - } + flutter::RunConfiguration run_configuration; + // Create the engine but don't launch the shell or run the root isolate. + auto embedder_engine = std::make_unique( + std::move(thread_host), // + std::move(task_runners), // + std::move(settings), // + std::move(run_configuration), // + on_create_platform_view, // + on_create_rasterizer, // + external_texture_callback // + ); - // Finally! Release the ownership of the embedder engine to the caller. + // Release the ownership of the embedder engine to the caller. *engine_out = reinterpret_cast( embedder_engine.release()); return kSuccess; } -FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) - engine) { +FlutterEngineResult FlutterEngineRunInitialized( + FLUTTER_API_SYMBOL(FlutterEngine) engine) { + if (!engine) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + auto embedder_engine = reinterpret_cast(engine); + + // The engine must not already be running. Initialize may only be called once + // on an engine instance. + if (embedder_engine->IsValid()) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + // Step 1: Launch the shell. + if (!embedder_engine->LaunchShell()) { + FML_LOG(ERROR) << "Could not launch the engine using supplied " + "initialization arguments."; + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + // Step 2: Tell the platform view to initialize itself. + if (!embedder_engine->NotifyCreated()) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + // Step 3: Launch the root isolate. + if (!embedder_engine->RunRootIsolate()) { + return LOG_EMBEDDER_ERROR(kInvalidArguments); + } + + return kSuccess; +} + +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) + engine) { if (engine == nullptr) { return LOG_EMBEDDER_ERROR(kInvalidArguments); } auto embedder_engine = reinterpret_cast(engine); embedder_engine->NotifyDestroyed(); + embedder_engine->CollectShell(); + return kSuccess; +} + +FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) + engine) { + auto result = FlutterEngineDeinitialize(engine); + if (result != kSuccess) { + return result; + } + auto embedder_engine = reinterpret_cast(engine); delete embedder_engine; return kSuccess; } diff --git a/engine/flutter/shell/platform/embedder/embedder.h b/engine/flutter/shell/platform/embedder/embedder.h index cde957bb8db74f8ffede73bd5b02d07cfb6cc074..a85be7b708db72b97c9c94e599c67f87413ea6ce 100644 --- a/engine/flutter/shell/platform/embedder/embedder.h +++ b/engine/flutter/shell/platform/embedder/embedder.h @@ -149,7 +149,7 @@ typedef struct { BoolCallback make_current; BoolCallback clear_current; BoolCallback present; - UserBoolCallback send_current_surface; // for ACE PC preivew + UserBoolCallback send_current_surface; // for ACE PC preivew UIntCallback fbo_callback; // This is an optional callback. Flutter will ask the emebdder to create a GL // context current on a background thread. If the embedder is able to do so, @@ -356,14 +356,24 @@ typedef struct { // // This field is required. FlutterTaskRunnerPostTaskCallback post_task_callback; + /// A unique identifier for the task runner. If multiple task runners service + /// tasks on the same thread, their identifiers must match. + size_t identifier; } FlutterTaskRunnerDescription; typedef struct { // The size of this struct. Must be sizeof(FlutterCustomTaskRunners). size_t struct_size; - // Specify the task runner for the thread on which the |FlutterEngineRun| call - // is made. + // Specify the task runner for the thread on which the |FlutterEngineRun| + /// call is made. The same task runner description can be specified for both + /// the render and platform task runners. This makes the Flutter engine use + /// the same thread for both task runners. const FlutterTaskRunnerDescription* platform_task_runner; + /// Specify the task runner for the thread on which the render tasks will be + /// run. The same task runner description can be specified for both the render + /// and platform task runners. This makes the Flutter engine use the same + /// thread for both task runners. + const FlutterTaskRunnerDescription* render_task_runner; } FlutterCustomTaskRunners; typedef struct { @@ -557,6 +567,32 @@ typedef struct { const FlutterCompositor* compositor; } FlutterProjectArgs; +//------------------------------------------------------------------------------ +/// @brief Initialize and run a Flutter engine instance and return a handle +/// to it. This is a convenience method for the the pair of calls to +/// `FlutterEngineInitialize` and `FlutterEngineRunInitialized`. +/// +/// @note This method of running a Flutter engine works well except in +/// cases where the embedder specifies custom task runners via +/// `FlutterProjectArgs::custom_task_runners`. In such cases, the +/// engine may need the embedder to post tasks back to it before +/// `FlutterEngineRun` has returned. Embedders can only post tasks +/// to the engine if they have a handle to the engine. In such +/// cases, embedders are advised to get the engine handle via the +/// `FlutterInitializeCall`. Then they can call +/// `FlutterEngineRunInitialized` knowing that they will be able to +/// service custom tasks on other threads with the engine handle. +/// +/// @param[in] version The Flutter embedder API version. Must be +/// FLUTTER_ENGINE_VERSION. +/// @param[in] config The renderer configuration. +/// @param[in] args The Flutter project arguments. +/// @param user_data A user data baton passed back to embedders in +/// callbacks. +/// @param[out] engine_out The engine handle on successful engine creation. +/// +/// @return The result of the call to run the Flutter engine. +/// FLUTTER_EXPORT FlutterEngineResult FlutterEngineRun(const FlutterRendererConfig* config, const FlutterProjectArgs* args, @@ -564,9 +600,78 @@ FlutterEngineResult FlutterEngineRun(const FlutterRendererConfig* config, FLUTTER_API_SYMBOL(FlutterEngine) * engine_out); +//------------------------------------------------------------------------------ +/// @brief Shuts down a Flutter engine instance. The engine handle is no +/// longer valid for any calls in the embedder API after this point. +/// Making additional calls with this handle is undefined behavior. +/// +/// @note This de-initializes the Flutter engine instance (via an implicit +/// call to `FlutterEngineDeinitialize`) if necessary. +/// +/// @param[in] engine The Flutter engine instance to collect. +/// +/// @return The result of the call to shutdown the Flutter engine instance. +/// FLUTTER_EXPORT FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) engine); +//------------------------------------------------------------------------------ +/// @brief Initialize a Flutter engine instance. This does not run the +/// Flutter application code till the `FlutterEngineRunInitialized` +/// call is made. Besides Flutter application code, no tasks are +/// scheduled on embedder managed task runners either. This allows +/// embedders providing custom task runners to the Flutter engine to +/// obtain a handle to the Flutter engine before the engine can post +/// tasks on these task runners. +/// +/// @param[in] version The Flutter embedder API version. Must be +/// FLUTTER_ENGINE_VERSION. +/// @param[in] config The renderer configuration. +/// @param[in] args The Flutter project arguments. +/// @param user_data A user data baton passed back to embedders in +/// callbacks. +/// @param[out] engine_out The engine handle on successful engine creation. +/// +/// @return The result of the call to initialize the Flutter engine. +/// +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineInitialize(const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * + engine_out); + +//------------------------------------------------------------------------------ +/// @brief Stops running the Flutter engine instance. After this call, the +/// embedder is also guaranteed that no more calls to post tasks +/// onto custom task runners specified by the embedder are made. The +/// Flutter engine handle still needs to be collected via a call to +/// `FlutterEngineShutdown`. +/// +/// @param[in] engine The running engine instance to de-initialize. +/// +/// @return The result of the call to de-initialize the Flutter engine. +/// +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) + engine); + +//------------------------------------------------------------------------------ +/// @brief Runs an initialized engine instance. An engine can be +/// initialized via `FlutterEngineInitialize`. An initialized +/// instance can only be run once. During and after this call, +/// custom task runners supplied by the embedder are expected to +/// start servicing tasks. +/// +/// @param[in] engine An initialized engine instance that has not previously +/// been run. +/// +/// @return The result of the call to run the initialized Flutter +/// engine instance. +/// +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineRunInitialized( + FLUTTER_API_SYMBOL(FlutterEngine) engine); FLUTTER_EXPORT FlutterEngineResult FlutterEngineSetIdleNotificationCallback( diff --git a/engine/flutter/shell/platform/embedder/embedder_engine.cc b/engine/flutter/shell/platform/embedder/embedder_engine.cc index 6f675e785dabb6d5163924a19020a69a7995088c..e9a1215e8ca41b781a90469d2a1d44d8eb9fc160 100644 --- a/engine/flutter/shell/platform/embedder/embedder_engine.cc +++ b/engine/flutter/shell/platform/embedder/embedder_engine.cc @@ -9,32 +9,73 @@ namespace flutter { +struct ShellArgs { + Settings settings; + Shell::CreateCallback on_create_platform_view; + Shell::CreateCallback on_create_rasterizer; + ShellArgs(Settings p_settings, + Shell::CreateCallback p_on_create_platform_view, + Shell::CreateCallback p_on_create_rasterizer) + : settings(std::move(p_settings)), + on_create_platform_view(std::move(p_on_create_platform_view)), + on_create_rasterizer(std::move(p_on_create_rasterizer)) {} +}; + EmbedderEngine::EmbedderEngine( std::unique_ptr thread_host, flutter::TaskRunners task_runners, flutter::Settings settings, + RunConfiguration run_configuration, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer, EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback) : thread_host_(std::move(thread_host)), task_runners_(task_runners), - shell_(Shell::Create(task_runners_, - std::move(settings), - on_create_platform_view, - on_create_rasterizer)), - external_texture_callback_(external_texture_callback) { - if (!shell_) { - return; + run_configuration_(std::move(run_configuration)), + shell_args_(std::make_unique(std::move(settings), + on_create_platform_view, + on_create_rasterizer)), + external_texture_callback_(external_texture_callback) {} + +EmbedderEngine::~EmbedderEngine() = default; + +bool EmbedderEngine::LaunchShell() { + if (!shell_args_) { + FML_DLOG(ERROR) << "Invalid shell arguments."; + return false; } - is_valid_ = true; + if (shell_) { + FML_DLOG(ERROR) << "Shell already initialized"; + } + + shell_ = Shell::Create(task_runners_, shell_args_->settings, + shell_args_->on_create_platform_view, + shell_args_->on_create_rasterizer); + + // Reset the args no matter what. They will never be used to initialize a + // shell again. + shell_args_.reset(); + + return IsValid(); } -EmbedderEngine::~EmbedderEngine() = default; +bool EmbedderEngine::CollectShell() { + shell_.reset(); + return IsValid(); +} + +bool EmbedderEngine::RunRootIsolate() { + if (!IsValid() || !run_configuration_.IsValid()) { + return false; + } + shell_->RunEngine(std::move(run_configuration_)); + return true; +} bool EmbedderEngine::IsValid() const { - return is_valid_; + return static_cast(shell_); } const TaskRunners& EmbedderEngine::GetTaskRunners() const { @@ -59,14 +100,6 @@ bool EmbedderEngine::NotifyDestroyed() { return true; } -bool EmbedderEngine::Run(RunConfiguration run_configuration) { - if (!IsValid() || !run_configuration.IsValid()) { - return false; - } - shell_->RunEngine(std::move(run_configuration)); - return true; -} - bool EmbedderEngine::SetViewportMetrics(flutter::ViewportMetrics metrics) { if (!IsValid()) { return false; @@ -196,7 +229,10 @@ bool EmbedderEngine::PostRenderThreadTask(fml::closure task) { } bool EmbedderEngine::RunTask(const FlutterTask* task) { - if (!IsValid() || task == nullptr) { + // The shell doesn't need to be running or valid for access to the thread + // host. This is why there is no `IsValid` check here. This allows embedders + // to perform custom task runner interop before the shell is running. + if (task == nullptr) { return false; } return thread_host_->PostTask(reinterpret_cast(task->runner), diff --git a/engine/flutter/shell/platform/embedder/embedder_engine.h b/engine/flutter/shell/platform/embedder/embedder_engine.h index 639a55512bd6e57a4b967b87ad96efcbc833a954..f7863fbeb81236129bfebe94aeabe0c16b48b49e 100644 --- a/engine/flutter/shell/platform/embedder/embedder_engine.h +++ b/engine/flutter/shell/platform/embedder/embedder_engine.h @@ -18,6 +18,8 @@ namespace flutter { +struct ShellArgs; + // The object that is returned to the embedder as an opaque pointer to the // instance of the Flutter engine. class EmbedderEngine { @@ -25,8 +27,9 @@ class EmbedderEngine { using IdleCallback = std::function; EmbedderEngine(std::unique_ptr thread_host, - flutter::TaskRunners task_runners, - flutter::Settings settings, + TaskRunners task_runners, + Settings settings, + RunConfiguration run_configuration, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer, EmbedderExternalTextureGL::ExternalTextureCallback @@ -34,13 +37,17 @@ class EmbedderEngine { ~EmbedderEngine(); + bool LaunchShell(); + + bool CollectShell(); + const TaskRunners& GetTaskRunners() const; bool NotifyCreated(); bool NotifyDestroyed(); - bool Run(RunConfiguration run_configuration); + bool RunRootIsolate(); bool IsValid() const; @@ -70,10 +77,11 @@ class EmbedderEngine { private: const std::unique_ptr thread_host_; TaskRunners task_runners_; + RunConfiguration run_configuration_; + std::unique_ptr shell_args_; std::unique_ptr shell_; const EmbedderExternalTextureGL::ExternalTextureCallback external_texture_callback_; - bool is_valid_ = false; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderEngine); }; diff --git a/engine/flutter/shell/platform/embedder/embedder_task_runner.cc b/engine/flutter/shell/platform/embedder/embedder_task_runner.cc index b9b2d092e7a55b76b42ab445297347470df0d419..b31bff768c44d08313b154651d13c4e1f4d81c80 100644 --- a/engine/flutter/shell/platform/embedder/embedder_task_runner.cc +++ b/engine/flutter/shell/platform/embedder/embedder_task_runner.cc @@ -5,18 +5,26 @@ #include "flutter/shell/platform/embedder/embedder_task_runner.h" #include "flutter/fml/message_loop_impl.h" +#include "flutter/fml/message_loop_task_queues.h" namespace flutter { -EmbedderTaskRunner::EmbedderTaskRunner(DispatchTable table) +EmbedderTaskRunner::EmbedderTaskRunner(DispatchTable table, + size_t embedder_identifier) : TaskRunner(nullptr /* loop implemenation*/), - dispatch_table_(std::move(table)) { + embedder_identifier_(embedder_identifier), + dispatch_table_(std::move(table)), + placeholder_id_(fml::MessageLoopTaskQueues::GetInstance()->CreateTaskQueue()) { FML_DCHECK(dispatch_table_.post_task_callback); FML_DCHECK(dispatch_table_.runs_task_on_current_thread_callback); } EmbedderTaskRunner::~EmbedderTaskRunner() = default; +size_t EmbedderTaskRunner::GetEmbedderIdentifier() const { + return embedder_identifier_; +} + void EmbedderTaskRunner::PostTask(fml::closure task) { PostTaskForTime(task, fml::TimePoint::Now()); } @@ -69,4 +77,8 @@ bool EmbedderTaskRunner::PostTask(uint64_t baton) { return true; } +fml::TaskQueueId EmbedderTaskRunner::GetTaskQueueId() { + return placeholder_id_; +} + } // namespace flutter diff --git a/engine/flutter/shell/platform/embedder/embedder_task_runner.h b/engine/flutter/shell/platform/embedder/embedder_task_runner.h index 507648e652a650647663dd763783ab1cc7968358..0262c9314d8fa430e4f4efe1944605dd817c8c96 100644 --- a/engine/flutter/shell/platform/embedder/embedder_task_runner.h +++ b/engine/flutter/shell/platform/embedder/embedder_task_runner.h @@ -14,22 +14,69 @@ namespace flutter { +//------------------------------------------------------------------------------ +/// A task runner which delegates responsibility of task execution to an +/// embedder. This is done by managing a dispatch table to the embedder. +/// class EmbedderTaskRunner final : public fml::TaskRunner { public: struct DispatchTable { + //-------------------------------------------------------------------------- + /// Delegates responsibility of deferred task execution to the embedder. + /// Once the embedder gets the task, it must call + /// `EmbedderTaskRunner::PostTask` with the supplied `task_baton` on the + /// correct thread after the tasks `target_time` point expires. + /// std::function post_task_callback; + //-------------------------------------------------------------------------- + /// Asks the embedder if tasks posted to it on this task task runner via the + /// `post_task_callback` will be executed (after task expiry) on the calling + /// thread. + /// std::function runs_task_on_current_thread_callback; }; - EmbedderTaskRunner(DispatchTable table); + //---------------------------------------------------------------------------- + /// @brief Create a task runner with a dispatch table for delegation of + /// task runner responsibility to the embedder. When embedders + /// specify task runner dispatch tables that service tasks on the + /// same thread, they also must ensure that their + /// `embedder_idetifier`s match. This allows the engine to + /// determine task runner equality without actually posting tasks + /// to the task runner. + /// + /// @param[in] table The task runner dispatch table. + /// @param[in] embedder_identifier The embedder identifier + /// + EmbedderTaskRunner(DispatchTable table, size_t embedder_identifier); ~EmbedderTaskRunner() override; + //---------------------------------------------------------------------------- + /// @brief The unique identifier provided by the embedder for the task + /// runner. Embedders whose dispatch tables service tasks on the + /// same underlying OS thread must ensure that their identifiers + /// match. This allows the engine to determine task runner + /// equality without posting tasks on the thread. + /// + /// @return The embedder identifier. + /// + size_t GetEmbedderIdentifier() const; + bool PostTask(uint64_t baton); +private: + const size_t embedder_identifier_; + DispatchTable dispatch_table_; + std::mutex tasks_mutex_; + uint64_t last_baton_ FML_GUARDED_BY(tasks_mutex_); + std::unordered_map pending_tasks_ + FML_GUARDED_BY(tasks_mutex_); + fml::TaskQueueId placeholder_id_; + // |fml::TaskRunner| void PostTask(fml::closure task) override; @@ -42,12 +89,8 @@ class EmbedderTaskRunner final : public fml::TaskRunner { // |fml::TaskRunner| bool RunsTasksOnCurrentThread() override; - private: - DispatchTable dispatch_table_; - std::mutex tasks_mutex_; - uint64_t last_baton_ FML_GUARDED_BY(tasks_mutex_); - std::unordered_map pending_tasks_ - FML_GUARDED_BY(tasks_mutex_); + // |fml::TaskRunner| + fml::TaskQueueId GetTaskQueueId() override; FML_DISALLOW_COPY_AND_ASSIGN(EmbedderTaskRunner); }; diff --git a/engine/flutter/shell/platform/embedder/embedder_thread_host.cc b/engine/flutter/shell/platform/embedder/embedder_thread_host.cc index 5c2a1f3ae27ea9f12cfb1419e0ece7829e084304..23c071132e6683f3ecc384f4c2847b9774210ba6 100644 --- a/engine/flutter/shell/platform/embedder/embedder_thread_host.cc +++ b/engine/flutter/shell/platform/embedder/embedder_thread_host.cc @@ -2,33 +2,50 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// This is why we can't yet export the UI thread to embedders. #define FML_USED_ON_EMBEDDER #include "flutter/shell/platform/embedder/embedder_thread_host.h" +#include + #include "flutter/fml/message_loop.h" #include "flutter/shell/platform/embedder/embedder_safe_access.h" namespace flutter { -static fml::RefPtr CreateEmbedderTaskRunner( - const FlutterTaskRunnerDescription* description) { +//------------------------------------------------------------------------------ +/// @brief Attempts to create a task runner from an embedder task runner +/// description. The first boolean in the pair indicate whether the +/// embedder specified an invalid task runner description. In this +/// case, engine launch must be aborted. If the embedder did not +/// specify any task runner, an engine managed task runner and +/// thread must be selected instead. +/// +/// @param[in] description The description +/// +/// @return A pair that returns if the embedder has specified a task runner +/// (null otherwise) and whether to terminate further engine launch. +/// +static std::pair> +CreateEmbedderTaskRunner(const FlutterTaskRunnerDescription* description) { if (description == nullptr) { - return {}; + // This is not embedder error. The embedder API will just have to create a + // plain old task runner (and create a thread for it) instead of using a + // task runner provided to us by the embedder. + return {true, {}}; } if (SAFE_ACCESS(description, runs_task_on_current_thread_callback, nullptr) == nullptr) { FML_LOG(ERROR) << "FlutterTaskRunnerDescription.runs_task_on_current_" "thread_callback was nullptr."; - return {}; + return {false, {}}; } if (SAFE_ACCESS(description, post_task_callback, nullptr) == nullptr) { FML_LOG(ERROR) << "FlutterTaskRunnerDescription.post_task_callback was nullptr."; - return {}; + return {false, {}}; } auto user_data = SAFE_ACCESS(description, user_data, nullptr); @@ -57,7 +74,9 @@ static fml::RefPtr CreateEmbedderTaskRunner( return runs_task_on_current_thread_callback_c(user_data); }}; - return fml::MakeRefCounted(task_runner_dispatch_table); + return {true, fml::MakeRefCounted( + task_runner_dispatch_table, + SAFE_ACCESS(description, identifier, 0u))}; } std::unique_ptr @@ -70,10 +89,9 @@ EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost( } } - // Only attempt to create the engine managed host if the embedder did not - // specify a custom configuration. We don't want to fallback to the engine - // managed configuration if the embedder attempted to specify a configuration - // but messed up with an incorrect configuration. + // specify a custom configuration. Don't fallback to the engine managed + // configuration if the embedder attempted to specify a configuration but + // messed up with an incorrect configuration. if (custom_task_runners == nullptr) { auto host = CreateEngineManagedThreadHost(); if (host && host->IsValid()) { @@ -84,6 +102,11 @@ EmbedderThreadHost::CreateEmbedderOrEngineManagedThreadHost( return nullptr; } +static fml::RefPtr GetCurrentThreadTaskRunner() { + fml::MessageLoop::EnsureInitializedForCurrentThread(); + return fml::MessageLoop::GetCurrent().GetTaskRunner(); +} + constexpr const char* kFlutterThreadName = "io.flutter"; // static @@ -94,26 +117,64 @@ EmbedderThreadHost::CreateEmbedderManagedThreadHost( return nullptr; } - const auto platform_task_runner = CreateEmbedderTaskRunner( - SAFE_ACCESS(custom_task_runners, platform_task_runner, nullptr)); + // The UI and IO threads are always created by the engine and the embedder has + // no opportunity to specify task runners for the same. + // + // If/when more task runners are exposed, this mask will need to be updated. + uint64_t engine_thread_host_mask = + ThreadHost::Type::UI | ThreadHost::Type::IO; - // TODO(chinmaygarde): Add more here as we allow more threads to be controlled - // by the embedder. Create fallbacks as necessary. + auto platform_task_runner_pair = CreateEmbedderTaskRunner( + SAFE_ACCESS(custom_task_runners, platform_task_runner, nullptr)); + auto render_task_runner_pair = CreateEmbedderTaskRunner( + SAFE_ACCESS(custom_task_runners, render_task_runner, nullptr)); - if (!platform_task_runner) { + if (!platform_task_runner_pair.first || !render_task_runner_pair.first) { + // User error while supplying a custom task runner. Return an invalid thread + // host. This will abort engine initialization. Don't fallback to defaults + // if the user wanted to specify a task runner but just messed up instead. return nullptr; } - ThreadHost thread_host(kFlutterThreadName, ThreadHost::Type::GPU | - ThreadHost::Type::IO | - ThreadHost::Type::UI); + // If the embedder has not supplied a GPU task runner, one needs to be + // created. + if (!render_task_runner_pair.second) { + engine_thread_host_mask |= ThreadHost::Type::GPU; + } + + // If both the platform task runner and the GPU task runner are specified and + // have the same identifier, store only one. + if (platform_task_runner_pair.second && render_task_runner_pair.second) { + if (platform_task_runner_pair.second->GetEmbedderIdentifier() == + render_task_runner_pair.second->GetEmbedderIdentifier()) { + render_task_runner_pair.second = platform_task_runner_pair.second; + } + } + + // Create a thread host with just the threads that need to be managed by the + // engine. The embedder has provided the rest. + ThreadHost thread_host(kFlutterThreadName, engine_thread_host_mask); + + // If the embedder has supplied a platform task runner, use that. If not, use + // the current thread task runner. + auto platform_task_runner = platform_task_runner_pair.second + ? static_cast>( + platform_task_runner_pair.second) + : GetCurrentThreadTaskRunner(); + + // If the embedder has supplied a GPU task runner, use that. If not, use the + // one from our thread host. + auto render_task_runner = render_task_runner_pair.second + ? static_cast>( + render_task_runner_pair.second) + : thread_host.gpu_thread->GetTaskRunner(); flutter::TaskRunners task_runners( kFlutterThreadName, - platform_task_runner, // platform - thread_host.gpu_thread->GetTaskRunner(), // gpu - thread_host.ui_thread->GetTaskRunner(), // ui - thread_host.io_thread->GetTaskRunner() // io + platform_task_runner, // platform + render_task_runner, // gpu + thread_host.ui_thread->GetTaskRunner(), // ui (always engine managed) + thread_host.io_thread->GetTaskRunner() // io (always engine managed) ); if (!task_runners.IsValid()) { @@ -121,7 +182,14 @@ EmbedderThreadHost::CreateEmbedderManagedThreadHost( } std::set> embedder_task_runners; - embedder_task_runners.insert(platform_task_runner); + + if (platform_task_runner_pair.second) { + embedder_task_runners.insert(platform_task_runner_pair.second); + } + + if (render_task_runner_pair.second) { + embedder_task_runners.insert(render_task_runner_pair.second); + } auto embedder_host = std::make_unique( std::move(thread_host), std::move(task_runners), @@ -143,12 +211,10 @@ EmbedderThreadHost::CreateEngineManagedThreadHost() { ThreadHost::Type::IO | ThreadHost::Type::UI); - fml::MessageLoop::EnsureInitializedForCurrentThread(); - // For embedder platforms that don't have native message loop interop, this // will reference a task runner that points to a null message loop // implementation. - auto platform_task_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(); + auto platform_task_runner = GetCurrentThreadTaskRunner(); flutter::TaskRunners task_runners( kFlutterThreadName, diff --git a/engine/flutter/shell/platform/glfw/flutter_glfw.cc b/engine/flutter/shell/platform/glfw/flutter_glfw.cc index b557e4690ab1918eb51efe17e3c93d7148b9d7d3..0bf8301c5f6a59fb1ed5208c9b22a19402bfd9f7 100644 --- a/engine/flutter/shell/platform/glfw/flutter_glfw.cc +++ b/engine/flutter/shell/platform/glfw/flutter_glfw.cc @@ -511,6 +511,7 @@ FlutterDesktopWindowControllerRef FlutterDesktopCreateWindow( FlutterCustomTaskRunners custom_task_runners = {}; custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners); custom_task_runners.platform_task_runner = &platform_task_runner; + custom_task_runners.render_task_runner = &platform_task_runner; // Start the engine. state->engine =