Reduce app startup latency by initializing the engine on a separate thread (#166918)

If settings.merged_platform_ui_thread is set to kMergeAfterLaunch, then
the engine will be started on the UI thread. After engine setup
completes and the Dart isolate is loaded, the UI task runner will be
merged into the platform thread and all future Dart execution will run
on the platform thread.

This makes it possible for other work to run on the platform thread
while the engine starts.

See https://github.com/flutter/flutter/issues/163064
This commit is contained in:
Jason Simmons 2025-04-17 21:40:08 +00:00 committed by GitHub
parent 9f4fe843a7
commit c53fdbdf24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 292 additions and 91 deletions

View File

@ -16,6 +16,7 @@
#include "flutter/fml/build_config.h"
#include "flutter/fml/closure.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/task_queue_id.h"
#include "flutter/fml/time/time_point.h"
#include "flutter/fml/unique_fd.h"
@ -70,8 +71,10 @@ class FrameTiming {
};
using TaskObserverAdd =
std::function<void(intptr_t /* key */, fml::closure /* callback */)>;
using TaskObserverRemove = std::function<void(intptr_t /* key */)>;
std::function<fml::TaskQueueId(intptr_t /* key */,
fml::closure /* callback */)>;
using TaskObserverRemove =
std::function<void(fml::TaskQueueId /* queue */, intptr_t /* key */)>;
using UnhandledExceptionCallback =
std::function<bool(const std::string& /* error */,
const std::string& /* stack trace */)>;
@ -359,9 +362,20 @@ struct Settings {
/// This is used by the runOnPlatformThread API.
bool enable_platform_isolates = false;
// If true, the UI thread is the platform thread on supported
// platforms.
bool merged_platform_ui_thread = true;
enum class MergedPlatformUIThread {
// Use separate threads for the UI and platform task runners.
kDisabled,
// Use the platform thread for both the UI and platform task runners.
kEnabled,
// Start the engine on a separate UI thread and then move the UI task
// runner to the platform thread after the engine is initialized.
// This can improve app launch latency by allowing other work to run on
// the platform thread during engine startup.
kMergeAfterLaunch
};
MergedPlatformUIThread merged_platform_ui_thread =
MergedPlatformUIThread::kEnabled;
};
} // namespace flutter

View File

@ -16,14 +16,16 @@ void ExecuteAfterTaskObservers() {
}
}
void CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
fit::closure observer) {
fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
fit::closure observer) {
if (observer) {
tTaskObservers[key] = std::move(observer);
}
return fml::TaskQueueId::Invalid();
}
void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key) {
void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id,
intptr_t key) {
tTaskObservers.erase(key);
}

View File

@ -7,6 +7,8 @@
#include <lib/fit/function.h>
#include "flutter/fml/task_queue_id.h"
namespace fml {
// Executes all closures that were registered via
@ -30,10 +32,11 @@ namespace fml {
// somehow.
void ExecuteAfterTaskObservers();
void CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
fit::closure observer);
fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
fit::closure observer);
void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key);
void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id,
intptr_t key);
} // namespace fml

View File

@ -25,6 +25,8 @@ class TaskQueueId {
/// Intializes a task queue with the given value as it's ID.
explicit TaskQueueId(size_t value) : value_(value) {}
static TaskQueueId Invalid() { return TaskQueueId(kInvalid); }
operator size_t() const { // NOLINT(google-explicit-constructor)
return value_;
}

View File

@ -55,7 +55,7 @@ ImageDecoder::ImageDecoder(
ImageDecoder::~ImageDecoder() = default;
fml::WeakPtr<ImageDecoder> ImageDecoder::GetWeakPtr() const {
fml::TaskRunnerAffineWeakPtr<ImageDecoder> ImageDecoder::GetWeakPtr() const {
return weak_factory_.GetWeakPtr();
}

View File

@ -44,7 +44,7 @@ class ImageDecoder {
uint32_t target_height,
const ImageResult& result) = 0;
fml::WeakPtr<ImageDecoder> GetWeakPtr() const;
fml::TaskRunnerAffineWeakPtr<ImageDecoder> GetWeakPtr() const;
protected:
TaskRunners runners_;
@ -57,7 +57,7 @@ class ImageDecoder {
fml::WeakPtr<IOManager> io_manager);
private:
fml::WeakPtrFactory<ImageDecoder> weak_factory_;
fml::TaskRunnerAffineWeakPtrFactory<ImageDecoder> weak_factory_;
FML_DISALLOW_COPY_AND_ASSIGN(ImageDecoder);
};

View File

@ -79,8 +79,8 @@ ImageGeneratorRegistry::CreateCompatibleGenerator(const sk_sp<SkData>& buffer) {
return nullptr;
}
fml::WeakPtr<ImageGeneratorRegistry> ImageGeneratorRegistry::GetWeakPtr()
const {
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
ImageGeneratorRegistry::GetWeakPtr() const {
return weak_factory_.GetWeakPtr();
}

View File

@ -58,7 +58,7 @@ class ImageGeneratorRegistry {
std::shared_ptr<ImageGenerator> CreateCompatibleGenerator(
const sk_sp<SkData>& buffer);
fml::WeakPtr<ImageGeneratorRegistry> GetWeakPtr() const;
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry> GetWeakPtr() const;
private:
struct PrioritizedFactory {
@ -85,7 +85,7 @@ class ImageGeneratorRegistry {
using FactorySet = std::set<PrioritizedFactory, Compare>;
FactorySet image_generator_factories_;
size_t nonce_;
fml::WeakPtrFactory<ImageGeneratorRegistry> weak_factory_;
fml::TaskRunnerAffineWeakPtrFactory<ImageGeneratorRegistry> weak_factory_;
};
} // namespace flutter

View File

@ -24,8 +24,9 @@ UIDartState::Context::Context(
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate,
fml::WeakPtr<IOManager> io_manager,
fml::RefPtr<SkiaUnrefQueue> unref_queue,
fml::WeakPtr<ImageDecoder> image_decoder,
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
image_generator_registry,
std::string advisory_script_uri,
std::string advisory_script_entrypoint,
bool deterministic_rendering_enabled,
@ -58,6 +59,7 @@ UIDartState::UIDartState(
const UIDartState::Context& context)
: add_callback_(std::move(add_callback)),
remove_callback_(std::move(remove_callback)),
callback_queue_id_(fml::TaskQueueId::kInvalid),
logger_prefix_(std::move(logger_prefix)),
is_root_isolate_(is_root_isolate),
unhandled_exception_callback_(std::move(unhandled_exception_callback)),
@ -178,10 +180,12 @@ void UIDartState::AddOrRemoveTaskObserver(bool add) {
}
FML_DCHECK(add_callback_ && remove_callback_);
if (add) {
add_callback_(reinterpret_cast<intptr_t>(this),
[this]() { this->FlushMicrotasksNow(); });
callback_queue_id_ =
add_callback_(reinterpret_cast<intptr_t>(this),
[this]() { this->FlushMicrotasksNow(); });
} else {
remove_callback_(reinterpret_cast<intptr_t>(this));
remove_callback_(callback_queue_id_, reinterpret_cast<intptr_t>(this));
callback_queue_id_ = fml::TaskQueueId::Invalid();
}
}
@ -190,12 +194,13 @@ UIDartState::GetSnapshotDelegate() const {
return context_.snapshot_delegate;
}
fml::WeakPtr<ImageDecoder> UIDartState::GetImageDecoder() const {
fml::TaskRunnerAffineWeakPtr<ImageDecoder> UIDartState::GetImageDecoder()
const {
return context_.image_decoder;
}
fml::WeakPtr<ImageGeneratorRegistry> UIDartState::GetImageGeneratorRegistry()
const {
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
UIDartState::GetImageGeneratorRegistry() const {
return context_.image_generator_registry;
}

View File

@ -48,8 +48,9 @@ class UIDartState : public tonic::DartState {
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate,
fml::WeakPtr<IOManager> io_manager,
fml::RefPtr<SkiaUnrefQueue> unref_queue,
fml::WeakPtr<ImageDecoder> image_decoder,
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
image_generator_registry,
std::string advisory_script_uri,
std::string advisory_script_entrypoint,
bool deterministic_rendering_enabled,
@ -76,12 +77,13 @@ class UIDartState : public tonic::DartState {
fml::RefPtr<SkiaUnrefQueue> unref_queue;
/// The image decoder.
fml::WeakPtr<ImageDecoder> image_decoder;
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder;
/// Cascading registry of image generator builders. Given compressed image
/// bytes as input, this is used to find and create image generators, which
/// can then be used for image decoding.
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry;
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
image_generator_registry;
/// The advisory script URI (only used for debugging). This does not affect
/// the code being run in the isolate in any way.
@ -144,9 +146,10 @@ class UIDartState : public tonic::DartState {
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> GetSnapshotDelegate() const;
fml::WeakPtr<ImageDecoder> GetImageDecoder() const;
fml::TaskRunnerAffineWeakPtr<ImageDecoder> GetImageDecoder() const;
fml::WeakPtr<ImageGeneratorRegistry> GetImageGeneratorRegistry() const;
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
GetImageGeneratorRegistry() const;
std::shared_ptr<IsolateNameServer> GetIsolateNameServer() const;
@ -205,6 +208,7 @@ class UIDartState : public tonic::DartState {
const TaskObserverAdd add_callback_;
const TaskObserverRemove remove_callback_;
fml::TaskQueueId callback_queue_id_;
const std::string logger_prefix_;
Dart_Port main_port_ = ILLEGAL_PORT;
const bool is_root_isolate_;

View File

@ -155,7 +155,10 @@ std::weak_ptr<DartIsolate> DartIsolate::CreateRunningRootIsolate(
{
tonic::DartState::Scope scope(isolate.get());
Dart_SetCurrentThreadOwnsIsolate();
if (settings.merged_platform_ui_thread !=
Settings::MergedPlatformUIThread::kMergeAfterLaunch) {
Dart_SetCurrentThreadOwnsIsolate();
}
if (settings.root_isolate_create_callback) {
// Isolate callbacks always occur in isolate scope and before user code
@ -349,6 +352,7 @@ Dart_Isolate DartIsolate::CreatePlatformIsolate(Dart_Handle entry_point,
}
old_task_observer_add(key, callback);
});
return platform_task_runner->GetTaskQueueId();
};
UIDartState::Context context(task_runners);
@ -1373,6 +1377,11 @@ std::weak_ptr<DartIsolate> DartIsolate::GetWeakIsolatePtr() {
return std::static_pointer_cast<DartIsolate>(shared_from_this());
}
void DartIsolate::SetOwnerToCurrentThread() {
tonic::DartIsolateScope isolate_scope(isolate());
Dart_SetCurrentThreadOwnsIsolate();
}
void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) {
shutdown_callbacks_.emplace_back(std::make_unique<AutoFireClosure>(closure));
}

View File

@ -411,6 +411,10 @@ class DartIsolate : public UIDartState {
static Dart_Handle LoadLibraryFromKernel(
const std::shared_ptr<const fml::Mapping>& mapping);
// Calls a Dart API that sets the isolate's owner thread to the current
// thread.
void SetOwnerToCurrentThread();
private:
friend class IsolateConfiguration;
class AutoFireClosure {

View File

@ -56,8 +56,9 @@ std::unique_ptr<RuntimeController> RuntimeController::Spawn(
const fml::closure& p_isolate_shutdown_callback,
const std::shared_ptr<const fml::Mapping>& p_persistent_isolate_data,
fml::WeakPtr<IOManager> io_manager,
fml::WeakPtr<ImageDecoder> image_decoder,
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
image_generator_registry,
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate) const {
UIDartState::Context spawned_context{context_.task_runners,
std::move(snapshot_delegate),
@ -676,6 +677,13 @@ void RuntimeController::ShutdownPlatformIsolates() {
platform_isolate_manager_->ShutdownPlatformIsolates();
}
void RuntimeController::SetRootIsolateOwnerToCurrentThread() {
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
if (root_isolate) {
root_isolate->SetOwnerToCurrentThread();
}
}
RuntimeController::Locale::Locale(std::string language_code_,
std::string country_code_,
std::string script_code_,

View File

@ -121,8 +121,9 @@ class RuntimeController : public PlatformConfigurationClient,
const fml::closure& isolate_shutdown_callback,
const std::shared_ptr<const fml::Mapping>& persistent_isolate_data,
fml::WeakPtr<IOManager> io_manager,
fml::WeakPtr<ImageDecoder> image_decoder,
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
image_generator_registry,
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate) const;
// |PlatformConfigurationClient|
@ -678,6 +679,8 @@ class RuntimeController : public PlatformConfigurationClient,
return platform_isolate_manager_;
}
void SetRootIsolateOwnerToCurrentThread();
//--------------------------------------------------------------------------
/// @brief Shuts down all registered platform isolates. Must be called
/// from the platform thread.

View File

@ -148,7 +148,7 @@ class Animator final {
std::deque<uint64_t> trace_flow_ids_;
bool has_rendered_ = false;
fml::WeakPtrFactory<Animator> weak_factory_;
fml::TaskRunnerAffineWeakPtrFactory<Animator> weak_factory_;
friend class testing::ShellTest;

View File

@ -148,7 +148,7 @@ std::unique_ptr<Engine> Engine::Spawn(
Engine::~Engine() = default;
fml::WeakPtr<Engine> Engine::GetWeakPtr() const {
fml::TaskRunnerAffineWeakPtr<Engine> Engine::GetWeakPtr() const {
return weak_factory_.GetWeakPtr();
}
@ -161,11 +161,12 @@ std::shared_ptr<AssetManager> Engine::GetAssetManager() {
return asset_manager_;
}
fml::WeakPtr<ImageDecoder> Engine::GetImageDecoderWeakPtr() {
fml::TaskRunnerAffineWeakPtr<ImageDecoder> Engine::GetImageDecoderWeakPtr() {
return image_decoder_->GetWeakPtr();
}
fml::WeakPtr<ImageGeneratorRegistry> Engine::GetImageGeneratorRegistry() {
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
Engine::GetImageGeneratorRegistry() {
return image_generator_registry_.GetWeakPtr();
}
@ -239,6 +240,19 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) {
}
};
if (settings_.merged_platform_ui_thread ==
Settings::MergedPlatformUIThread::kMergeAfterLaunch) {
// Queue a task to the UI task runner that sets the owner of the root
// isolate. This task runs after the thread merge and will therefore be
// executed on the platform thread. The task will run before any tasks
// queued by LaunchRootIsolate that execute the app's Dart code.
task_runners_.GetUITaskRunner()->PostTask([engine = GetWeakPtr()]() {
if (engine) {
engine->runtime_controller_->SetRootIsolateOwnerToCurrentThread();
}
});
}
if (!runtime_controller_->LaunchRootIsolate(
settings_, //
root_isolate_create_callback, //
@ -260,6 +274,18 @@ Engine::RunStatus Engine::Run(RunConfiguration configuration) {
HandlePlatformMessage(std::move(service_id_message));
}
if (settings_.merged_platform_ui_thread ==
Settings::MergedPlatformUIThread::kMergeAfterLaunch) {
// Move the UI task runner to the platform thread.
bool success = fml::MessageLoopTaskQueues::GetInstance()->Merge(
task_runners_.GetPlatformTaskRunner()->GetTaskQueueId(),
task_runners_.GetUITaskRunner()->GetTaskQueueId());
if (!success) {
FML_LOG(ERROR)
<< "Unable to move the UI task runner to the platform thread";
}
}
return Engine::RunStatus::Success;
}

View File

@ -441,7 +441,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
/// @return The pointer to this instance of the engine. The engine may
/// only be accessed safely on the UI task runner.
///
fml::WeakPtr<Engine> GetWeakPtr() const;
fml::TaskRunnerAffineWeakPtr<Engine> GetWeakPtr() const;
//----------------------------------------------------------------------------
/// @brief Moves the root isolate to the `DartIsolate::Phase::Running`
@ -877,7 +877,7 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
std::shared_ptr<AssetManager> GetAssetManager() override;
// Return the weak_ptr of ImageDecoder.
fml::WeakPtr<ImageDecoder> GetImageDecoderWeakPtr();
fml::TaskRunnerAffineWeakPtr<ImageDecoder> GetImageDecoderWeakPtr();
//----------------------------------------------------------------------------
/// @brief Get the `ImageGeneratorRegistry` associated with the current
@ -885,7 +885,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
///
/// @return The engine's `ImageGeneratorRegistry`.
///
fml::WeakPtr<ImageGeneratorRegistry> GetImageGeneratorRegistry();
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
GetImageGeneratorRegistry();
// |PointerDataDispatcher::Delegate|
void DoDispatchPacket(std::unique_ptr<PointerDataPacket> packet,
@ -1084,7 +1085,8 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
const std::unique_ptr<ImageDecoder> image_decoder_;
ImageGeneratorRegistry image_generator_registry_;
TaskRunners task_runners_;
fml::WeakPtrFactory<Engine> weak_factory_; // Must be the last member.
fml::TaskRunnerAffineWeakPtrFactory<Engine>
weak_factory_; // Must be the last member.
FML_DISALLOW_COPY_AND_ASSIGN(Engine);
};

View File

@ -159,7 +159,8 @@ class SmoothPointerDataDispatcher : public DefaultPointerDataDispatcher {
bool is_pointer_data_in_progress_ = false;
// WeakPtrFactory must be the last member.
fml::WeakPtrFactory<SmoothPointerDataDispatcher> weak_factory_;
fml::TaskRunnerAffineWeakPtrFactory<SmoothPointerDataDispatcher>
weak_factory_;
FML_DISALLOW_COPY_AND_ASSIGN(SmoothPointerDataDispatcher);
};

View File

@ -567,6 +567,19 @@ Shell::~Shell() {
platform_latch.Signal();
}));
platform_latch.Wait();
if (settings_.merged_platform_ui_thread ==
Settings::MergedPlatformUIThread::kMergeAfterLaunch) {
// Move the UI task runner back to its original thread to enable shutdown of
// that thread.
auto task_queues = fml::MessageLoopTaskQueues::GetInstance();
auto platform_queue_id =
task_runners_.GetPlatformTaskRunner()->GetTaskQueueId();
auto ui_queue_id = task_runners_.GetUITaskRunner()->GetTaskQueueId();
if (task_queues->Owns(platform_queue_id, ui_queue_id)) {
task_queues->Unmerge(platform_queue_id, ui_queue_id);
}
}
}
std::unique_ptr<Shell> Shell::Spawn(
@ -575,6 +588,16 @@ std::unique_ptr<Shell> Shell::Spawn(
const CreateCallback<PlatformView>& on_create_platform_view,
const CreateCallback<Rasterizer>& on_create_rasterizer) const {
FML_DCHECK(task_runners_.IsValid());
if (settings_.merged_platform_ui_thread ==
Settings::MergedPlatformUIThread::kMergeAfterLaunch) {
// Spawning engines that share the same task runners can result in
// deadlocks when the UI task runner is moved to the platform thread.
FML_LOG(ERROR) << "MergedPlatformUIThread::kMergeAfterLaunch does not "
"support spawning";
return nullptr;
}
// It's safe to store this value since it is set on the platform thread.
bool is_gpu_disabled = false;
GetIsGpuDisabledSyncSwitch()->Execute(
@ -811,7 +834,7 @@ fml::TaskRunnerAffineWeakPtr<Rasterizer> Shell::GetRasterizer() const {
return weak_rasterizer_;
}
fml::WeakPtr<Engine> Shell::GetEngine() {
fml::TaskRunnerAffineWeakPtr<Engine> Shell::GetEngine() {
FML_DCHECK(is_set_up_);
return weak_engine_;
}
@ -1079,12 +1102,12 @@ void Shell::OnPlatformViewDispatchPlatformMessage(
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
fml::TaskRunner::RunNowAndFlushMessages(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable([engine = engine_->GetWeakPtr(),
message = std::move(message)]() mutable {
if (engine) {
engine->DispatchPlatformMessage(std::move(message));
}
}));
fml::MakeCopyable(
[engine = weak_engine_, message = std::move(message)]() mutable {
if (engine) {
engine->DispatchPlatformMessage(std::move(message));
}
}));
}
// |PlatformView::Delegate|

View File

@ -260,7 +260,7 @@ class Shell final : public PlatformView::Delegate,
///
/// @return A weak pointer to the engine.
///
fml::WeakPtr<Engine> GetEngine();
fml::TaskRunnerAffineWeakPtr<Engine> GetEngine();
//----------------------------------------------------------------------------
/// @brief Platform views may only be accessed on the platform task
@ -470,7 +470,8 @@ class Shell final : public PlatformView::Delegate,
std::shared_ptr<PlatformMessageHandler> platform_message_handler_;
std::atomic<bool> route_messages_through_platform_thread_ = false;
fml::WeakPtr<Engine> weak_engine_; // to be shared across threads
fml::TaskRunnerAffineWeakPtr<Engine>
weak_engine_; // to be shared across threads
fml::TaskRunnerAffineWeakPtr<Rasterizer>
weak_rasterizer_; // to be shared across threads
fml::WeakPtr<PlatformView>

View File

@ -25,8 +25,10 @@ static void StartupAndShutdownShell(benchmark::State& state,
{
benchmarking::ScopedPauseTiming pause(state, !measure_startup);
Settings settings = {};
settings.task_observer_add = [](intptr_t, const fml::closure&) {};
settings.task_observer_remove = [](intptr_t) {};
settings.task_observer_add = [](intptr_t, const fml::closure&) {
return fml::TaskQueueId::Invalid();
};
settings.task_observer_remove = [](fml::TaskQueueId, intptr_t) {};
if (DartVM::IsRunningPrecompiledCode()) {
aot_symbols = testing::LoadELFSymbolFromFixturesIfNeccessary(

View File

@ -226,7 +226,8 @@ void ShellTest::PumpOneFrame(Shell* shell, FrameContent frame_content) {
// tree pipeline nonempty. Without either of this, the layer tree below
// won't be rasterized.
fml::AutoResetWaitableEvent latch;
fml::WeakPtr<RuntimeDelegate> runtime_delegate = shell->weak_engine_;
fml::TaskRunnerAffineWeakPtr<RuntimeDelegate> runtime_delegate =
shell->weak_engine_;
shell->GetTaskRunners().GetUITaskRunner()->PostTask(
[&latch, engine = shell->weak_engine_, &frame_content,
runtime_delegate]() {
@ -341,10 +342,14 @@ Settings ShellTest::CreateSettingsForFixture() {
Settings settings;
settings.leak_vm = false;
settings.task_observer_add = [](intptr_t key, const fml::closure& handler) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, handler);
fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId();
fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key,
handler);
return queue_id;
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) {
fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id,
key);
};
settings.isolate_create_callback = [this]() {
native_resolver_->SetNativeResolverForIsolate();

View File

@ -5072,6 +5072,47 @@ TEST_F(ShellTest, ProvidesNullEngineId) {
DestroyShell(std::move(shell), task_runners);
}
TEST_F(ShellTest, MergeUIAndPlatformThreadsAfterLaunch) {
Settings settings = CreateSettingsForFixture();
settings.merged_platform_ui_thread =
Settings::MergedPlatformUIThread::kMergeAfterLaunch;
ThreadHost thread_host(ThreadHost::ThreadHostConfig(
"io.flutter.test." + GetCurrentTestName() + ".",
ThreadHost::Type::kPlatform | ThreadHost::Type::kRaster |
ThreadHost::Type::kIo | ThreadHost::Type::kUi));
TaskRunners task_runners("test", thread_host.platform_thread->GetTaskRunner(),
thread_host.raster_thread->GetTaskRunner(),
thread_host.ui_thread->GetTaskRunner(),
thread_host.io_thread->GetTaskRunner());
std::unique_ptr<Shell> shell = CreateShell(settings, task_runners);
ASSERT_TRUE(shell);
ASSERT_FALSE(fml::TaskRunnerChecker::RunsOnTheSameThread(
task_runners.GetUITaskRunner()->GetTaskQueueId(),
task_runners.GetPlatformTaskRunner()->GetTaskQueueId()));
fml::AutoResetWaitableEvent latch;
AddNativeCallback(
"NotifyNative", CREATE_NATIVE_ENTRY([&](auto args) {
ASSERT_TRUE(
task_runners.GetPlatformTaskRunner()->RunsTasksOnCurrentThread());
latch.Signal();
}));
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("mainNotifyNative");
RunEngine(shell.get(), std::move(configuration));
latch.Wait();
ASSERT_TRUE(fml::TaskRunnerChecker::RunsOnTheSameThread(
task_runners.GetUITaskRunner()->GetTaskQueueId(),
task_runners.GetPlatformTaskRunner()->GetTaskQueueId()));
DestroyShell(std::move(shell), task_runners);
}
} // namespace testing
} // namespace flutter

View File

@ -529,8 +529,26 @@ Settings SettingsFromCommandLine(const fml::CommandLine& command_line) {
settings.enable_surface_control = command_line.HasOption(
FlagForSwitch(Switch::EnableAndroidSurfaceControl));
settings.merged_platform_ui_thread = !command_line.HasOption(
FlagForSwitch(Switch::DisableMergedPlatformUIThread));
if (command_line.HasOption(
FlagForSwitch(Switch::DisableMergedPlatformUIThread))) {
settings.merged_platform_ui_thread =
Settings::MergedPlatformUIThread::kDisabled;
} else if (command_line.HasOption(
FlagForSwitch(Switch::MergedPlatformUIThread))) {
std::string merged_platform_ui;
command_line.GetOptionValue(FlagForSwitch(Switch::MergedPlatformUIThread),
&merged_platform_ui);
if (merged_platform_ui == "enabled") {
settings.merged_platform_ui_thread =
Settings::MergedPlatformUIThread::kEnabled;
} else if (merged_platform_ui == "disabled") {
settings.merged_platform_ui_thread =
Settings::MergedPlatformUIThread::kDisabled;
} else if (merged_platform_ui == "mergeAfterLaunch") {
settings.merged_platform_ui_thread =
Settings::MergedPlatformUIThread::kMergeAfterLaunch;
}
}
settings.enable_flutter_gpu =
command_line.HasOption(FlagForSwitch(Switch::EnableFlutterGPU));

View File

@ -294,6 +294,11 @@ DEF_SWITCH(EnableEmbedderAPI,
DEF_SWITCH(EnablePlatformIsolates,
"enable-platform-isolates",
"Enable support for isolates that run on the platform thread.")
DEF_SWITCH(MergedPlatformUIThread,
"merged-platform-ui-thread",
"Sets whether the ui thread and platform thread should be merged.")
// This is a legacy flag that has been superseded by merged-platform-ui-thread.
// TODO(163064): remove this when users have been migrated.
DEF_SWITCH(DisableMergedPlatformUIThread,
"no-enable-merged-platform-ui-thread",
"Merge the ui thread and platform thread.")

View File

@ -90,7 +90,8 @@ AndroidShellHolder::AndroidShellHolder(
auto thread_label = std::to_string(thread_host_count++);
auto mask = ThreadHost::Type::kRaster | ThreadHost::Type::kIo;
if (!settings.merged_platform_ui_thread) {
if (settings.merged_platform_ui_thread !=
Settings::MergedPlatformUIThread::kEnabled) {
mask |= ThreadHost::Type::kUi;
}
@ -139,7 +140,8 @@ AndroidShellHolder::AndroidShellHolder(
fml::RefPtr<fml::TaskRunner> platform_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
raster_runner = thread_host_->raster_thread->GetTaskRunner();
if (settings.merged_platform_ui_thread) {
if (settings.merged_platform_ui_thread ==
Settings::MergedPlatformUIThread::kEnabled) {
ui_runner = platform_runner;
} else {
ui_runner = thread_host_->ui_thread->GetTaskRunner();

View File

@ -201,7 +201,8 @@ TEST(AndroidShellHolder, CreateWithMergedPlatformAndUIThread) {
TEST(AndroidShellHolder, CreateWithUnMergedPlatformAndUIThread) {
Settings settings;
settings.merged_platform_ui_thread = false;
settings.merged_platform_ui_thread =
Settings::MergedPlatformUIThread::kDisabled;
auto jni = std::make_shared<MockPlatformViewAndroidJNI>();
auto holder = std::make_unique<AndroidShellHolder>(
settings, jni, AndroidRenderingAPI::kImpellerOpenGLES);

View File

@ -147,11 +147,15 @@ void FlutterMain::Init(JNIEnv* env,
}
settings.task_observer_add = [](intptr_t key, const fml::closure& callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback);
fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId();
fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key,
callback);
return queue_id;
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) {
fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id,
key);
};
settings.log_message_callback = [](const std::string& tag,

View File

@ -368,10 +368,8 @@ public class FlutterLoader {
if (metaData.getBoolean(IMPELLER_VULKAN_GPU_TRACING_DATA_KEY, false)) {
shellArgs.add("--enable-vulkan-gpu-tracing");
}
if (metaData.containsKey(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY)) {
if (metaData.getBoolean(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY)) {
shellArgs.add("--no-enable-merged-platform-ui-thread");
}
if (metaData.getBoolean(DISABLE_MERGED_PLATFORM_UI_THREAD_KEY, false)) {
shellArgs.add("--merged-platform-ui-thread=disabled");
}
if (metaData.getBoolean(ENABLE_FLUTTER_GPU, false)) {
shellArgs.add("--enable-flutter-gpu");

View File

@ -64,11 +64,13 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p
auto settings = flutter::SettingsFromCommandLine(command_line);
settings.task_observer_add = [](intptr_t key, const fml::closure& callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback);
fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId();
fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key, callback);
return queue_id;
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) {
fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id, key);
};
settings.log_message_callback = [](const std::string& tag, const std::string& message) {
@ -209,7 +211,9 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p
NSNumber* enableMergedPlatformUIThread =
[mainBundle objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"];
if (enableMergedPlatformUIThread != nil) {
settings.merged_platform_ui_thread = enableMergedPlatformUIThread.boolValue;
settings.merged_platform_ui_thread = enableMergedPlatformUIThread.boolValue
? flutter::Settings::MergedPlatformUIThread::kEnabled
: flutter::Settings::MergedPlatformUIThread::kDisabled;
}
NSNumber* enableFlutterGPU = [mainBundle objectForInfoDictionaryKey:@"FLTEnableFlutterGPU"];

View File

@ -752,7 +752,7 @@ static flutter::ThreadHost MakeThreadHost(NSString* thread_label,
fml::MessageLoop::EnsureInitializedForCurrentThread();
uint32_t threadHostType = flutter::ThreadHost::Type::kRaster | flutter::ThreadHost::Type::kIo;
if (!settings.merged_platform_ui_thread) {
if (settings.merged_platform_ui_thread != flutter::Settings::MergedPlatformUIThread::kEnabled) {
threadHostType |= flutter::ThreadHost::Type::kUi;
}
@ -839,7 +839,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
[](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
fml::RefPtr<fml::TaskRunner> ui_runner;
if (settings.enable_impeller && settings.merged_platform_ui_thread) {
if (settings.enable_impeller &&
settings.merged_platform_ui_thread == flutter::Settings::MergedPlatformUIThread::kEnabled) {
ui_runner = fml::MessageLoop::GetCurrent().GetTaskRunner();
} else {
ui_runner = _threadHost->ui_thread->GetTaskRunner();

View File

@ -528,7 +528,7 @@ FLUTTER_ASSERT_ARC
- (void)testCanUnMergePlatformAndUIThread {
#if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR
auto settings = FLTDefaultSettingsForBundle();
settings.merged_platform_ui_thread = false;
settings.merged_platform_ui_thread = flutter::Settings::MergedPlatformUIThread::kDisabled;
FlutterDartProject* project = [[FlutterDartProject alloc] initWithSettings:settings];
FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project];
[engine run];

View File

@ -2389,12 +2389,18 @@ FlutterEngineResult FlutterEngineInitialize(size_t version,
settings.task_observer_add = [has_ui_thread_message_loop](
intptr_t key, const fml::closure& callback) {
if (has_ui_thread_message_loop) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback);
fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId();
fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key,
callback);
return queue_id;
} else {
return fml::TaskQueueId::Invalid();
}
};
settings.task_observer_remove = [has_ui_thread_message_loop](intptr_t key) {
if (has_ui_thread_message_loop) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) {
if (queue_id.is_valid()) {
fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id,
key);
}
};

View File

@ -469,8 +469,9 @@ ComponentV2::ComponentV2(
std::bind(&fml::CurrentMessageLoopAddAfterTaskObserver,
std::placeholders::_1, std::placeholders::_2);
settings_.task_observer_remove = std::bind(
&fml::CurrentMessageLoopRemoveAfterTaskObserver, std::placeholders::_1);
settings_.task_observer_remove =
std::bind(&fml::CurrentMessageLoopRemoveAfterTaskObserver,
std::placeholders::_1, std::placeholders::_2);
settings_.log_message_callback = [](const std::string& tag,
const std::string& message) {

View File

@ -666,11 +666,15 @@ int main(int argc, char* argv[]) {
};
settings.task_observer_add = [](intptr_t key, const fml::closure& callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, callback);
fml::TaskQueueId queue_id = fml::MessageLoop::GetCurrentTaskQueueId();
fml::MessageLoopTaskQueues::GetInstance()->AddTaskObserver(queue_id, key,
callback);
return queue_id;
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
settings.task_observer_remove = [](fml::TaskQueueId queue_id, intptr_t key) {
fml::MessageLoopTaskQueues::GetInstance()->RemoveTaskObserver(queue_id,
key);
};
settings.unhandled_exception_callback = [](const std::string& error,

View File

@ -30,8 +30,10 @@ DartFixture::DartFixture(std::string kernel_filename,
Settings DartFixture::CreateSettingsForFixture() {
Settings settings;
settings.leak_vm = false;
settings.task_observer_add = [](intptr_t, const fml::closure&) {};
settings.task_observer_remove = [](intptr_t) {};
settings.task_observer_add = [](intptr_t, const fml::closure&) {
return fml::TaskQueueId::Invalid();
};
settings.task_observer_remove = [](fml::TaskQueueId, intptr_t) {};
settings.isolate_create_callback = [this]() {
native_resolver_->SetNativeResolverForIsolate();
};