Skip to content

Commit c53fdbd

Browse files
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 #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_;

0 commit comments

Comments
 (0)