Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[engine] support combined UI/Platform thread for iOS/Android. #53656

Merged
merged 7 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ struct Settings {
bool enable_impeller = false;
#endif

// If true, the UI thread is the platform thread on supported
// platforms.
bool merged_platform_ui_thread = false;

// Log a warning during shell initialization if Impeller is not enabled.
bool warn_on_impeller_opt_out = false;

Expand Down
109 changes: 60 additions & 49 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -944,11 +944,12 @@ void Shell::OnPlatformViewDestroyed() {

// Notify the Dart VM that the PlatformView has been destroyed and some
// cleanup activity can be done (e.g: garbage collect the Dart heap).
task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() {
if (engine) {
engine->NotifyDestroyed();
}
});
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = engine_->GetWeakPtr()]() {
if (engine) {
engine->NotifyDestroyed();
}
});

// Note:
// This is a synchronous operation because certain platforms depend on
Expand Down Expand Up @@ -1006,11 +1007,12 @@ void Shell::OnPlatformViewScheduleFrame() {
FML_DCHECK(is_set_up_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());

task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() {
if (engine) {
engine->ScheduleFrame();
}
});
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = engine_->GetWeakPtr()]() {
if (engine) {
engine->ScheduleFrame();
}
});
}

// |PlatformView::Delegate|
Expand Down Expand Up @@ -1038,7 +1040,8 @@ void Shell::OnPlatformViewSetViewportMetrics(int64_t view_id,
}
});

task_runners_.GetUITaskRunner()->PostTask(
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
[engine = engine_->GetWeakPtr(), view_id, metrics]() {
if (engine) {
engine->SetViewportMetrics(view_id, metrics);
Expand Down Expand Up @@ -1078,8 +1081,10 @@ void Shell::OnPlatformViewDispatchPlatformMessage(

// The static leak checker gets confused by the use of fml::MakeCopyable.
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
task_runners_.GetUITaskRunner()->PostTask(fml::MakeCopyable(
[engine = engine_->GetWeakPtr(), message = std::move(message)]() mutable {
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable([engine = engine_->GetWeakPtr(),
message = std::move(message)]() mutable {
if (engine) {
engine->DispatchPlatformMessage(std::move(message));
}
Expand All @@ -1095,7 +1100,8 @@ void Shell::OnPlatformViewDispatchPointerDataPacket(
TRACE_FLOW_BEGIN("flutter", "PointerEvent", next_pointer_flow_id_);
FML_DCHECK(is_set_up_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
task_runners_.GetUITaskRunner()->PostTask(
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable([engine = weak_engine_, packet = std::move(packet),
flow_id = next_pointer_flow_id_]() mutable {
if (engine) {
Expand All @@ -1112,7 +1118,8 @@ void Shell::OnPlatformViewDispatchSemanticsAction(int32_t node_id,
FML_DCHECK(is_set_up_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());

task_runners_.GetUITaskRunner()->PostTask(
fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable([engine = engine_->GetWeakPtr(), node_id, action,
args = std::move(args)]() mutable {
if (engine) {
Expand All @@ -1126,25 +1133,25 @@ void Shell::OnPlatformViewSetSemanticsEnabled(bool enabled) {
FML_DCHECK(is_set_up_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());

task_runners_.GetUITaskRunner()->PostTask(
[engine = engine_->GetWeakPtr(), enabled] {
if (engine) {
engine->SetSemanticsEnabled(enabled);
}
});
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = engine_->GetWeakPtr(), enabled] {
if (engine) {
engine->SetSemanticsEnabled(enabled);
}
});
}

// |PlatformView::Delegate|
void Shell::OnPlatformViewSetAccessibilityFeatures(int32_t flags) {
FML_DCHECK(is_set_up_);
FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());

task_runners_.GetUITaskRunner()->PostTask(
[engine = engine_->GetWeakPtr(), flags] {
if (engine) {
engine->SetAccessibilityFeatures(flags);
}
});
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = engine_->GetWeakPtr(), flags] {
if (engine) {
engine->SetAccessibilityFeatures(flags);
}
});
}

// |PlatformView::Delegate|
Expand Down Expand Up @@ -1205,11 +1212,12 @@ void Shell::OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) {
});

// Schedule a new frame without having to rebuild the layer tree.
task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr()]() {
if (engine) {
engine->ScheduleFrame(false);
}
});
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = engine_->GetWeakPtr()]() {
if (engine) {
engine->ScheduleFrame(false);
}
});
}

// |PlatformView::Delegate|
Expand Down Expand Up @@ -1317,7 +1325,8 @@ void Shell::OnEngineUpdateSemantics(SemanticsNodeUpdates update,
FML_DCHECK(is_set_up_);
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());

task_runners_.GetPlatformTaskRunner()->PostTask(
task_runners_.GetPlatformTaskRunner()->RunNowOrPostTask(
task_runners_.GetPlatformTaskRunner(),
[view = platform_view_->GetWeakPtr(), update = std::move(update),
actions = std::move(actions)] {
if (view) {
Expand Down Expand Up @@ -2142,15 +2151,16 @@ void Shell::OnPlatformViewAddView(int64_t view_id,
<< "Unexpected request to add the implicit view #"
<< kFlutterImplicitViewId << ". This view should never be added.";

task_runners_.GetUITaskRunner()->PostTask([engine = engine_->GetWeakPtr(), //
viewport_metrics, //
view_id, //
callback = std::move(callback) //
task_runners_.GetUITaskRunner()->RunNowOrPostTask(
task_runners_.GetUITaskRunner(), [engine = engine_->GetWeakPtr(), //
viewport_metrics, //
view_id, //
callback = std::move(callback) //
] {
if (engine) {
engine->AddView(view_id, viewport_metrics, callback);
}
});
if (engine) {
engine->AddView(view_id, viewport_metrics, callback);
}
});
}

void Shell::OnPlatformViewRemoveView(int64_t view_id,
Expand All @@ -2163,7 +2173,8 @@ void Shell::OnPlatformViewRemoveView(int64_t view_id,
<< kFlutterImplicitViewId << ". This view should never be removed.";

expected_frame_sizes_.erase(view_id);
task_runners_.GetUITaskRunner()->PostTask(
task_runners_.GetUITaskRunner()->RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
[&task_runners = task_runners_, //
engine = engine_->GetWeakPtr(), //
rasterizer = rasterizer_->GetWeakPtr(), //
Expand Down Expand Up @@ -2303,13 +2314,13 @@ void Shell::OnDisplayUpdates(std::vector<std::unique_ptr<Display>> displays) {
for (const auto& display : displays) {
display_data.push_back(display->GetDisplayData());
}
task_runners_.GetUITaskRunner()->PostTask(
[engine = engine_->GetWeakPtr(),
display_data = std::move(display_data)]() {
if (engine) {
engine->SetDisplays(display_data);
}
});
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = engine_->GetWeakPtr(),
display_data = std::move(display_data)]() {
if (engine) {
engine->SetDisplays(display_data);
}
});

display_manager_->HandleDisplayUpdates(std::move(displays));
}
Expand Down
3 changes: 3 additions & 0 deletions shell/common/switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.verbose_logging =
command_line.HasOption(FlagForSwitch(Switch::VerboseLogging));

settings.merged_platform_ui_thread = command_line.HasOption(
FlagForSwitch(Switch::EnableMergedPlatformUIThread));

command_line.GetOptionValue(FlagForSwitch(Switch::FlutterAssetsDir),
&settings.assets_path);

Expand Down
3 changes: 3 additions & 0 deletions shell/common/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ DEF_SWITCH(EnableEmbedderAPI,
DEF_SWITCH(EnablePlatformIsolates,
"enable-platform-isolates",
"Enable support for isolates that run on the platform thread.")
DEF_SWITCH(EnableMergedPlatformUIThread,
"enable-merged-platform-ui-thread",
"Merge the ui thread and platform thread.")
DEF_SWITCHES_END

void PrintUsage(const std::string& executable_name);
Expand Down
18 changes: 13 additions & 5 deletions shell/platform/android/android_shell_holder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,12 @@ AndroidShellHolder::AndroidShellHolder(

flutter::ThreadHost::ThreadHostConfig host_config(
thread_label, mask, AndroidPlatformThreadConfigSetter);
host_config.ui_config = fml::Thread::ThreadConfig(
flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
flutter::ThreadHost::Type::kUi, thread_label),
fml::Thread::ThreadPriority::kDisplay);
if (!settings.merged_platform_ui_thread) {
host_config.ui_config = fml::Thread::ThreadConfig(
flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
flutter::ThreadHost::Type::kUi, thread_label),
fml::Thread::ThreadPriority::kDisplay);
}
host_config.raster_config = fml::Thread::ThreadConfig(
flutter::ThreadHost::ThreadHostConfig::MakeThreadName(
flutter::ThreadHost::Type::kRaster, thread_label),
Expand Down Expand Up @@ -137,7 +139,13 @@ AndroidShellHolder::AndroidShellHolder(
fml::RefPtr<fml::TaskRunner> platform_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
raster_runner = thread_host_->raster_thread->GetTaskRunner();
ui_runner = thread_host_->ui_thread->GetTaskRunner();
if (settings.merged_platform_ui_thread) {
FML_LOG(IMPORTANT)
<< "Warning: Using highly experimental merged thread mode.";
ui_runner = platform_runner;
} else {
ui_runner = thread_host_->ui_thread->GetTaskRunner();
}
io_runner = thread_host_->io_thread->GetTaskRunner();

flutter::TaskRunners task_runners(thread_label, // label
Expand Down
3 changes: 3 additions & 0 deletions shell/platform/android/android_shell_holder.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ class AndroidShellHolder {

void UpdateDisplayMetrics();

// Visible for testing.
const std::unique_ptr<Shell>& GetShellForTesting() const { return shell_; }

private:
const flutter::Settings settings_;
const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;
Expand Down
29 changes: 29 additions & 0 deletions shell/platform/android/android_shell_holder_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -160,5 +160,34 @@ TEST(AndroidShellHolder, HandlePlatformMessage) {
holder->GetPlatformMessageHandler()
->InvokePlatformMessageEmptyResponseCallback(response_id);
}

TEST(AndroidShellHolder, CreateWithMergedPlatformAndUIThread) {
Settings settings;
settings.merged_platform_ui_thread = true;
auto jni = std::make_shared<MockPlatformViewAndroidJNI>();
auto holder = std::make_unique<AndroidShellHolder>(settings, jni);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(
nullptr, /*is_fake_window=*/true);
holder->GetPlatformView()->NotifyCreated(window);

EXPECT_EQ(
holder->GetShellForTesting()->GetTaskRunners().GetUITaskRunner(),
holder->GetShellForTesting()->GetTaskRunners().GetPlatformTaskRunner());
}

TEST(AndroidShellHolder, CreateWithUnMergedPlatformAndUIThread) {
Settings settings;
settings.merged_platform_ui_thread = false;
auto jni = std::make_shared<MockPlatformViewAndroidJNI>();
auto holder = std::make_unique<AndroidShellHolder>(settings, jni);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(
nullptr, /*is_fake_window=*/true);
holder->GetPlatformView()->NotifyCreated(window);

EXPECT_NE(
holder->GetShellForTesting()->GetTaskRunners().GetUITaskRunner(),
holder->GetShellForTesting()->GetTaskRunners().GetPlatformTaskRunner());
}

} // namespace testing
} // namespace flutter
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,6 @@ public static FlutterShellArgs fromIntent(@NonNull Intent intent) {
// Before adding more entries to this list, consider that arbitrary
// Android applications can generate intents with extra data and that
// there are many security-sensitive args in the binary.
// TODO(mattcarroll): I left this warning as-is, but we should clarify what exactly this warning
// is warning against.
ArrayList<String> args = new ArrayList<>();

if (intent.getBooleanExtra(ARG_KEY_TRACE_STARTUP, false)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public class FlutterLoader {
"io.flutter.embedding.android.EnableOpenGLGPUTracing";
private static final String IMPELLER_VULKAN_GPU_TRACING_DATA_KEY =
"io.flutter.embedding.android.EnableVulkanGPUTracing";
private static final String ENABLED_MERGED_PLATFORM_UI_THREAD_KEY =
"io.flutter.embedding.android.EnableMergedPlatformUIThread";

/**
* Set whether leave or clean up the VM after the last shell shuts down. It can be set from app's
Expand Down Expand Up @@ -352,6 +354,9 @@ public void ensureInitializationComplete(
if (metaData.getBoolean(IMPELLER_VULKAN_GPU_TRACING_DATA_KEY, false)) {
shellArgs.add("--enable-vulkan-gpu-tracing");
}
if (metaData.getBoolean(ENABLED_MERGED_PLATFORM_UI_THREAD_KEY, false)) {
shellArgs.add("--enable-merged-platform-ui-thread");
}
String backend = metaData.getString(IMPELLER_BACKEND_META_DATA_KEY);
if (backend != null) {
shellArgs.add("--impeller-backend=" + backend);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,12 @@ static BOOL DoesHardwareSupportWideGamut() {
settings.enable_dart_profiling = enableDartProfiling.boolValue;
}

NSNumber* enableMergedPlatformUIThread =
[mainBundle objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"];
if (enableMergedPlatformUIThread != nil) {
settings.merged_platform_ui_thread = enableMergedPlatformUIThread.boolValue;
}

// Leak Dart VM settings, set whether leave or clean up the VM after the last shell shuts down.
NSNumber* leakDartVM = [mainBundle objectForInfoDictionaryKey:@"FLTLeakDartVM"];
// It will change the default leak_vm value in settings only if the key exists.
Expand Down
Loading