Skip to content

Commit c53fdbd

Browse files
Reduce app startup latency by initializing the engine on a separate thread (flutter#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 flutter#163064
1 parent 9f4fe84 commit c53fdbd

36 files changed

+292
-91
lines changed

engine/src/flutter/common/settings.h

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "flutter/fml/build_config.h"
1717
#include "flutter/fml/closure.h"
1818
#include "flutter/fml/mapping.h"
19+
#include "flutter/fml/task_queue_id.h"
1920
#include "flutter/fml/time/time_point.h"
2021
#include "flutter/fml/unique_fd.h"
2122

@@ -70,8 +71,10 @@ class FrameTiming {
7071
};
7172

7273
using TaskObserverAdd =
73-
std::function<void(intptr_t /* key */, fml::closure /* callback */)>;
74-
using TaskObserverRemove = std::function<void(intptr_t /* key */)>;
74+
std::function<fml::TaskQueueId(intptr_t /* key */,
75+
fml::closure /* callback */)>;
76+
using TaskObserverRemove =
77+
std::function<void(fml::TaskQueueId /* queue */, intptr_t /* key */)>;
7578
using UnhandledExceptionCallback =
7679
std::function<bool(const std::string& /* error */,
7780
const std::string& /* stack trace */)>;
@@ -359,9 +362,20 @@ struct Settings {
359362
/// This is used by the runOnPlatformThread API.
360363
bool enable_platform_isolates = false;
361364

362-
// If true, the UI thread is the platform thread on supported
363-
// platforms.
364-
bool merged_platform_ui_thread = true;
365+
enum class MergedPlatformUIThread {
366+
// Use separate threads for the UI and platform task runners.
367+
kDisabled,
368+
// Use the platform thread for both the UI and platform task runners.
369+
kEnabled,
370+
// Start the engine on a separate UI thread and then move the UI task
371+
// runner to the platform thread after the engine is initialized.
372+
// This can improve app launch latency by allowing other work to run on
373+
// the platform thread during engine startup.
374+
kMergeAfterLaunch
375+
};
376+
377+
MergedPlatformUIThread merged_platform_ui_thread =
378+
MergedPlatformUIThread::kEnabled;
365379
};
366380

367381
} // namespace flutter

engine/src/flutter/fml/platform/fuchsia/task_observers.cc

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,16 @@ void ExecuteAfterTaskObservers() {
1616
}
1717
}
1818

19-
void CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
20-
fit::closure observer) {
19+
fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
20+
fit::closure observer) {
2121
if (observer) {
2222
tTaskObservers[key] = std::move(observer);
2323
}
24+
return fml::TaskQueueId::Invalid();
2425
}
2526

26-
void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key) {
27+
void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id,
28+
intptr_t key) {
2729
tTaskObservers.erase(key);
2830
}
2931

engine/src/flutter/fml/platform/fuchsia/task_observers.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include <lib/fit/function.h>
99

10+
#include "flutter/fml/task_queue_id.h"
11+
1012
namespace fml {
1113

1214
// Executes all closures that were registered via
@@ -30,10 +32,11 @@ namespace fml {
3032
// somehow.
3133
void ExecuteAfterTaskObservers();
3234

33-
void CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
34-
fit::closure observer);
35+
fml::TaskQueueId CurrentMessageLoopAddAfterTaskObserver(intptr_t key,
36+
fit::closure observer);
3537

36-
void CurrentMessageLoopRemoveAfterTaskObserver(intptr_t key);
38+
void CurrentMessageLoopRemoveAfterTaskObserver(fml::TaskQueueId queue_id,
39+
intptr_t key);
3740

3841
} // namespace fml
3942

engine/src/flutter/fml/task_queue_id.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ class TaskQueueId {
2525
/// Intializes a task queue with the given value as it's ID.
2626
explicit TaskQueueId(size_t value) : value_(value) {}
2727

28+
static TaskQueueId Invalid() { return TaskQueueId(kInvalid); }
29+
2830
operator size_t() const { // NOLINT(google-explicit-constructor)
2931
return value_;
3032
}

engine/src/flutter/lib/ui/painting/image_decoder.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ ImageDecoder::ImageDecoder(
5555

5656
ImageDecoder::~ImageDecoder() = default;
5757

58-
fml::WeakPtr<ImageDecoder> ImageDecoder::GetWeakPtr() const {
58+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> ImageDecoder::GetWeakPtr() const {
5959
return weak_factory_.GetWeakPtr();
6060
}
6161

engine/src/flutter/lib/ui/painting/image_decoder.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class ImageDecoder {
4444
uint32_t target_height,
4545
const ImageResult& result) = 0;
4646

47-
fml::WeakPtr<ImageDecoder> GetWeakPtr() const;
47+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> GetWeakPtr() const;
4848

4949
protected:
5050
TaskRunners runners_;
@@ -57,7 +57,7 @@ class ImageDecoder {
5757
fml::WeakPtr<IOManager> io_manager);
5858

5959
private:
60-
fml::WeakPtrFactory<ImageDecoder> weak_factory_;
60+
fml::TaskRunnerAffineWeakPtrFactory<ImageDecoder> weak_factory_;
6161

6262
FML_DISALLOW_COPY_AND_ASSIGN(ImageDecoder);
6363
};

engine/src/flutter/lib/ui/painting/image_generator_registry.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ ImageGeneratorRegistry::CreateCompatibleGenerator(const sk_sp<SkData>& buffer) {
7979
return nullptr;
8080
}
8181

82-
fml::WeakPtr<ImageGeneratorRegistry> ImageGeneratorRegistry::GetWeakPtr()
83-
const {
82+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
83+
ImageGeneratorRegistry::GetWeakPtr() const {
8484
return weak_factory_.GetWeakPtr();
8585
}
8686

engine/src/flutter/lib/ui/painting/image_generator_registry.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class ImageGeneratorRegistry {
5858
std::shared_ptr<ImageGenerator> CreateCompatibleGenerator(
5959
const sk_sp<SkData>& buffer);
6060

61-
fml::WeakPtr<ImageGeneratorRegistry> GetWeakPtr() const;
61+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry> GetWeakPtr() const;
6262

6363
private:
6464
struct PrioritizedFactory {
@@ -85,7 +85,7 @@ class ImageGeneratorRegistry {
8585
using FactorySet = std::set<PrioritizedFactory, Compare>;
8686
FactorySet image_generator_factories_;
8787
size_t nonce_;
88-
fml::WeakPtrFactory<ImageGeneratorRegistry> weak_factory_;
88+
fml::TaskRunnerAffineWeakPtrFactory<ImageGeneratorRegistry> weak_factory_;
8989
};
9090

9191
} // namespace flutter

engine/src/flutter/lib/ui/ui_dart_state.cc

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ UIDartState::Context::Context(
2424
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate,
2525
fml::WeakPtr<IOManager> io_manager,
2626
fml::RefPtr<SkiaUnrefQueue> unref_queue,
27-
fml::WeakPtr<ImageDecoder> image_decoder,
28-
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
27+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
28+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
29+
image_generator_registry,
2930
std::string advisory_script_uri,
3031
std::string advisory_script_entrypoint,
3132
bool deterministic_rendering_enabled,
@@ -58,6 +59,7 @@ UIDartState::UIDartState(
5859
const UIDartState::Context& context)
5960
: add_callback_(std::move(add_callback)),
6061
remove_callback_(std::move(remove_callback)),
62+
callback_queue_id_(fml::TaskQueueId::kInvalid),
6163
logger_prefix_(std::move(logger_prefix)),
6264
is_root_isolate_(is_root_isolate),
6365
unhandled_exception_callback_(std::move(unhandled_exception_callback)),
@@ -178,10 +180,12 @@ void UIDartState::AddOrRemoveTaskObserver(bool add) {
178180
}
179181
FML_DCHECK(add_callback_ && remove_callback_);
180182
if (add) {
181-
add_callback_(reinterpret_cast<intptr_t>(this),
182-
[this]() { this->FlushMicrotasksNow(); });
183+
callback_queue_id_ =
184+
add_callback_(reinterpret_cast<intptr_t>(this),
185+
[this]() { this->FlushMicrotasksNow(); });
183186
} else {
184-
remove_callback_(reinterpret_cast<intptr_t>(this));
187+
remove_callback_(callback_queue_id_, reinterpret_cast<intptr_t>(this));
188+
callback_queue_id_ = fml::TaskQueueId::Invalid();
185189
}
186190
}
187191

@@ -190,12 +194,13 @@ UIDartState::GetSnapshotDelegate() const {
190194
return context_.snapshot_delegate;
191195
}
192196

193-
fml::WeakPtr<ImageDecoder> UIDartState::GetImageDecoder() const {
197+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> UIDartState::GetImageDecoder()
198+
const {
194199
return context_.image_decoder;
195200
}
196201

197-
fml::WeakPtr<ImageGeneratorRegistry> UIDartState::GetImageGeneratorRegistry()
198-
const {
202+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
203+
UIDartState::GetImageGeneratorRegistry() const {
199204
return context_.image_generator_registry;
200205
}
201206

engine/src/flutter/lib/ui/ui_dart_state.h

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@ class UIDartState : public tonic::DartState {
4848
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate,
4949
fml::WeakPtr<IOManager> io_manager,
5050
fml::RefPtr<SkiaUnrefQueue> unref_queue,
51-
fml::WeakPtr<ImageDecoder> image_decoder,
52-
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
51+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
52+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
53+
image_generator_registry,
5354
std::string advisory_script_uri,
5455
std::string advisory_script_entrypoint,
5556
bool deterministic_rendering_enabled,
@@ -76,12 +77,13 @@ class UIDartState : public tonic::DartState {
7677
fml::RefPtr<SkiaUnrefQueue> unref_queue;
7778

7879
/// The image decoder.
79-
fml::WeakPtr<ImageDecoder> image_decoder;
80+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder;
8081

8182
/// Cascading registry of image generator builders. Given compressed image
8283
/// bytes as input, this is used to find and create image generators, which
8384
/// can then be used for image decoding.
84-
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry;
85+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
86+
image_generator_registry;
8587

8688
/// The advisory script URI (only used for debugging). This does not affect
8789
/// the code being run in the isolate in any way.
@@ -144,9 +146,10 @@ class UIDartState : public tonic::DartState {
144146

145147
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> GetSnapshotDelegate() const;
146148

147-
fml::WeakPtr<ImageDecoder> GetImageDecoder() const;
149+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> GetImageDecoder() const;
148150

149-
fml::WeakPtr<ImageGeneratorRegistry> GetImageGeneratorRegistry() const;
151+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
152+
GetImageGeneratorRegistry() const;
150153

151154
std::shared_ptr<IsolateNameServer> GetIsolateNameServer() const;
152155

@@ -205,6 +208,7 @@ class UIDartState : public tonic::DartState {
205208

206209
const TaskObserverAdd add_callback_;
207210
const TaskObserverRemove remove_callback_;
211+
fml::TaskQueueId callback_queue_id_;
208212
const std::string logger_prefix_;
209213
Dart_Port main_port_ = ILLEGAL_PORT;
210214
const bool is_root_isolate_;

engine/src/flutter/runtime/dart_isolate.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ std::weak_ptr<DartIsolate> DartIsolate::CreateRunningRootIsolate(
155155

156156
{
157157
tonic::DartState::Scope scope(isolate.get());
158-
Dart_SetCurrentThreadOwnsIsolate();
158+
if (settings.merged_platform_ui_thread !=
159+
Settings::MergedPlatformUIThread::kMergeAfterLaunch) {
160+
Dart_SetCurrentThreadOwnsIsolate();
161+
}
159162

160163
if (settings.root_isolate_create_callback) {
161164
// Isolate callbacks always occur in isolate scope and before user code
@@ -349,6 +352,7 @@ Dart_Isolate DartIsolate::CreatePlatformIsolate(Dart_Handle entry_point,
349352
}
350353
old_task_observer_add(key, callback);
351354
});
355+
return platform_task_runner->GetTaskQueueId();
352356
};
353357

354358
UIDartState::Context context(task_runners);
@@ -1373,6 +1377,11 @@ std::weak_ptr<DartIsolate> DartIsolate::GetWeakIsolatePtr() {
13731377
return std::static_pointer_cast<DartIsolate>(shared_from_this());
13741378
}
13751379

1380+
void DartIsolate::SetOwnerToCurrentThread() {
1381+
tonic::DartIsolateScope isolate_scope(isolate());
1382+
Dart_SetCurrentThreadOwnsIsolate();
1383+
}
1384+
13761385
void DartIsolate::AddIsolateShutdownCallback(const fml::closure& closure) {
13771386
shutdown_callbacks_.emplace_back(std::make_unique<AutoFireClosure>(closure));
13781387
}

engine/src/flutter/runtime/dart_isolate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,10 @@ class DartIsolate : public UIDartState {
411411
static Dart_Handle LoadLibraryFromKernel(
412412
const std::shared_ptr<const fml::Mapping>& mapping);
413413

414+
// Calls a Dart API that sets the isolate's owner thread to the current
415+
// thread.
416+
void SetOwnerToCurrentThread();
417+
414418
private:
415419
friend class IsolateConfiguration;
416420
class AutoFireClosure {

engine/src/flutter/runtime/runtime_controller.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@ std::unique_ptr<RuntimeController> RuntimeController::Spawn(
5656
const fml::closure& p_isolate_shutdown_callback,
5757
const std::shared_ptr<const fml::Mapping>& p_persistent_isolate_data,
5858
fml::WeakPtr<IOManager> io_manager,
59-
fml::WeakPtr<ImageDecoder> image_decoder,
60-
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
59+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
60+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
61+
image_generator_registry,
6162
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate) const {
6263
UIDartState::Context spawned_context{context_.task_runners,
6364
std::move(snapshot_delegate),
@@ -676,6 +677,13 @@ void RuntimeController::ShutdownPlatformIsolates() {
676677
platform_isolate_manager_->ShutdownPlatformIsolates();
677678
}
678679

680+
void RuntimeController::SetRootIsolateOwnerToCurrentThread() {
681+
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
682+
if (root_isolate) {
683+
root_isolate->SetOwnerToCurrentThread();
684+
}
685+
}
686+
679687
RuntimeController::Locale::Locale(std::string language_code_,
680688
std::string country_code_,
681689
std::string script_code_,

engine/src/flutter/runtime/runtime_controller.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,9 @@ class RuntimeController : public PlatformConfigurationClient,
121121
const fml::closure& isolate_shutdown_callback,
122122
const std::shared_ptr<const fml::Mapping>& persistent_isolate_data,
123123
fml::WeakPtr<IOManager> io_manager,
124-
fml::WeakPtr<ImageDecoder> image_decoder,
125-
fml::WeakPtr<ImageGeneratorRegistry> image_generator_registry,
124+
fml::TaskRunnerAffineWeakPtr<ImageDecoder> image_decoder,
125+
fml::TaskRunnerAffineWeakPtr<ImageGeneratorRegistry>
126+
image_generator_registry,
126127
fml::TaskRunnerAffineWeakPtr<SnapshotDelegate> snapshot_delegate) const;
127128

128129
// |PlatformConfigurationClient|
@@ -678,6 +679,8 @@ class RuntimeController : public PlatformConfigurationClient,
678679
return platform_isolate_manager_;
679680
}
680681

682+
void SetRootIsolateOwnerToCurrentThread();
683+
681684
//--------------------------------------------------------------------------
682685
/// @brief Shuts down all registered platform isolates. Must be called
683686
/// from the platform thread.

engine/src/flutter/shell/common/animator.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class Animator final {
148148
std::deque<uint64_t> trace_flow_ids_;
149149
bool has_rendered_ = false;
150150

151-
fml::WeakPtrFactory<Animator> weak_factory_;
151+
fml::TaskRunnerAffineWeakPtrFactory<Animator> weak_factory_;
152152

153153
friend class testing::ShellTest;
154154

0 commit comments

Comments
 (0)