diff --git a/flutter/shell/platform/embedder/embedder.h b/flutter/shell/platform/embedder/embedder.h index 66a25c7..1ef6365 100644 --- a/flutter/shell/platform/embedder/embedder.h +++ b/flutter/shell/platform/embedder/embedder.h @@ -1824,6 +1824,16 @@ FlutterEngineResult FlutterEngineRun(size_t version, FLUTTER_API_SYMBOL(FlutterEngine) * engine_out); +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. @@ -2441,6 +2451,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)( @@ -2554,6 +2571,7 @@ typedef struct { FlutterEngineCreateAOTDataFnPtr CreateAOTData; FlutterEngineCollectAOTDataFnPtr CollectAOTData; FlutterEngineRunFnPtr Run; + FlutterEngineSpawnFnPtr Spawn; FlutterEngineShutdownFnPtr Shutdown; FlutterEngineInitializeFnPtr Initialize; FlutterEngineDeinitializeFnPtr Deinitialize; diff --git a/flutter/shell/platform/tizen/BUILD.gn b/flutter/shell/platform/tizen/BUILD.gn index 8cf28e3..17b4c62 100644 --- a/flutter/shell/platform/tizen/BUILD.gn +++ b/flutter/shell/platform/tizen/BUILD.gn @@ -97,6 +97,7 @@ template("embedder") { "flutter_tizen.cc", "flutter_tizen_elementary.cc", "flutter_tizen_engine.cc", + "flutter_tizen_engine_group.cc", "flutter_tizen_texture_registrar.cc", "flutter_tizen_view.cc", "logger.cc", diff --git a/flutter/shell/platform/tizen/flutter_tizen.cc b/flutter/shell/platform/tizen/flutter_tizen.cc index 981c035..eb14b94 100644 --- a/flutter/shell/platform/tizen/flutter_tizen.cc +++ b/flutter/shell/platform/tizen/flutter_tizen.cc @@ -10,6 +10,7 @@ #include "flutter/shell/platform/common/incoming_message_dispatcher.h" #include "flutter/shell/platform/tizen/flutter_project_bundle.h" #include "flutter/shell/platform/tizen/flutter_tizen_engine.h" +#include "flutter/shell/platform/tizen/flutter_tizen_engine_group.h" #include "flutter/shell/platform/tizen/flutter_tizen_view.h" #include "flutter/shell/platform/tizen/logger.h" #include "flutter/shell/platform/tizen/public/flutter_platform_view.h" @@ -71,20 +72,22 @@ FlutterDesktopEngineRef FlutterDesktopEngineCreate( } flutter::Logger::Start(); - auto engine = std::make_unique(project); - return HandleForEngine(engine.release()); + auto engine = + flutter::FlutterTizenEngineGroup::GetInstance().MakeEngineWithProject( + project); + + return HandleForEngine(engine); } bool FlutterDesktopEngineRun(const FlutterDesktopEngineRef engine) { - return EngineFromHandle(engine)->RunEngine(); + return EngineFromHandle(engine)->RunOrSpawnEngine(); } void FlutterDesktopEngineShutdown(FlutterDesktopEngineRef engine_ref) { flutter::Logger::Stop(); flutter::FlutterTizenEngine* engine = EngineFromHandle(engine_ref); - engine->StopEngine(); - delete engine; + flutter::FlutterTizenEngineGroup::GetInstance().StopEngine(engine->id()); } FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( @@ -219,12 +222,11 @@ FlutterDesktopViewRef FlutterDesktopViewCreateFromNewWindow( auto view = std::make_unique(std::move(window)); - // Take ownership of the engine, starting it if necessary. - view->SetEngine( - std::unique_ptr(EngineFromHandle(engine))); + // Starting it if necessary. + view->SetEngine(EngineFromHandle(engine)); view->CreateRenderSurface(window_properties.renderer_type); if (!view->engine()->IsRunning()) { - if (!view->engine()->RunEngine()) { + if (!view->engine()->RunOrSpawnEngine()) { return nullptr; } } diff --git a/flutter/shell/platform/tizen/flutter_tizen_elementary.cc b/flutter/shell/platform/tizen/flutter_tizen_elementary.cc index 9123930..4d094e7 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_elementary.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_elementary.cc @@ -33,12 +33,11 @@ FlutterDesktopViewRef FlutterDesktopViewCreateFromElmParent( auto view = std::make_unique(std::move(tizen_view)); - // Take ownership of the engine, starting it if necessary. - view->SetEngine( - std::unique_ptr(EngineFromHandle(engine))); + // Starting it if necessary. + view->SetEngine(EngineFromHandle(engine)); view->CreateRenderSurface(FlutterDesktopRendererType::kEvasGL); if (!view->engine()->IsRunning()) { - if (!view->engine()->RunEngine()) { + if (!view->engine()->RunOrSpawnEngine()) { return nullptr; } } diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.cc b/flutter/shell/platform/tizen/flutter_tizen_engine.cc index c364f66..4fb55be 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.cc @@ -14,6 +14,7 @@ #include "flutter/shell/platform/tizen/flutter_platform_node_delegate_tizen.h" #include "flutter/shell/platform/tizen/tizen_renderer_egl.h" #endif +#include "flutter/shell/platform/tizen/flutter_tizen_engine_group.h" #include "flutter/shell/platform/tizen/flutter_tizen_view.h" #include "flutter/shell/platform/tizen/logger.h" #include "flutter/shell/platform/tizen/system_utils.h" @@ -100,6 +101,14 @@ void FlutterTizenEngine::CreateRenderer( #endif } +bool FlutterTizenEngine::RunOrSpawnEngine() { + if (FlutterTizenEngineGroup::GetInstance().GetEngineCount() <= 1) { + return RunEngine(); + } else { + return SpawnEngine(); + } +} + bool FlutterTizenEngine::RunEngine() { if (engine_ != nullptr) { FT_LOG(Error) << "The engine has already started."; @@ -258,6 +267,131 @@ bool FlutterTizenEngine::RunEngine() { return true; } +bool FlutterTizenEngine::SpawnEngine() { + if (engine_ != nullptr) { + FT_LOG(Error) << "The engine has already started."; + return false; + } + if (IsHeaded() && !renderer_->IsValid()) { + FT_LOG(Error) << "The display was not valid."; + return false; + } + + if (!project_->HasValidPaths()) { + FT_LOG(Error) << "Missing or unresolvable path to assets."; + return false; + } + std::string assets_path_string = project_->assets_path().u8string(); + std::string icu_path_string = project_->icu_path().u8string(); + if (embedder_api_.RunsAOTCompiledDartCode()) { + aot_data_ = project_->LoadAotData(embedder_api_); + if (!aot_data_) { + FT_LOG(Error) << "Unable to start engine without AOT data."; + return false; + } + } + + // FlutterProjectArgs is expecting a full argv, so when processing it for + // flags the first item is treated as the executable and ignored. Add a dummy + // value so that all provided arguments are used. + std::vector engine_args = project_->engine_arguments(); + std::vector engine_argv = {"placeholder"}; + std::transform( + engine_args.begin(), engine_args.end(), std::back_inserter(engine_argv), + [](const std::string& arg) -> const char* { return arg.c_str(); }); + + const std::vector& entrypoint_args = + project_->dart_entrypoint_arguments(); + std::vector entrypoint_argv; + std::transform( + entrypoint_args.begin(), entrypoint_args.end(), + std::back_inserter(entrypoint_argv), + [](const std::string& arg) -> const char* { return arg.c_str(); }); + + FlutterProjectArgs args = {}; + args.struct_size = sizeof(FlutterProjectArgs); + args.assets_path = assets_path_string.c_str(); + args.icu_data_path = icu_path_string.c_str(); + args.command_line_argc = static_cast(engine_argv.size()); + args.command_line_argv = + engine_argv.size() > 0 ? engine_argv.data() : nullptr; + args.dart_entrypoint_argc = static_cast(entrypoint_argv.size()); + args.dart_entrypoint_argv = + entrypoint_argv.size() > 0 ? entrypoint_argv.data() : nullptr; + args.platform_message_callback = + [](const FlutterPlatformMessage* engine_message, void* user_data) { + if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) { + FT_LOG(Error) << "Invalid message size received. Expected: " + << sizeof(FlutterPlatformMessage) << ", but received " + << engine_message->struct_size; + return; + } + auto* engine = reinterpret_cast(user_data); + FlutterDesktopMessage message = + engine->ConvertToDesktopMessage(*engine_message); + engine->message_dispatcher_->HandleMessage(message); + }; + if (aot_data_) { + args.aot_data = aot_data_.get(); + } + if (!project_->custom_dart_entrypoint().empty()) { + args.custom_dart_entrypoint = project_->custom_dart_entrypoint().c_str(); + } +#ifndef WEARABLE_PROFILE + args.update_semantics_node_callback = OnUpdateSemanticsNode; + args.update_semantics_custom_action_callback = OnUpdateSemanticsCustomActions; + + if (IsHeaded() && dynamic_cast(renderer_.get())) { + vsync_waiter_ = std::make_unique(this); + args.vsync_callback = [](void* user_data, intptr_t baton) -> void { + auto* engine = reinterpret_cast(user_data); + engine->vsync_waiter_->AsyncWaitForVsync(baton); + }; + } +#endif + + auto* spawner = FlutterTizenEngineGroup::GetInstance().GetEngineSpawner(); + FlutterRendererConfig renderer_config = GetRendererConfig(); + FlutterEngineResult result = + embedder_api_.Spawn(FLUTTER_ENGINE_VERSION, &renderer_config, &args, this, + spawner->engine_, &engine_); + + if (result != kSuccess || engine_ == nullptr) { + FT_LOG(Error) << "Failed to start the Flutter engine with error: " + << result; + return false; + } + + internal_plugin_registrar_ = + std::make_unique(plugin_registrar_.get()); + accessibility_channel_ = std::make_unique( + internal_plugin_registrar_->messenger()); + app_control_channel_ = std::make_unique( + internal_plugin_registrar_->messenger()); + lifecycle_channel_ = std::make_unique( + internal_plugin_registrar_->messenger()); + settings_channel_ = std::make_unique( + internal_plugin_registrar_->messenger()); + + if (IsHeaded()) { + texture_registrar_ = std::make_unique(this); + key_event_channel_ = std::make_unique( + internal_plugin_registrar_->messenger(), + [this](const FlutterKeyEvent& event, FlutterKeyEventCallback callback, + void* user_data) { SendKeyEvent(event, callback, user_data); }); + navigation_channel_ = std::make_unique( + internal_plugin_registrar_->messenger()); + platform_view_channel_ = std::make_unique( + internal_plugin_registrar_->messenger()); + } + + accessibility_settings_ = std::make_unique(this); + + SetupLocales(); + + return true; +} + bool FlutterTizenEngine::StopEngine() { if (engine_) { if (platform_view_channel_) { diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.h b/flutter/shell/platform/tizen/flutter_tizen_engine.h index 6e559d7..1989942 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.h +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.h @@ -62,12 +62,19 @@ class FlutterTizenEngine { // Creates a GL renderer from the given type. void CreateRenderer(FlutterDesktopRendererType renderer_type); + // If the engine is the only engine of the engine group, starts running the + // engine. Otherwise, spawns the engine. + bool RunOrSpawnEngine(); + // Starts running the engine with the given entrypoint. If null, defaults to // main(). // // Returns false if the engine couldn't be started. bool RunEngine(); + // Returns false if the engine couldn't be spawned. + bool SpawnEngine(); + // Returns true if the engine is currently running. bool IsRunning() { return engine_ != nullptr; } @@ -81,6 +88,10 @@ class FlutterTizenEngine { // headless engines. FlutterTizenView* view() { return view_; } + void SetId(uint32_t id) { id_ = id; } + + uint32_t id() { return id_; } + FlutterDesktopMessengerRef messenger() { return messenger_.get(); } IncomingMessageDispatcher* message_dispatcher() { @@ -288,6 +299,8 @@ class FlutterTizenEngine { // The vsync waiter for the embedder. std::unique_ptr vsync_waiter_; #endif + + uint32_t id_; }; } // namespace flutter diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine_group.cc b/flutter/shell/platform/tizen/flutter_tizen_engine_group.cc new file mode 100644 index 0000000..06d423c --- /dev/null +++ b/flutter/shell/platform/tizen/flutter_tizen_engine_group.cc @@ -0,0 +1,40 @@ +#include "flutter/shell/platform/tizen/flutter_tizen_engine_group.h" + +#include + +namespace flutter { + +FlutterTizenEngine* FlutterTizenEngineGroup::MakeEngineWithProject( + const FlutterProjectBundle& project) { + std::unique_ptr engine = + std::make_unique(project); + + static std::random_device random_device; + static std::mt19937 generator(random_device()); + static std::uniform_int_distribution distribution(0, 99999); + + engine->SetId(distribution(generator)); + + engines_.push_back(std::move(engine)); + return engines_.back().get(); +} + +FlutterTizenEngine* FlutterTizenEngineGroup::GetEngineSpawner() { + return engines_[0].get(); +} + +int FlutterTizenEngineGroup::GetEngineCount() { + return engines_.size(); +} + +void FlutterTizenEngineGroup::StopEngine(uint32_t id) { + for (auto it = engines_.begin(); it != engines_.end(); ++it) { + if (id == it->get()->id()) { + it->get()->StopEngine(); + engines_.erase(it); + return; + } + } +} + +} // namespace flutter diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine_group.h b/flutter/shell/platform/tizen/flutter_tizen_engine_group.h new file mode 100644 index 0000000..fdd4791 --- /dev/null +++ b/flutter/shell/platform/tizen/flutter_tizen_engine_group.h @@ -0,0 +1,32 @@ +#ifndef EMBEDDER_FLUTTER_TIZEN_ENGINE_GROUP_H_ +#define EMBEDDER_FLUTTER_TIZEN_ENGINE_GROUP_H_ + +#include "flutter/shell/platform/tizen/flutter_tizen_engine.h" + +namespace flutter { + +class FlutterTizenEngineGroup { + public: + static FlutterTizenEngineGroup& GetInstance() { + static FlutterTizenEngineGroup instance; + return instance; + } + + FlutterTizenEngine* GetEngineSpawner(); + + FlutterTizenEngine* MakeEngineWithProject( + const FlutterProjectBundle& project); + + int GetEngineCount(); + + void StopEngine(uint32_t id); + + private: + FlutterTizenEngineGroup() {} + + std::vector> engines_; +}; + +} // namespace flutter + +#endif diff --git a/flutter/shell/platform/tizen/flutter_tizen_nui.cc b/flutter/shell/platform/tizen/flutter_tizen_nui.cc index f9ad52e..e5a1b88 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_nui.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_nui.cc @@ -42,12 +42,11 @@ FlutterDesktopViewRef FlutterDesktopViewCreateFromImageView( auto view = std::make_unique(std::move(tizen_view)); - // Take ownership of the engine, starting it if necessary. - view->SetEngine( - std::unique_ptr(EngineFromHandle(engine))); + // Starting it if necessary. + view->SetEngine(EngineFromHandle(engine)); view->CreateRenderSurface(FlutterDesktopRendererType::kEGL); if (!view->engine()->IsRunning()) { - if (!view->engine()->RunEngine()) { + if (!view->engine()->RunOrSpawnEngine()) { return nullptr; } } diff --git a/flutter/shell/platform/tizen/flutter_tizen_view.cc b/flutter/shell/platform/tizen/flutter_tizen_view.cc index 4cb5a97..0918acd 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_view.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_view.cc @@ -5,6 +5,7 @@ #include "flutter_tizen_view.h" +#include "flutter/shell/platform/tizen/flutter_tizen_engine_group.h" #include "flutter/shell/platform/tizen/logger.h" #include "flutter/shell/platform/tizen/tizen_view.h" #ifdef NUI_SUPPORT @@ -60,13 +61,13 @@ FlutterTizenView::FlutterTizenView(std::unique_ptr tizen_view) FlutterTizenView::~FlutterTizenView() { if (engine_) { - engine_->StopEngine(); + flutter::FlutterTizenEngineGroup::GetInstance().StopEngine(engine_->id()); } DestroyRenderSurface(); } -void FlutterTizenView::SetEngine(std::unique_ptr engine) { - engine_ = std::move(engine); +void FlutterTizenView::SetEngine(FlutterTizenEngine* engine) { + engine_ = engine; engine_->SetView(this); internal_plugin_registrar_ = @@ -275,8 +276,7 @@ void FlutterTizenView::OnKey(const char* key, if (engine_->key_event_channel()) { engine_->key_event_channel()->SendKey( key, string, compose, modifiers, scan_code, is_down, - [engine = engine_.get(), symbol = std::string(key), - is_down](bool handled) { + [engine = engine_, symbol = std::string(key), is_down](bool handled) { if (handled) { return; } diff --git a/flutter/shell/platform/tizen/flutter_tizen_view.h b/flutter/shell/platform/tizen/flutter_tizen_view.h index 2b1501a..80c88ad 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_view.h +++ b/flutter/shell/platform/tizen/flutter_tizen_view.h @@ -28,9 +28,9 @@ class FlutterTizenView : public TizenViewEventHandlerDelegate { // Configures the window instance with an instance of a running Flutter // engine. - void SetEngine(std::unique_ptr engine); + void SetEngine(FlutterTizenEngine* engine); - FlutterTizenEngine* engine() { return engine_.get(); } + FlutterTizenEngine* engine() { return engine_; } TizenViewBase* tizen_view() { return tizen_view_.get(); } @@ -131,7 +131,7 @@ class FlutterTizenView : public TizenViewEventHandlerDelegate { int device_id); // The engine associated with this view. - std::unique_ptr engine_; + FlutterTizenEngine* engine_; // The platform view associated with this Flutter view. std::unique_ptr tizen_view_;