diff --git a/engine/src/flutter/shell/platform/linux/fl_dart_project.cc b/engine/src/flutter/shell/platform/linux/fl_dart_project.cc index 43d98070a8906..6c626c61105a1 100644 --- a/engine/src/flutter/shell/platform/linux/fl_dart_project.cc +++ b/engine/src/flutter/shell/platform/linux/fl_dart_project.cc @@ -16,6 +16,7 @@ struct _FlDartProject { FlUIThreadPolicy ui_thread_policy; gboolean enable_impeller; + gboolean enable_flutter_gpu; }; G_DEFINE_TYPE(FlDartProject, fl_dart_project, G_TYPE_OBJECT) @@ -145,3 +146,16 @@ gboolean fl_dart_project_get_enable_impeller(FlDartProject* project) { g_return_val_if_fail(FL_IS_DART_PROJECT(project), FALSE); return project->enable_impeller; } + +G_MODULE_EXPORT +void fl_dart_project_set_enable_flutter_gpu(FlDartProject* project, + gboolean enable_flutter_gpu) { + g_return_if_fail(FL_IS_DART_PROJECT(project)); + project->enable_flutter_gpu = enable_flutter_gpu; +} + +G_MODULE_EXPORT +gboolean fl_dart_project_get_enable_flutter_gpu(FlDartProject* project) { + g_return_val_if_fail(FL_IS_DART_PROJECT(project), FALSE); + return project->enable_flutter_gpu; +} diff --git a/engine/src/flutter/shell/platform/linux/fl_dart_project_test.cc b/engine/src/flutter/shell/platform/linux/fl_dart_project_test.cc index dc0d1d2a9498b..93e570e535e79 100644 --- a/engine/src/flutter/shell/platform/linux/fl_dart_project_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_dart_project_test.cc @@ -80,3 +80,13 @@ TEST_F(FlDartProjectTest, EnableImpeller) { fl_dart_project_set_enable_impeller(project, TRUE); EXPECT_TRUE(fl_dart_project_get_enable_impeller(project)); } + +TEST_F(FlDartProjectTest, EnableFlutterGpu) { + EXPECT_FALSE(fl_dart_project_get_enable_flutter_gpu(project)); + + fl_dart_project_set_enable_flutter_gpu(project, TRUE); + EXPECT_TRUE(fl_dart_project_get_enable_flutter_gpu(project)); + + fl_dart_project_set_enable_flutter_gpu(project, FALSE); + EXPECT_FALSE(fl_dart_project_get_enable_flutter_gpu(project)); +} diff --git a/engine/src/flutter/shell/platform/linux/fl_engine.cc b/engine/src/flutter/shell/platform/linux/fl_engine.cc index 6c7b6d2ae3ccf..f88d08cb05f47 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine.cc @@ -812,9 +812,12 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { break; } + const std::vector env_switches = + flutter::GetSwitchesFromEnvironment(); + gboolean enable_impeller = fl_dart_project_get_enable_impeller(self->project); gboolean has_enable_impeller = FALSE; - for (const auto& env_switch : flutter::GetSwitchesFromEnvironment()) { + for (const auto& env_switch : env_switches) { if (env_switch == "--enable-impeller" || env_switch == "--enable-impeller=true") { enable_impeller = TRUE; @@ -828,7 +831,7 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { g_autoptr(GPtrArray) command_line_args = g_ptr_array_new_with_free_func(g_free); g_ptr_array_insert(command_line_args, 0, g_strdup("flutter")); - for (const auto& env_switch : flutter::GetSwitchesFromEnvironment()) { + for (const auto& env_switch : env_switches) { g_ptr_array_add(command_line_args, g_strdup(env_switch.c_str())); } // Linux (and other desktop platforms) always uses SDFs. @@ -838,6 +841,23 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { g_ptr_array_add(command_line_args, g_strdup("--enable-impeller")); } + // Forward the project's Flutter GPU setting unless an environment switch + // already carries it (the switch is presence based, so it is only ever + // added, never negated). + if (fl_dart_project_get_enable_flutter_gpu(self->project)) { + gboolean has_enable_flutter_gpu = FALSE; + for (const auto& env_switch : env_switches) { + if (env_switch == "--enable-flutter-gpu" || + env_switch == "--enable-flutter-gpu=true") { + has_enable_flutter_gpu = TRUE; + break; + } + } + if (!has_enable_flutter_gpu) { + g_ptr_array_add(command_line_args, g_strdup("--enable-flutter-gpu")); + } + } + gchar** dart_entrypoint_args = fl_dart_project_get_dart_entrypoint_arguments(self->project); diff --git a/engine/src/flutter/shell/platform/linux/fl_engine_test.cc b/engine/src/flutter/shell/platform/linux/fl_engine_test.cc index 2e32e301a84bc..dd2528e248223 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine_test.cc @@ -1008,6 +1008,58 @@ TEST_F(FlEngineTest, DisableImpeller) { EXPECT_TRUE(called); } +TEST_F(FlEngineTest, EnableFlutterGpuDefault) { + bool called = false; + fl_engine_get_embedder_api(engine)->Initialize = MOCK_ENGINE_PROC( + Initialize, + ([&called](size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + called = true; + bool has_flutter_gpu_switch = false; + for (int i = 0; i < args->command_line_argc; i++) { + if (strcmp(args->command_line_argv[i], "--enable-flutter-gpu") == 0) { + has_flutter_gpu_switch = true; + } + } + EXPECT_FALSE(has_flutter_gpu_switch); + return kSuccess; + })); + fl_engine_get_embedder_api(engine)->RunInitialized = + MOCK_ENGINE_PROC(RunInitialized, ([](auto engine) { return kSuccess; })); + + StartEngine(); + EXPECT_TRUE(called); +} + +TEST_F(FlEngineTest, EnableFlutterGpu) { + fl_dart_project_set_enable_flutter_gpu(project, TRUE); + + bool called = false; + fl_engine_get_embedder_api(engine)->Initialize = MOCK_ENGINE_PROC( + Initialize, + ([&called](size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + called = true; + bool has_flutter_gpu_switch = false; + for (int i = 0; i < args->command_line_argc; i++) { + if (strcmp(args->command_line_argv[i], "--enable-flutter-gpu") == 0) { + has_flutter_gpu_switch = true; + } + } + EXPECT_TRUE(has_flutter_gpu_switch); + return kSuccess; + })); + fl_engine_get_embedder_api(engine)->RunInitialized = + MOCK_ENGINE_PROC(RunInitialized, ([](auto engine) { return kSuccess; })); + + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + EXPECT_EQ(error, nullptr); + EXPECT_TRUE(called); +} + TEST_F(FlEngineTest, ChildObjects) { // Check objects exist before engine started. EXPECT_NE(fl_engine_get_binary_messenger(engine), nullptr); diff --git a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h index 0b50eac76024b..4b83173a3557d 100644 --- a/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h +++ b/engine/src/flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h @@ -178,6 +178,25 @@ void fl_dart_project_set_enable_impeller(FlDartProject* project, */ gboolean fl_dart_project_get_enable_impeller(FlDartProject* project); +/** + * fl_dart_project_set_enable_flutter_gpu: + * @project: an #FlDartProject. + * @enable_flutter_gpu: whether to enable the Flutter GPU API. + * + * Sets whether the Flutter GPU API (package:flutter_gpu) should be enabled. + * Flutter GPU requires the Impeller renderer. + */ +void fl_dart_project_set_enable_flutter_gpu(FlDartProject* project, + gboolean enable_flutter_gpu); + +/** + * fl_dart_project_get_enable_flutter_gpu: + * @project: an #FlDartProject. + * + * Returns: %TRUE if the Flutter GPU API is enabled. + */ +gboolean fl_dart_project_get_enable_flutter_gpu(FlDartProject* project); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_PUBLIC_FLUTTER_LINUX_FL_DART_PROJECT_H_ diff --git a/engine/src/flutter/shell/platform/windows/client_wrapper/dart_project_unittests.cc b/engine/src/flutter/shell/platform/windows/client_wrapper/dart_project_unittests.cc index bfc151947f673..e2a8746bb20a4 100644 --- a/engine/src/flutter/shell/platform/windows/client_wrapper/dart_project_unittests.cc +++ b/engine/src/flutter/shell/platform/windows/client_wrapper/dart_project_unittests.cc @@ -55,4 +55,15 @@ TEST_F(DartProjectTest, DartEntrypointArguments) { EXPECT_EQ(returned_arguments[2], "arg3"); } +TEST_F(DartProjectTest, EnableFlutterGpu) { + DartProject project(L"test"); + EXPECT_FALSE(project.enable_flutter_gpu()); + + project.set_enable_flutter_gpu(true); + EXPECT_TRUE(project.enable_flutter_gpu()); + + project.set_enable_flutter_gpu(false); + EXPECT_FALSE(project.enable_flutter_gpu()); +} + } // namespace flutter diff --git a/engine/src/flutter/shell/platform/windows/client_wrapper/flutter_engine.cc b/engine/src/flutter/shell/platform/windows/client_wrapper/flutter_engine.cc index f5340fc558ea8..fb2a31ddf5761 100644 --- a/engine/src/flutter/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/engine/src/flutter/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -27,6 +27,7 @@ FlutterEngine::FlutterEngine(const DartProject& project) { project.accessibility_mode()); c_engine_properties.impeller_switch = static_cast(project.impeller_switch()); + c_engine_properties.enable_flutter_gpu = project.enable_flutter_gpu(); const std::vector& entrypoint_args = project.dart_entrypoint_arguments(); diff --git a/engine/src/flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h b/engine/src/flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h index 4a2e830cafb57..2dc37d201fbc9 100644 --- a/engine/src/flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h +++ b/engine/src/flutter/shell/platform/windows/client_wrapper/include/flutter/dart_project.h @@ -153,6 +153,16 @@ class DartProject { // Defaults to ImpellerSwitch::Default. ImpellerSwitch impeller_switch() const { return impeller_switch_; } + // Sets whether the Flutter GPU API (package:flutter_gpu) is enabled. + // Flutter GPU requires the Impeller renderer. + void set_enable_flutter_gpu(bool enable_flutter_gpu) { + enable_flutter_gpu_ = enable_flutter_gpu; + } + + // Returns whether the Flutter GPU API is enabled. + // Defaults to false. + bool enable_flutter_gpu() const { return enable_flutter_gpu_; } + private: // Accessors for internals are private, so that they can be changed if more // flexible options for project structures are needed later without it @@ -185,6 +195,8 @@ class DartProject { AccessibilityMode accessibility_mode_ = AccessibilityMode::Default; // The Impeller enablement switch. ImpellerSwitch impeller_switch_ = ImpellerSwitch::Default; + // Whether the Flutter GPU API is enabled. + bool enable_flutter_gpu_ = false; }; } // namespace flutter diff --git a/engine/src/flutter/shell/platform/windows/flutter_project_bundle.cc b/engine/src/flutter/shell/platform/windows/flutter_project_bundle.cc index 03f20fb586976..63cfead0e0d64 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_project_bundle.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_project_bundle.cc @@ -42,6 +42,8 @@ FlutterProjectBundle::FlutterProjectBundle( impeller_switch_ = static_cast(properties.impeller_switch); + enable_flutter_gpu_ = properties.enable_flutter_gpu; + // Resolve any relative paths. if (assets_path_.is_relative() || icu_path_.is_relative() || (!aot_library_path_.empty() && aot_library_path_.is_relative())) { diff --git a/engine/src/flutter/shell/platform/windows/flutter_project_bundle.h b/engine/src/flutter/shell/platform/windows/flutter_project_bundle.h index 59f7041d39da3..f504ea8555687 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_project_bundle.h +++ b/engine/src/flutter/shell/platform/windows/flutter_project_bundle.h @@ -97,6 +97,9 @@ class FlutterProjectBundle { // Returns the Impeller enablement switch. FlutterImpellerSwitch impeller_switch() const { return impeller_switch_; } + // Returns whether the Flutter GPU API is enabled. + bool enable_flutter_gpu() const { return enable_flutter_gpu_; } + private: std::filesystem::path assets_path_; std::filesystem::path icu_path_; @@ -124,6 +127,9 @@ class FlutterProjectBundle { // The Impeller enablement switch. FlutterImpellerSwitch impeller_switch_ = FlutterImpellerSwitch::Default; + + // Whether the Flutter GPU API is enabled. + bool enable_flutter_gpu_ = false; }; } // namespace flutter diff --git a/engine/src/flutter/shell/platform/windows/flutter_project_bundle_unittests.cc b/engine/src/flutter/shell/platform/windows/flutter_project_bundle_unittests.cc index 607d1cc707d99..c30d7f1ee8a4d 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_project_bundle_unittests.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_project_bundle_unittests.cc @@ -34,6 +34,19 @@ TEST(FlutterProjectBundle, BasicPropertiesRelativePaths) { EXPECT_EQ(project.icu_path().filename().string(), "icudtl.dat"); } +TEST(FlutterProjectBundle, EnableFlutterGpu) { + FlutterDesktopEngineProperties properties = {}; + properties.assets_path = L"foo\\flutter_assets"; + properties.icu_data_path = L"foo\\icudtl.dat"; + + FlutterProjectBundle default_project(properties); + EXPECT_FALSE(default_project.enable_flutter_gpu()); + + properties.enable_flutter_gpu = true; + FlutterProjectBundle project(properties); + EXPECT_TRUE(project.enable_flutter_gpu()); +} + TEST(FlutterProjectBundle, SwitchesEmpty) { FlutterDesktopEngineProperties properties = {}; properties.assets_path = L"foo\\flutter_assets"; diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc index 5275e5b612424..d9f797ca257ba 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine.cc @@ -326,6 +326,16 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { switches.push_back("--enable-impeller=false"); } } + if (project_->enable_flutter_gpu()) { + if (std::find(switches.begin(), switches.end(), "--enable-flutter-gpu") == + switches.end() && + std::find(switches.begin(), switches.end(), + "--enable-flutter-gpu=true") == switches.end()) { + // Flutter GPU was enabled programmatically, so forward the switch to + // the engine. + switches.push_back("--enable-flutter-gpu"); + } + } std::transform( switches.begin(), switches.end(), std::back_inserter(argv), [](const std::string& arg) -> const char* { return arg.c_str(); }); diff --git a/engine/src/flutter/shell/platform/windows/flutter_windows_engine_unittests.cc b/engine/src/flutter/shell/platform/windows/flutter_windows_engine_unittests.cc index 23daedc82ed14..23e127cbc3dfa 100644 --- a/engine/src/flutter/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/engine/src/flutter/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -534,6 +534,115 @@ TEST_F(FlutterWindowsEngineTest, RunWithProjectFlagEnableImpeller) { modifier.ReleaseEGLManager(); } +TEST_F(FlutterWindowsEngineTest, RunWithProjectFlagEnableFlutterGpu) { + FlutterWindowsEngineBuilder builder{GetContext()}; + builder.SetEnableFlutterGpu(true); + std::unique_ptr engine = builder.Build(); + EngineModifier modifier(engine.get()); + + modifier.embedder_api().NotifyDisplayUpdate = + MOCK_ENGINE_PROC(NotifyDisplayUpdate, + ([](FLUTTER_API_SYMBOL(FlutterEngine) raw_engine, + const FlutterEngineDisplaysUpdateType update_type, + const FlutterEngineDisplay* embedder_displays, + size_t display_count) { return kSuccess; })); + + modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC( + UpdateAccessibilityFeatures, + [](FLUTTER_API_SYMBOL(FlutterEngine) engine, + FlutterAccessibilityFeature flags) { return kSuccess; }); + + modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC( + UpdateLocales, ([](auto engine, const FlutterLocale** locales, + size_t locales_count) { return kSuccess; })); + + modifier.embedder_api().SendPlatformMessage = + MOCK_ENGINE_PROC(SendPlatformMessage, + ([](auto engine, auto message) { return kSuccess; })); + + bool run_called = false; + modifier.embedder_api().Run = MOCK_ENGINE_PROC( + Run, ([&run_called](size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + run_called = true; + *engine_out = reinterpret_cast(1); + + bool has_flutter_gpu_switch = false; + for (int i = 0; i < args->command_line_argc; ++i) { + if (strcmp(args->command_line_argv[i], "--enable-flutter-gpu") == 0) { + has_flutter_gpu_switch = true; + } + } + EXPECT_TRUE(has_flutter_gpu_switch); + return kSuccess; + })); + + // Set the EGL manager to !nullptr to test ANGLE rendering. + modifier.SetEGLManager(std::make_unique()); + + engine->Run(); + + EXPECT_TRUE(run_called); + + modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; }; + modifier.ReleaseEGLManager(); +} + +TEST_F(FlutterWindowsEngineTest, RunWithoutProjectFlagEnableFlutterGpu) { + FlutterWindowsEngineBuilder builder{GetContext()}; + std::unique_ptr engine = builder.Build(); + EngineModifier modifier(engine.get()); + + modifier.embedder_api().NotifyDisplayUpdate = + MOCK_ENGINE_PROC(NotifyDisplayUpdate, + ([](FLUTTER_API_SYMBOL(FlutterEngine) raw_engine, + const FlutterEngineDisplaysUpdateType update_type, + const FlutterEngineDisplay* embedder_displays, + size_t display_count) { return kSuccess; })); + + modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC( + UpdateAccessibilityFeatures, + [](FLUTTER_API_SYMBOL(FlutterEngine) engine, + FlutterAccessibilityFeature flags) { return kSuccess; }); + + modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC( + UpdateLocales, ([](auto engine, const FlutterLocale** locales, + size_t locales_count) { return kSuccess; })); + + modifier.embedder_api().SendPlatformMessage = + MOCK_ENGINE_PROC(SendPlatformMessage, + ([](auto engine, auto message) { return kSuccess; })); + + bool run_called = false; + modifier.embedder_api().Run = MOCK_ENGINE_PROC( + Run, ([&run_called](size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + run_called = true; + *engine_out = reinterpret_cast(1); + + bool has_flutter_gpu_switch = false; + for (int i = 0; i < args->command_line_argc; ++i) { + if (strcmp(args->command_line_argv[i], "--enable-flutter-gpu") == 0) { + has_flutter_gpu_switch = true; + } + } + EXPECT_FALSE(has_flutter_gpu_switch); + return kSuccess; + })); + + // Set the EGL manager to !nullptr to test ANGLE rendering. + modifier.SetEGLManager(std::make_unique()); + + engine->Run(); + + EXPECT_TRUE(run_called); + + modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; }; + modifier.ReleaseEGLManager(); +} + TEST_F(FlutterWindowsEngineTest, RunWithProjectFlagDisableImpeller) { FlutterWindowsEngineBuilder builder{GetContext()}; builder.SetImpellerSwitch(DisabledImpeller); diff --git a/engine/src/flutter/shell/platform/windows/public/flutter_windows.h b/engine/src/flutter/shell/platform/windows/public/flutter_windows.h index 54954042f47cd..2194cca17201b 100644 --- a/engine/src/flutter/shell/platform/windows/public/flutter_windows.h +++ b/engine/src/flutter/shell/platform/windows/public/flutter_windows.h @@ -128,6 +128,11 @@ typedef struct { // Policy for enabling the Impeller renderer. FlutterDesktopImpellerSwitch impeller_switch; + + // Whether to enable the Flutter GPU API (package:flutter_gpu). + // Flutter GPU requires the Impeller renderer. + // If not set defaults to false. + bool enable_flutter_gpu; } FlutterDesktopEngineProperties; // ========== View Controller ========== diff --git a/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.cc b/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.cc index 62c1a407ff2e8..28a302a2d6e82 100644 --- a/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.cc +++ b/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.cc @@ -75,6 +75,10 @@ void FlutterWindowsEngineBuilder::SetImpellerSwitch( properties_.impeller_switch = impeller_switch; } +void FlutterWindowsEngineBuilder::SetEnableFlutterGpu(bool enable_flutter_gpu) { + properties_.enable_flutter_gpu = enable_flutter_gpu; +} + void FlutterWindowsEngineBuilder::SetCreateKeyboardHandlerCallbacks( KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state, KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan) { diff --git a/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h b/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h index a10d11394d7ae..2f2ef89ea7df1 100644 --- a/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h +++ b/engine/src/flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h @@ -33,6 +33,8 @@ class FlutterWindowsEngineBuilder { void SetImpellerSwitch(FlutterDesktopImpellerSwitch impeller_switch); + void SetEnableFlutterGpu(bool enable_flutter_gpu); + void SetWindowsProcTable( std::shared_ptr windows_proc_table);