From 1ff0761982bc6cc418d24e66308bb003c84f0580 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 22 May 2026 15:30:28 -0700 Subject: [PATCH 1/5] Adds flag to enable impeller --- .../shell/platform/linux/fl_dart_project.cc | 15 +++++++++++ .../platform/linux/fl_dart_project_test.cc | 11 ++++++++ .../flutter/shell/platform/linux/fl_engine.cc | 17 ++++++++++++ .../shell/platform/linux/fl_engine_test.cc | 27 +++++++++++++++++++ .../public/flutter_linux/fl_dart_project.h | 18 +++++++++++++ 5 files changed, 88 insertions(+) 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 7d3120a09753b..750593077e56d 100644 --- a/engine/src/flutter/shell/platform/linux/fl_dart_project.cc +++ b/engine/src/flutter/shell/platform/linux/fl_dart_project.cc @@ -15,6 +15,7 @@ struct _FlDartProject { gchar** dart_entrypoint_args; FlUIThreadPolicy ui_thread_policy; + gboolean enable_impeller; }; G_DEFINE_TYPE(FlDartProject, fl_dart_project, G_TYPE_OBJECT) @@ -60,6 +61,7 @@ G_MODULE_EXPORT FlDartProject* fl_dart_project_new() { g_build_filename(executable_dir, "data", "flutter_assets", nullptr); self->icu_data_path = g_build_filename(executable_dir, "data", "icudtl.dat", nullptr); + self->enable_impeller = FALSE; return self; } @@ -130,3 +132,16 @@ FlUIThreadPolicy fl_dart_project_get_ui_thread_policy(FlDartProject* project) { FL_UI_THREAD_POLICY_DEFAULT); return project->ui_thread_policy; } + +G_MODULE_EXPORT +void fl_dart_project_set_enable_impeller(FlDartProject* project, + gboolean enable_impeller) { + g_return_if_fail(FL_IS_DART_PROJECT(project)); + project->enable_impeller = enable_impeller; +} + +G_MODULE_EXPORT +gboolean fl_dart_project_get_enable_impeller(FlDartProject* project) { + g_return_val_if_fail(FL_IS_DART_PROJECT(project), FALSE); + return project->enable_impeller; +} 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 bcc6cca909224..b8ed21a39b984 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 @@ -76,3 +76,14 @@ TEST(FlDartProjectTest, DartEntrypointArgs) { EXPECT_EQ(g_strv_length(retrieved_args), 3U); } + +TEST(FlDartProjectTest, EnableImpeller) { + g_autoptr(FlDartProject) project = fl_dart_project_new(); + EXPECT_FALSE(fl_dart_project_get_enable_impeller(project)); + + fl_dart_project_set_enable_impeller(project, TRUE); + EXPECT_TRUE(fl_dart_project_get_enable_impeller(project)); + + fl_dart_project_set_enable_impeller(project, FALSE); + EXPECT_FALSE(fl_dart_project_get_enable_impeller(project)); +} diff --git a/engine/src/flutter/shell/platform/linux/fl_engine.cc b/engine/src/flutter/shell/platform/linux/fl_engine.cc index 1de67e09a8116..80db298d51a27 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine.cc @@ -812,6 +812,19 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { break; } + gboolean enable_impeller = fl_dart_project_get_enable_impeller(self->project); + gboolean has_enable_impeller = FALSE; + for (const auto& env_switch : flutter::GetSwitchesFromEnvironment()) { + if (env_switch == "--enable-impeller" || + env_switch == "--enable-impeller=true") { + enable_impeller = TRUE; + has_enable_impeller = TRUE; + } else if (env_switch == "--enable-impeller=false") { + enable_impeller = FALSE; + has_enable_impeller = TRUE; + } + } + 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")); @@ -821,6 +834,10 @@ gboolean fl_engine_start(FlEngine* self, GError** error) { // Linux (and other desktop platforms) always uses SDFs. g_ptr_array_add(command_line_args, g_strdup("--impeller-use-sdfs")); + if (enable_impeller && !has_enable_impeller) { + g_ptr_array_add(command_line_args, g_strdup("--enable-impeller")); + } + 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 16e99a61892b2..53f9f05d64838 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine_test.cc @@ -1062,6 +1062,33 @@ TEST(FlEngineTest, SendKeyEventError) { EXPECT_TRUE(called); } +TEST(FlEngineTest, EnableImpeller) { + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_enable_impeller(project, TRUE); + g_autoptr(FlEngine) engine = fl_engine_new(project); + + bool called = false; + fl_engine_get_embedder_api(engine)->Initialize = MOCK_ENGINE_PROC( + Initialize, ([](size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + bool has_impeller_switch = false; + for (int i = 0; i < args->command_line_argc; i++) { + if (strcmp(args->command_line_argv[i], "--enable-impeller") == 0) { + has_impeller_switch = true; + } + } + EXPECT_TRUE(has_impeller_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); +} + TEST(FlEngineTest, ChildObjects) { g_autoptr(FlDartProject) project = fl_dart_project_new(); g_autoptr(FlEngine) engine = fl_engine_new(project); 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 47c100e75ef7a..0b50eac76024b 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 @@ -160,6 +160,24 @@ void fl_dart_project_set_ui_thread_policy(FlDartProject* project, */ FlUIThreadPolicy fl_dart_project_get_ui_thread_policy(FlDartProject* project); +/** + * fl_dart_project_set_enable_impeller: + * @project: an #FlDartProject. + * @enable_impeller: whether to enable the Impeller renderer. + * + * Sets whether the Impeller renderer should be enabled. + */ +void fl_dart_project_set_enable_impeller(FlDartProject* project, + gboolean enable_impeller); + +/** + * fl_dart_project_get_enable_impeller: + * @project: an #FlDartProject. + * + * Returns: %TRUE if the Impeller renderer is enabled. + */ +gboolean fl_dart_project_get_enable_impeller(FlDartProject* project); + G_END_DECLS #endif // FLUTTER_SHELL_PLATFORM_LINUX_PUBLIC_FLUTTER_LINUX_FL_DART_PROJECT_H_ From ebe2e500738d61c76e923c0db8f8cb1ef6817109 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 22 May 2026 22:41:16 +0000 Subject: [PATCH 2/5] made sure the callback was called in the test --- .../src/flutter/shell/platform/linux/fl_engine_test.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 53f9f05d64838..bdc8cb438d439 100644 --- a/engine/src/flutter/shell/platform/linux/fl_engine_test.cc +++ b/engine/src/flutter/shell/platform/linux/fl_engine_test.cc @@ -1069,9 +1069,11 @@ TEST(FlEngineTest, EnableImpeller) { bool called = false; fl_engine_get_embedder_api(engine)->Initialize = MOCK_ENGINE_PROC( - Initialize, ([](size_t version, const FlutterRendererConfig* config, - const FlutterProjectArgs* args, void* user_data, - FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + Initialize, + ([&called](size_t version, const FlutterRendererConfig* config, + const FlutterProjectArgs* args, void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) { + called = true; bool has_impeller_switch = false; for (int i = 0; i < args->command_line_argc; i++) { if (strcmp(args->command_line_argv[i], "--enable-impeller") == 0) { @@ -1087,6 +1089,7 @@ TEST(FlEngineTest, EnableImpeller) { g_autoptr(GError) error = nullptr; EXPECT_TRUE(fl_engine_start(engine, &error)); EXPECT_EQ(error, nullptr); + EXPECT_TRUE(called); } TEST(FlEngineTest, ChildObjects) { From 6fa4a99b001788585f39149ada0ea03c17924f7c Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 22 May 2026 23:26:12 +0000 Subject: [PATCH 3/5] updated integration test --- .../bin/tasks/hello_world_impeller_linux.dart | 144 +++++++++++++----- 1 file changed, 107 insertions(+), 37 deletions(-) diff --git a/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart b/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart index 6fd86429d2847..5a35ecddb6b2c 100644 --- a/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart +++ b/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart @@ -16,6 +16,11 @@ Future run() async { deviceOperatingSystem = DeviceOperatingSystem.linux; final Directory appDir = dir(path.join(flutterDirectory.path, 'examples/hello_world')); + final String myApplicationPath = path.join(appDir.path, 'linux', 'runner', 'my_application.cc'); + final myApplicationFile = File(myApplicationPath); + + const vulkanBackendMessage = 'Using the Impeller rendering backend (Vulkan).'; + const openGLBackendMessage = 'Using the Impeller rendering backend (OpenGL).'; var res = TaskResult.success(null); @@ -23,49 +28,114 @@ Future run() async { await inDirectory(appDir, () async { await flutter('packages', options: ['get']); - final Process process = await startFlutter( - 'run', - options: ['--enable-impeller', '-d', 'linux'], - ); - - final completer = Completer(); - var sawImpellerBackendMessage = false; - const vulkanBackendMessage = 'Using the Impeller rendering backend (VulkanSDF).'; - const openGLBackendMessage = 'Using the Impeller rendering backend (OpenGLESSDF).'; - - final StreamSubscription subscription = process.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String line) { - print('[STDOUT]: $line'); - if (line.contains(vulkanBackendMessage) || line.contains(openGLBackendMessage)) { - sawImpellerBackendMessage = true; - if (!completer.isCompleted) { - completer.complete(); + // Step 1: Test using command line flag. + { + final Process process = await startFlutter( + 'run', + options: ['--enable-impeller', '-d', 'linux'], + ); + + final completer = Completer(); + var sawImpellerBackendMessage = false; + + final StreamSubscription subscription = process.stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen((String line) { + print('[STDOUT 1]: $line'); + if (line.contains(vulkanBackendMessage) || line.contains(openGLBackendMessage)) { + sawImpellerBackendMessage = true; + if (!completer.isCompleted) { + completer.complete(); + } } - } - }); - - await Future.any(>[ - completer.future, - Future.delayed(const Duration(minutes: 2)), - ]); - - process.stdin.writeln('q'); - final int exitCode = await process.exitCode; - await subscription.cancel(); - - if (exitCode != 0) { - res = TaskResult.failure('Flutter process exited with non-zero exit code: $exitCode'); - } else if (!sawImpellerBackendMessage) { - res = TaskResult.failure( - 'Did not see "$vulkanBackendMessage" or ' - '"$openGLBackendMessage" in output', + }); + + await Future.any(>[ + completer.future, + Future.delayed(const Duration(minutes: 2)), + ]); + + process.stdin.writeln('q'); + final int exitCode = await process.exitCode; + await subscription.cancel(); + + if (exitCode != 0) { + res = TaskResult.failure('Flutter process 1 exited with non-zero exit code: $exitCode'); + return; + } else if (!sawImpellerBackendMessage) { + res = TaskResult.failure( + 'Did not see "$vulkanBackendMessage" or ' + '"$openGLBackendMessage" in output (Step 1)', + ); + return; + } + } + + // Step 2: Test using project flag. + { + if (!myApplicationFile.existsSync()) { + res = TaskResult.failure('my_application.cc not found at $myApplicationPath'); + return; + } + + final String originalContent = myApplicationFile.readAsStringSync(); + final String modifiedContent = originalContent.replaceFirst( + 'g_autoptr(FlDartProject) project = fl_dart_project_new();', + 'g_autoptr(FlDartProject) project = fl_dart_project_new();\n fl_dart_project_set_enable_impeller(project, TRUE);', ); + if (modifiedContent == originalContent) { + res = TaskResult.failure('Failed to modify my_application.cc'); + return; + } + myApplicationFile.writeAsStringSync(modifiedContent); + + // Run 'flutter run' without command-line flag --enable-impeller. + final Process process = await startFlutter('run', options: ['-d', 'linux']); + + final completer = Completer(); + var sawImpellerBackendMessage = false; + + final StreamSubscription subscription = process.stdout + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen((String line) { + print('[STDOUT 2]: $line'); + if (line.contains(vulkanBackendMessage) || line.contains(openGLBackendMessage)) { + sawImpellerBackendMessage = true; + if (!completer.isCompleted) { + completer.complete(); + } + } + }); + + await Future.any(>[ + completer.future, + Future.delayed(const Duration(minutes: 2)), + ]); + + process.stdin.writeln('q'); + final int exitCode = await process.exitCode; + await subscription.cancel(); + + if (exitCode != 0) { + res = TaskResult.failure('Flutter process 2 exited with non-zero exit code: $exitCode'); + return; + } else if (!sawImpellerBackendMessage) { + res = TaskResult.failure( + 'Did not see "$vulkanBackendMessage" or ' + '"$openGLBackendMessage" in output (Step 2)', + ); + return; + } } }); } catch (e) { res = TaskResult.failure('Test failed with exception: $e'); + } finally { + if (myApplicationFile.existsSync()) { + await exec('git', ['checkout', myApplicationPath]); + } } return res; From e05ec92ac2b6595a64f76d2a98890781a3d9d4f9 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Fri, 22 May 2026 23:45:09 +0000 Subject: [PATCH 4/5] updated tool --- .../flutter_tools/lib/src/desktop_device.dart | 3 ++- .../general.shard/desktop_device_test.dart | 18 ++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/flutter_tools/lib/src/desktop_device.dart b/packages/flutter_tools/lib/src/desktop_device.dart index e485068c73288..71aaf04d06055 100644 --- a/packages/flutter_tools/lib/src/desktop_device.dart +++ b/packages/flutter_tools/lib/src/desktop_device.dart @@ -298,8 +298,9 @@ abstract class DesktopDevice extends Device { case ImpellerStatus.enabled: addFlag('enable-impeller=true'); case ImpellerStatus.disabled: - case ImpellerStatus.platformDefault: addFlag('enable-impeller=false'); + case ImpellerStatus.platformDefault: + break; } if (debuggingOptions.enableFlutterGpu) { addFlag('enable-flutter-gpu=true'); diff --git a/packages/flutter_tools/test/general.shard/desktop_device_test.dart b/packages/flutter_tools/test/general.shard/desktop_device_test.dart index 54606c79cd197..403964780f955 100644 --- a/packages/flutter_tools/test/general.shard/desktop_device_test.dart +++ b/packages/flutter_tools/test/general.shard/desktop_device_test.dart @@ -169,14 +169,13 @@ void main() { 'FLUTTER_ENGINE_SWITCH_11': 'endless-trace-buffer=true', 'FLUTTER_ENGINE_SWITCH_12': 'profile-microtasks=true', 'FLUTTER_ENGINE_SWITCH_13': 'purge-persistent-cache=true', - 'FLUTTER_ENGINE_SWITCH_14': 'enable-impeller=false', - 'FLUTTER_ENGINE_SWITCH_15': 'enable-checked-mode=true', - 'FLUTTER_ENGINE_SWITCH_16': 'verify-entry-points=true', - 'FLUTTER_ENGINE_SWITCH_17': 'start-paused=true', - 'FLUTTER_ENGINE_SWITCH_18': 'disable-service-auth-codes=true', - 'FLUTTER_ENGINE_SWITCH_19': 'use-test-fonts=true', - 'FLUTTER_ENGINE_SWITCH_20': 'verbose-logging=true', - 'FLUTTER_ENGINE_SWITCHES': '20', + 'FLUTTER_ENGINE_SWITCH_14': 'enable-checked-mode=true', + 'FLUTTER_ENGINE_SWITCH_15': 'verify-entry-points=true', + 'FLUTTER_ENGINE_SWITCH_16': 'start-paused=true', + 'FLUTTER_ENGINE_SWITCH_17': 'disable-service-auth-codes=true', + 'FLUTTER_ENGINE_SWITCH_18': 'use-test-fonts=true', + 'FLUTTER_ENGINE_SWITCH_19': 'verbose-logging=true', + 'FLUTTER_ENGINE_SWITCHES': '19', }, ), ]); @@ -223,8 +222,7 @@ void main() { 'FLUTTER_ENGINE_SWITCH_1': 'enable-dart-profiling=true', 'FLUTTER_ENGINE_SWITCH_2': 'trace-startup=true', 'FLUTTER_ENGINE_SWITCH_3': 'trace-allowlist=foo,bar', - 'FLUTTER_ENGINE_SWITCH_4': 'enable-impeller=false', - 'FLUTTER_ENGINE_SWITCHES': '4', + 'FLUTTER_ENGINE_SWITCHES': '3', }, ), ]); From 5506fcc01bf223a697df55482fc8a01ed1b89ed3 Mon Sep 17 00:00:00 2001 From: Aaron Clarke Date: Tue, 26 May 2026 15:39:24 +0000 Subject: [PATCH 5/5] rebase fix --- dev/devicelab/bin/tasks/hello_world_impeller_linux.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart b/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart index 5a35ecddb6b2c..2131219a63550 100644 --- a/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart +++ b/dev/devicelab/bin/tasks/hello_world_impeller_linux.dart @@ -19,8 +19,8 @@ Future run() async { final String myApplicationPath = path.join(appDir.path, 'linux', 'runner', 'my_application.cc'); final myApplicationFile = File(myApplicationPath); - const vulkanBackendMessage = 'Using the Impeller rendering backend (Vulkan).'; - const openGLBackendMessage = 'Using the Impeller rendering backend (OpenGL).'; + const vulkanBackendMessage = 'Using the Impeller rendering backend (VulkanSDF).'; + const openGLBackendMessage = 'Using the Impeller rendering backend (OpenGLESSDF).'; var res = TaskResult.success(null);