diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index db1d53fc91ab7..0094daaa61796 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1979,6 +1979,381 @@ FlutterEngineResult FlutterEngineInitialize(size_t version, return kSuccess; } +FlutterEngineResult FlutterEngineSpawn(size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) + engine_spawner, + FLUTTER_API_SYMBOL(FlutterEngine) * + engine_out) { + // Step 0. Figure out arguments for shell spawn. + if (version != FLUTTER_ENGINE_VERSION) { + return LOG_EMBEDDER_ERROR( + kInvalidLibraryVersion, + "Flutter embedder version mismatch. There has been a breaking change. " + "Please consult the changelog and update the embedder."); + } + + if (engine_out == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, + "The engine out parameter was missing."); + } + + if (args == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, + "The Flutter project arguments were missing."); + } + + if (SAFE_ACCESS(args, assets_path, nullptr) == nullptr) { + return LOG_EMBEDDER_ERROR( + kInvalidArguments, + "The assets path in the Flutter project arguments was missing."); + } + + if (SAFE_ACCESS(args, main_path__unused__, nullptr) != nullptr) { + FML_LOG(WARNING) + << "FlutterProjectArgs.main_path is deprecated and should be set null."; + } + + if (SAFE_ACCESS(args, packages_path__unused__, nullptr) != nullptr) { + FML_LOG(WARNING) << "FlutterProjectArgs.packages_path is deprecated and " + "should be set null."; + } + + if (!IsRendererValid(config)) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, + "The renderer configuration was invalid."); + } + + std::string icu_data_path; + if (SAFE_ACCESS(args, icu_data_path, nullptr) != nullptr) { + icu_data_path = SAFE_ACCESS(args, icu_data_path, nullptr); + } + + if (SAFE_ACCESS(args, persistent_cache_path, nullptr) != nullptr) { + std::string persistent_cache_path = + SAFE_ACCESS(args, persistent_cache_path, nullptr); + flutter::PersistentCache::SetCacheDirectoryPath(persistent_cache_path); + } + + if (SAFE_ACCESS(args, is_persistent_cache_read_only, false)) { + flutter::PersistentCache::gIsReadOnly = true; + } + + fml::CommandLine command_line; + if (SAFE_ACCESS(args, command_line_argc, 0) != 0 && + SAFE_ACCESS(args, command_line_argv, nullptr) != nullptr) { + command_line = fml::CommandLineFromArgcArgv( + SAFE_ACCESS(args, command_line_argc, 0), + SAFE_ACCESS(args, command_line_argv, nullptr)); + } + + flutter::Settings settings = flutter::SettingsFromCommandLine(command_line); + + if (SAFE_ACCESS(args, aot_data, nullptr)) { + if (SAFE_ACCESS(args, vm_snapshot_data, nullptr) || + SAFE_ACCESS(args, vm_snapshot_instructions, nullptr) || + SAFE_ACCESS(args, isolate_snapshot_data, nullptr) || + SAFE_ACCESS(args, isolate_snapshot_instructions, nullptr)) { + return LOG_EMBEDDER_ERROR( + kInvalidArguments, + "Multiple AOT sources specified. Embedders should provide either " + "*_snapshot_* buffers or aot_data, not both."); + } + } + + if (flutter::DartVM::IsRunningPrecompiledCode()) { + PopulateAOTSnapshotMappingCallbacks(args, settings); + } else { + PopulateJITSnapshotMappingCallbacks(args, settings); + } + + settings.icu_data_path = icu_data_path; + settings.assets_path = args->assets_path; + settings.leak_vm = !SAFE_ACCESS(args, shutdown_dart_vm_when_done, false); + settings.old_gen_heap_size = SAFE_ACCESS(args, dart_old_gen_heap_size, -1); + + if (!flutter::DartVM::IsRunningPrecompiledCode()) { + // Verify the assets path contains Dart 2 kernel assets. + const std::string kApplicationKernelSnapshotFileName = "kernel_blob.bin"; + std::string application_kernel_path = fml::paths::JoinPaths( + {settings.assets_path, kApplicationKernelSnapshotFileName}); + if (!fml::IsFile(application_kernel_path)) { + return LOG_EMBEDDER_ERROR( + kInvalidArguments, + "Not running in AOT mode but could not resolve the kernel binary."); + } + settings.application_kernel_asset = kApplicationKernelSnapshotFileName; + } + + settings.task_observer_add = [](intptr_t key, const fml::closure& callback) { + fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback); + }; + settings.task_observer_remove = [](intptr_t key) { + fml::MessageLoop::GetCurrent().RemoveTaskObserver(key); + }; + if (SAFE_ACCESS(args, root_isolate_create_callback, nullptr) != nullptr) { + VoidCallback callback = + SAFE_ACCESS(args, root_isolate_create_callback, nullptr); + settings.root_isolate_create_callback = + [callback, user_data](const auto& isolate) { callback(user_data); }; + } + if (SAFE_ACCESS(args, log_message_callback, nullptr) != nullptr) { + FlutterLogMessageCallback callback = + SAFE_ACCESS(args, log_message_callback, nullptr); + settings.log_message_callback = [callback, user_data]( + const std::string& tag, + const std::string& message) { + callback(tag.c_str(), message.c_str(), user_data); + }; + } + if (SAFE_ACCESS(args, log_tag, nullptr) != nullptr) { + settings.log_tag = SAFE_ACCESS(args, log_tag, nullptr); + } + + bool has_update_semantics_2_callback = + SAFE_ACCESS(args, update_semantics_callback2, nullptr) != nullptr; + bool has_update_semantics_callback = + SAFE_ACCESS(args, update_semantics_callback, nullptr) != nullptr; + bool has_legacy_update_semantics_callback = + SAFE_ACCESS(args, update_semantics_node_callback, nullptr) != nullptr || + SAFE_ACCESS(args, update_semantics_custom_action_callback, nullptr) != + nullptr; + + int semantic_callback_count = (has_update_semantics_2_callback ? 1 : 0) + + (has_update_semantics_callback ? 1 : 0) + + (has_legacy_update_semantics_callback ? 1 : 0); + + if (semantic_callback_count > 1) { + return LOG_EMBEDDER_ERROR( + kInvalidArguments, + "Multiple semantics update callbacks provided. " + "Embedders should provide either `update_semantics_callback2`, " + "`update_semantics_callback`, or both " + "`update_semantics_node_callback` and " + "`update_semantics_custom_action_callback`."); + } + + flutter::PlatformViewEmbedder::UpdateSemanticsCallback + update_semantics_callback = + CreateEmbedderSemanticsUpdateCallback(args, user_data); + + flutter::PlatformViewEmbedder::PlatformMessageResponseCallback + platform_message_response_callback = nullptr; + if (SAFE_ACCESS(args, platform_message_callback, nullptr) != nullptr) { + platform_message_response_callback = + [ptr = args->platform_message_callback, + user_data](std::unique_ptr message) { + auto handle = new FlutterPlatformMessageResponseHandle(); + const FlutterPlatformMessage incoming_message = { + sizeof(FlutterPlatformMessage), // struct_size + message->channel().c_str(), // channel + message->data().GetMapping(), // message + message->data().GetSize(), // message_size + handle, // response_handle + }; + handle->message = std::move(message); + return ptr(&incoming_message, user_data); + }; + } + + flutter::VsyncWaiterEmbedder::VsyncCallback vsync_callback = nullptr; + if (SAFE_ACCESS(args, vsync_callback, nullptr) != nullptr) { + vsync_callback = [ptr = args->vsync_callback, user_data](intptr_t baton) { + return ptr(user_data, baton); + }; + } + + flutter::PlatformViewEmbedder::ComputePlatformResolvedLocaleCallback + compute_platform_resolved_locale_callback = nullptr; + if (SAFE_ACCESS(args, compute_platform_resolved_locale_callback, nullptr) != + nullptr) { + compute_platform_resolved_locale_callback = + [ptr = args->compute_platform_resolved_locale_callback]( + const std::vector& supported_locales_data) { + const size_t number_of_strings_per_locale = 3; + size_t locale_count = + supported_locales_data.size() / number_of_strings_per_locale; + std::vector supported_locales; + std::vector supported_locales_ptr; + for (size_t i = 0; i < locale_count; ++i) { + supported_locales.push_back( + {.struct_size = sizeof(FlutterLocale), + .language_code = + supported_locales_data[i * number_of_strings_per_locale + + 0] + .c_str(), + .country_code = + supported_locales_data[i * number_of_strings_per_locale + + 1] + .c_str(), + .script_code = + supported_locales_data[i * number_of_strings_per_locale + + 2] + .c_str(), + .variant_code = nullptr}); + supported_locales_ptr.push_back(&supported_locales[i]); + } + + const FlutterLocale* result = + ptr(supported_locales_ptr.data(), locale_count); + + std::unique_ptr> out = + std::make_unique>(); + if (result) { + std::string language_code(SAFE_ACCESS(result, language_code, "")); + if (language_code != "") { + out->push_back(language_code); + out->emplace_back(SAFE_ACCESS(result, country_code, "")); + out->emplace_back(SAFE_ACCESS(result, script_code, "")); + } + } + return out; + }; + } + + flutter::PlatformViewEmbedder::OnPreEngineRestartCallback + on_pre_engine_restart_callback = nullptr; + if (SAFE_ACCESS(args, on_pre_engine_restart_callback, nullptr) != nullptr) { + on_pre_engine_restart_callback = [ptr = + args->on_pre_engine_restart_callback, + user_data]() { return ptr(user_data); }; + } + + auto external_view_embedder_result = + InferExternalViewEmbedderFromArgs(SAFE_ACCESS(args, compositor, nullptr)); + if (external_view_embedder_result.second) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, + "Compositor arguments were invalid."); + } + + flutter::PlatformViewEmbedder::PlatformDispatchTable platform_dispatch_table = + { + update_semantics_callback, // + platform_message_response_callback, // + vsync_callback, // + compute_platform_resolved_locale_callback, // + on_pre_engine_restart_callback, // + }; + + auto on_create_platform_view = InferPlatformViewCreationCallback( + config, user_data, platform_dispatch_table, + std::move(external_view_embedder_result.first)); + + if (!on_create_platform_view) { + return LOG_EMBEDDER_ERROR( + kInternalInconsistency, + "Could not infer platform view creation callback."); + } + + flutter::Shell::CreateCallback on_create_rasterizer = + [](flutter::Shell& shell) { + return std::make_unique(shell); + }; + + using ExternalTextureResolver = flutter::EmbedderExternalTextureResolver; + std::unique_ptr external_texture_resolver; + external_texture_resolver = std::make_unique(); + +#ifdef SHELL_ENABLE_GL + flutter::EmbedderExternalTextureGL::ExternalTextureCallback + external_texture_callback; + if (config->type == kOpenGL) { + const FlutterOpenGLRendererConfig* open_gl_config = &config->open_gl; + if (SAFE_ACCESS(open_gl_config, gl_external_texture_frame_callback, + nullptr) != nullptr) { + external_texture_callback = + [ptr = open_gl_config->gl_external_texture_frame_callback, user_data]( + int64_t texture_identifier, size_t width, + size_t height) -> std::unique_ptr { + std::unique_ptr texture = + std::make_unique(); + if (!ptr(user_data, texture_identifier, width, height, texture.get())) { + return nullptr; + } + return texture; + }; + external_texture_resolver = + std::make_unique(external_texture_callback); + } + } +#endif +#ifdef SHELL_ENABLE_METAL + flutter::EmbedderExternalTextureMetal::ExternalTextureCallback + external_texture_metal_callback; + if (config->type == kMetal) { + const FlutterMetalRendererConfig* metal_config = &config->metal; + if (SAFE_ACCESS(metal_config, external_texture_frame_callback, nullptr)) { + external_texture_metal_callback = + [ptr = metal_config->external_texture_frame_callback, user_data]( + int64_t texture_identifier, size_t width, + size_t height) -> std::unique_ptr { + std::unique_ptr texture = + std::make_unique(); + texture->struct_size = sizeof(FlutterMetalExternalTexture); + if (!ptr(user_data, texture_identifier, width, height, texture.get())) { + return nullptr; + } + return texture; + }; + external_texture_resolver = std::make_unique( + external_texture_metal_callback); + } + } +#endif + + auto run_configuration = + flutter::RunConfiguration::InferFromSettings(settings); + + if (SAFE_ACCESS(args, custom_dart_entrypoint, nullptr) != nullptr) { + auto dart_entrypoint = std::string{args->custom_dart_entrypoint}; + if (!dart_entrypoint.empty()) { + run_configuration.SetEntrypoint(std::move(dart_entrypoint)); + } + } + + if (SAFE_ACCESS(args, dart_entrypoint_argc, 0) > 0) { + if (SAFE_ACCESS(args, dart_entrypoint_argv, nullptr) == nullptr) { + return LOG_EMBEDDER_ERROR(kInvalidArguments, + "Could not determine Dart entrypoint arguments " + "as dart_entrypoint_argc " + "was set, but dart_entrypoint_argv was null."); + } + std::vector arguments(args->dart_entrypoint_argc); + for (int i = 0; i < args->dart_entrypoint_argc; ++i) { + arguments[i] = std::string{args->dart_entrypoint_argv[i]}; + } + run_configuration.SetEntrypointArgs(std::move(arguments)); + } + + if (!run_configuration.IsValid()) { + return LOG_EMBEDDER_ERROR( + kInvalidArguments, + "Could not infer the Flutter project to run from given arguments."); + } + + // Spawn the engine by using spawner + auto embedder_engine_spawner = + reinterpret_cast(engine_spawner); + auto embedder_engine = embedder_engine_spawner->SpawnEmbedderEngine( + std::move(settings), // + std::move(run_configuration), // + on_create_platform_view, // + on_create_rasterizer, // + std::move(external_texture_resolver)); + + if (!embedder_engine->NotifyCreated()) { + return LOG_EMBEDDER_ERROR(kInternalInconsistency, + "Could not create platform view components."); + } + + // 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) { @@ -3074,6 +3449,7 @@ FlutterEngineResult FlutterEngineGetProcAddresses( SET_PROC(CreateAOTData, FlutterEngineCreateAOTData); SET_PROC(CollectAOTData, FlutterEngineCollectAOTData); SET_PROC(Run, FlutterEngineRun); + SET_PROC(Spawn, FlutterEngineSpawn); SET_PROC(Shutdown, FlutterEngineShutdown); SET_PROC(Initialize, FlutterEngineInitialize); SET_PROC(Deinitialize, FlutterEngineDeinitialize); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 5cdba06ef505d..bf1b4074b3a3c 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -2169,6 +2169,32 @@ FlutterEngineResult FlutterEngineRun(size_t version, engine_out); //------------------------------------------------------------------------------ +/// @brief Spawn a Flutter engine instance. It is implemented by modifying +/// `FlutterInitiaize`. Unlike `FlutterEngineInitialize`, +/// `FlutterEngineSpawn` uses the shell of given spawner engine to +/// spawn a engine instance. +/// +/// @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[in] spawner The spawner engine handle. +/// @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 FlutterEngineSpawn(size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) + spawner, + 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. @@ -2805,6 +2831,13 @@ typedef FlutterEngineResult (*FlutterEngineRunFnPtr)( const FlutterProjectArgs* args, void* user_data, FLUTTER_API_SYMBOL(FlutterEngine) * engine_out); +typedef FlutterEngineResult (*FlutterEngineSpawnFnPtr)( + size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) engine_spawner, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out); typedef FlutterEngineResult (*FlutterEngineShutdownFnPtr)( FLUTTER_API_SYMBOL(FlutterEngine) engine); typedef FlutterEngineResult (*FlutterEngineInitializeFnPtr)( @@ -2922,6 +2955,7 @@ typedef struct { FlutterEngineCreateAOTDataFnPtr CreateAOTData; FlutterEngineCollectAOTDataFnPtr CollectAOTData; FlutterEngineRunFnPtr Run; + FlutterEngineSpawnFnPtr Spawn; FlutterEngineShutdownFnPtr Shutdown; FlutterEngineInitializeFnPtr Initialize; FlutterEngineDeinitializeFnPtr Deinitialize; diff --git a/shell/platform/embedder/embedder_engine.cc b/shell/platform/embedder/embedder_engine.cc index cc4da738f72fc..eebea2a6fd604 100644 --- a/shell/platform/embedder/embedder_engine.cc +++ b/shell/platform/embedder/embedder_engine.cc @@ -22,14 +22,14 @@ struct ShellArgs { }; EmbedderEngine::EmbedderEngine( - std::unique_ptr thread_host, + std::shared_ptr thread_host, const flutter::TaskRunners& task_runners, const flutter::Settings& settings, RunConfiguration run_configuration, const Shell::CreateCallback& on_create_platform_view, const Shell::CreateCallback& on_create_rasterizer, std::unique_ptr external_texture_resolver) - : thread_host_(std::move(thread_host)), + : thread_host_(thread_host), task_runners_(task_runners), run_configuration_(std::move(run_configuration)), shell_args_(std::make_unique(settings, @@ -293,4 +293,29 @@ Shell& EmbedderEngine::GetShell() { return *shell_.get(); } +std::unique_ptr EmbedderEngine::SpawnEmbedderEngine( + flutter::Settings settings, + RunConfiguration run_configuration, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer, + std::unique_ptr + external_texture_resolver) { + auto engine = std::make_unique( + thread_host_, thread_host_->GetTaskRunners(), + std::move(settings), // + std::move(run_configuration), // + on_create_platform_view, // + on_create_rasterizer, // + std::move(external_texture_resolver)); + + auto shell = + shell_->Spawn(std::move(engine->run_configuration_), std::string(), + on_create_platform_view, on_create_rasterizer); + + engine->shell_ = std::move(shell); + engine->shell_args_.reset(); + + return engine; +} + } // namespace flutter diff --git a/shell/platform/embedder/embedder_engine.h b/shell/platform/embedder/embedder_engine.h index 60d33ba76e652..a9a7fb64b3463 100644 --- a/shell/platform/embedder/embedder_engine.h +++ b/shell/platform/embedder/embedder_engine.h @@ -23,7 +23,7 @@ struct ShellArgs; class EmbedderEngine { public: EmbedderEngine( - std::unique_ptr thread_host, + std::shared_ptr thread_host, const TaskRunners& task_runners, const Settings& settings, RunConfiguration run_configuration, @@ -86,8 +86,16 @@ class EmbedderEngine { Shell& GetShell(); + std::unique_ptr SpawnEmbedderEngine( + flutter::Settings settings, + RunConfiguration run_configuration, + Shell::CreateCallback on_create_platform_view, + Shell::CreateCallback on_create_rasterizer, + std::unique_ptr + external_texture_resolver); + private: - const std::unique_ptr thread_host_; + const std::shared_ptr thread_host_; TaskRunners task_runners_; RunConfiguration run_configuration_; std::unique_ptr shell_args_;