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

Commit 14d06cb

Browse files
committed
Implemented FlutterEngineGroup and Spawn API.
1 parent 1d9678a commit 14d06cb

File tree

13 files changed

+360
-14
lines changed

13 files changed

+360
-14
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -945,6 +945,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDel
945945
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h
946946
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterDartProject.h
947947
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngine.h
948+
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterEngineGroup.h
948949
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterHeadlessDartRunner.h
949950
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h
950951
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h
@@ -963,6 +964,8 @@ FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartPro
963964
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm
964965
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h
965966
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
967+
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineGroup.mm
968+
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineGroupTest.mm
966969
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm
967970
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm
968971
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine_Internal.h

shell/common/shell.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,21 @@ Shell::~Shell() {
436436
platform_latch.Wait();
437437
}
438438

439+
std::unique_ptr<Shell> Shell::Spawn(
440+
Settings settings,
441+
const CreateCallback<PlatformView>& on_create_platform_view,
442+
const CreateCallback<Rasterizer>& on_create_rasterizer) {
443+
RunConfiguration configuration =
444+
RunConfiguration::InferFromSettings(settings);
445+
TaskRunners task_runners = task_runners_;
446+
FML_DCHECK(task_runners.IsValid());
447+
std::unique_ptr<Shell> result(Shell::Create(std::move(task_runners), settings,
448+
on_create_platform_view,
449+
on_create_rasterizer));
450+
result->RunEngine(std::move(configuration));
451+
return result;
452+
}
453+
439454
void Shell::NotifyLowMemoryWarning() const {
440455
auto trace_id = fml::tracing::TraceNonce();
441456
TRACE_EVENT_ASYNC_BEGIN0("flutter", "Shell::NotifyLowMemoryWarning",

shell/common/shell.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,19 @@ class Shell final : public PlatformView::Delegate,
220220
///
221221
~Shell();
222222

223+
//----------------------------------------------------------------------------
224+
/// @brief Creates one Shell from another Shell where the created Shell
225+
/// takes the opportunity share any internal components it can.
226+
/// This results is a Shell that has a smaller startup time cost
227+
/// and a smaller memory footprint than an Shell created with a
228+
/// Create function.
229+
///
230+
/// @see http://flutter.dev/go/multiple-engines
231+
std::unique_ptr<Shell> Spawn(
232+
Settings settings,
233+
const CreateCallback<PlatformView>& on_create_platform_view,
234+
const CreateCallback<Rasterizer>& on_create_rasterizer);
235+
223236
//----------------------------------------------------------------------------
224237
/// @brief Starts an isolate for the given RunConfiguration.
225238
///

shell/common/shell_unittests.cc

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "flutter/shell/common/vsync_waiter_fallback.h"
3333
#include "flutter/shell/version/version.h"
3434
#include "flutter/testing/testing.h"
35+
#include "gmock/gmock.h"
3536
#include "third_party/rapidjson/include/rapidjson/writer.h"
3637
#include "third_party/skia/include/core/SkPictureRecorder.h"
3738
#include "third_party/tonic/converter/dart_converter.h"
@@ -42,6 +43,77 @@
4243

4344
namespace flutter {
4445
namespace testing {
46+
namespace {
47+
class MockPlatformViewDelegate : public PlatformView::Delegate {
48+
MOCK_METHOD1(OnPlatformViewCreated, void(std::unique_ptr<Surface> surface));
49+
50+
MOCK_METHOD0(OnPlatformViewDestroyed, void());
51+
52+
MOCK_METHOD1(OnPlatformViewSetNextFrameCallback,
53+
void(const fml::closure& closure));
54+
55+
MOCK_METHOD1(OnPlatformViewSetViewportMetrics,
56+
void(const ViewportMetrics& metrics));
57+
58+
MOCK_METHOD1(OnPlatformViewDispatchPlatformMessage,
59+
void(fml::RefPtr<PlatformMessage> message));
60+
61+
MOCK_METHOD1(OnPlatformViewDispatchPointerDataPacket,
62+
void(std::unique_ptr<PointerDataPacket> packet));
63+
64+
MOCK_METHOD3(OnPlatformViewDispatchSemanticsAction,
65+
void(int32_t id,
66+
SemanticsAction action,
67+
std::vector<uint8_t> args));
68+
69+
MOCK_METHOD1(OnPlatformViewSetSemanticsEnabled, void(bool enabled));
70+
71+
MOCK_METHOD1(OnPlatformViewSetAccessibilityFeatures, void(int32_t flags));
72+
73+
MOCK_METHOD1(OnPlatformViewRegisterTexture,
74+
void(std::shared_ptr<Texture> texture));
75+
76+
MOCK_METHOD1(OnPlatformViewUnregisterTexture, void(int64_t texture_id));
77+
78+
MOCK_METHOD1(OnPlatformViewMarkTextureFrameAvailable,
79+
void(int64_t texture_id));
80+
81+
MOCK_METHOD3(LoadDartDeferredLibrary,
82+
void(intptr_t loading_unit_id,
83+
std::unique_ptr<const fml::Mapping> snapshot_data,
84+
std::unique_ptr<const fml::Mapping> snapshot_instructions));
85+
86+
MOCK_METHOD3(LoadDartDeferredLibraryError,
87+
void(intptr_t loading_unit_id,
88+
const std::string error_message,
89+
bool transient));
90+
91+
MOCK_METHOD1(UpdateAssetManager,
92+
void(std::shared_ptr<AssetManager> asset_manager));
93+
};
94+
95+
class MockSurface : public Surface {
96+
MOCK_METHOD0(IsValid, bool());
97+
98+
MOCK_METHOD1(AcquireFrame,
99+
std::unique_ptr<SurfaceFrame>(const SkISize& size));
100+
101+
MOCK_CONST_METHOD0(GetRootTransformation, SkMatrix());
102+
103+
MOCK_METHOD0(GetContext, GrDirectContext*());
104+
105+
MOCK_METHOD0(MakeRenderContextCurrent, std::unique_ptr<GLContextResult>());
106+
107+
MOCK_METHOD0(ClearRenderContext, bool());
108+
};
109+
110+
class MockPlatformView : public PlatformView {
111+
public:
112+
MockPlatformView(MockPlatformViewDelegate& delegate, TaskRunners task_runners)
113+
: PlatformView(delegate, task_runners) {}
114+
MOCK_METHOD0(CreateRenderingSurface, std::unique_ptr<Surface>());
115+
};
116+
} // namespace
45117

46118
static bool ValidateShell(Shell* shell) {
47119
if (!shell) {
@@ -2318,5 +2390,52 @@ TEST_F(ShellTest, AssetManagerMulti) {
23182390
}
23192391
}
23202392

2393+
TEST_F(ShellTest, Spawn) {
2394+
auto settings = CreateSettingsForFixture();
2395+
auto shell = CreateShell(settings);
2396+
ASSERT_TRUE(ValidateShell(shell.get()));
2397+
2398+
auto configuration = RunConfiguration::InferFromSettings(settings);
2399+
ASSERT_TRUE(configuration.IsValid());
2400+
configuration.SetEntrypoint("fixturesAreFunctionalMain");
2401+
2402+
fml::AutoResetWaitableEvent main_latch;
2403+
AddNativeCallback(
2404+
"SayHiFromFixturesAreFunctionalMain",
2405+
CREATE_NATIVE_ENTRY([&main_latch](auto args) { main_latch.Signal(); }));
2406+
2407+
RunEngine(shell.get(), std::move(configuration));
2408+
main_latch.Wait();
2409+
ASSERT_TRUE(DartVMRef::IsInstanceRunning());
2410+
2411+
{
2412+
fml::AutoResetWaitableEvent latch;
2413+
fml::TaskRunner::RunNowOrPostTask(
2414+
shell->GetTaskRunners().GetPlatformTaskRunner(),
2415+
[this, &spawner = shell, &latch, settings]() {
2416+
MockPlatformViewDelegate platform_view_delegate;
2417+
auto spawn = spawner->Spawn(
2418+
settings,
2419+
[&platform_view_delegate](Shell& shell) {
2420+
auto result = std::make_unique<MockPlatformView>(
2421+
platform_view_delegate, shell.GetTaskRunners());
2422+
ON_CALL(*result, CreateRenderingSurface())
2423+
.WillByDefault(::testing::Invoke(
2424+
[] { return std::make_unique<MockSurface>(); }));
2425+
return result;
2426+
},
2427+
[](Shell& shell) { return std::make_unique<Rasterizer>(shell); });
2428+
ASSERT_NE(nullptr, spawn.get());
2429+
ASSERT_TRUE(ValidateShell(spawn.get()));
2430+
DestroyShell(std::move(spawn));
2431+
latch.Signal();
2432+
});
2433+
latch.Wait();
2434+
}
2435+
2436+
DestroyShell(std::move(shell));
2437+
ASSERT_FALSE(DartVMRef::IsInstanceRunning());
2438+
}
2439+
23212440
} // namespace testing
23222441
} // namespace flutter

shell/platform/darwin/ios/BUILD.gn

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ _flutter_framework_headers = [
2828
"framework/Headers/FlutterCallbackCache.h",
2929
"framework/Headers/FlutterDartProject.h",
3030
"framework/Headers/FlutterEngine.h",
31+
"framework/Headers/FlutterEngineGroup.h",
3132
"framework/Headers/FlutterHeadlessDartRunner.h",
3233
"framework/Headers/FlutterPlatformViews.h",
3334
"framework/Headers/FlutterPlugin.h",
@@ -52,6 +53,7 @@ source_set("flutter_framework_source") {
5253
"framework/Source/FlutterDartProject.mm",
5354
"framework/Source/FlutterDartProject_Internal.h",
5455
"framework/Source/FlutterEngine.mm",
56+
"framework/Source/FlutterEngineGroup.mm",
5557
"framework/Source/FlutterEngine_Internal.h",
5658
"framework/Source/FlutterHeadlessDartRunner.mm",
5759
"framework/Source/FlutterObservatoryPublisher.h",
@@ -215,6 +217,7 @@ shared_library("ios_test_flutter") {
215217
"framework/Source/FlutterAppDelegateTest.mm",
216218
"framework/Source/FlutterBinaryMessengerRelayTest.mm",
217219
"framework/Source/FlutterDartProjectTest.mm",
220+
"framework/Source/FlutterEngineGroupTest.mm",
218221
"framework/Source/FlutterEngineTest.mm",
219222
"framework/Source/FlutterPluginAppLifeCycleDelegateTest.m",
220223
"framework/Source/FlutterTextInputPluginTest.m",

shell/platform/darwin/ios/framework/Headers/Flutter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#import "FlutterCodecs.h"
1313
#import "FlutterDartProject.h"
1414
#import "FlutterEngine.h"
15+
#import "FlutterEngineGroup.h"
1516
#import "FlutterHeadlessDartRunner.h"
1617
#import "FlutterMacros.h"
1718
#import "FlutterPlatformViews.h"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import <Foundation/Foundation.h>
6+
7+
#import "FlutterEngine.h"
8+
9+
NS_ASSUME_NONNULL_BEGIN
10+
11+
/**
12+
* Represents a collection of FlutterEngines who share resources which allows
13+
* them to be created with less time const and occupy less memory than just
14+
* creating multiple FlutterEngines.
15+
*
16+
* @see http://flutter.dev/go/multiple-engines
17+
*/
18+
@interface FlutterEngineGroup : NSObject
19+
- (instancetype)init NS_UNAVAILABLE;
20+
- (instancetype)initWithName:(NSString*)name NS_DESIGNATED_INITIALIZER;
21+
- (FlutterEngine*)makeEngineWithEntrypoint:(nullable NSString*)entrypoint;
22+
@end
23+
24+
NS_ASSUME_NONNULL_END

shell/platform/darwin/ios/framework/Source/FlutterEngine.mm

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
NSString* const FlutterDefaultDartEntrypoint = nil;
3636
NSString* const FlutterDefaultInitialRoute = nil;
37+
NSString* const FlutterEngineWillDealloc = @"FlutterEngineWillDealloc";
3738
static constexpr int kNumProfilerSamplesPerSec = 5;
3839

3940
@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
@@ -54,7 +55,7 @@ @interface FlutterEngine () <FlutterTextInputDelegate, FlutterBinaryMessenger>
5455

5556
@implementation FlutterEngine {
5657
fml::scoped_nsobject<FlutterDartProject> _dartProject;
57-
flutter::ThreadHost _threadHost;
58+
std::shared_ptr<flutter::ThreadHost> _threadHost;
5859
std::unique_ptr<flutter::Shell> _shell;
5960
NSString* _labelPrefix;
6061
std::unique_ptr<fml::WeakPtrFactory<FlutterEngine>> _weakFactory;
@@ -64,8 +65,8 @@ @implementation FlutterEngine {
6465

6566
std::shared_ptr<flutter::FlutterPlatformViewsController> _platformViewsController;
6667
flutter::IOSRenderingAPI _renderingApi;
67-
std::unique_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
68-
std::unique_ptr<flutter::SamplingProfiler> _profiler;
68+
std::shared_ptr<flutter::ProfilerMetricsIOS> _profiler_metrics;
69+
std::shared_ptr<flutter::SamplingProfiler> _profiler;
6970

7071
// Channels
7172
fml::scoped_nsobject<FlutterPlatformPlugin> _platformPlugin;
@@ -181,6 +182,10 @@ - (void)dealloc {
181182
}
182183
}];
183184

185+
[[NSNotificationCenter defaultCenter] postNotificationName:FlutterEngineWillDealloc
186+
object:self
187+
userInfo:nil];
188+
184189
/// nil out weak references.
185190
[_registrars
186191
enumerateKeysAndObjectsUsingBlock:^(id key, FlutterEngineRegistrar* registrar, BOOL* stop) {
@@ -306,7 +311,7 @@ - (void)destroyContext {
306311
self.isolateId = nil;
307312
_shell.reset();
308313
_profiler.reset();
309-
_threadHost.Reset();
314+
_threadHost.reset();
310315
_platformViewsController.reset();
311316
}
312317

@@ -368,10 +373,10 @@ - (void)resetChannels {
368373
}
369374

370375
- (void)startProfiler {
371-
FML_DCHECK(!_threadHost.name_prefix.empty());
372-
_profiler_metrics = std::make_unique<flutter::ProfilerMetricsIOS>();
373-
_profiler = std::make_unique<flutter::SamplingProfiler>(
374-
_threadHost.name_prefix.c_str(), _threadHost.profiler_thread->GetTaskRunner(),
376+
FML_DCHECK(!_threadHost->name_prefix.empty());
377+
_profiler_metrics = std::make_shared<flutter::ProfilerMetricsIOS>();
378+
_profiler = std::make_shared<flutter::SamplingProfiler>(
379+
_threadHost->name_prefix.c_str(), _threadHost->profiler_thread->GetTaskRunner(),
375380
[self]() { return self->_profiler_metrics->GenerateSample(); }, kNumProfilerSamplesPerSec);
376381
_profiler->Start();
377382
}
@@ -508,7 +513,7 @@ + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
508513
return [NSString stringWithFormat:@"%@.%zu", labelPrefix, ++s_shellCount];
509514
}
510515

511-
+ (flutter::ThreadHost)makeThreadHost:(NSString*)threadLabel {
516+
+ (std::shared_ptr<flutter::ThreadHost>)makeThreadHost:(NSString*)threadLabel {
512517
// The current thread will be used as the platform thread. Ensure that the message loop is
513518
// initialized.
514519
fml::MessageLoop::EnsureInitializedForCurrentThread();
@@ -518,8 +523,8 @@ + (NSString*)generateThreadLabel:(NSString*)labelPrefix {
518523
if ([FlutterEngine isProfilerEnabled]) {
519524
threadHostType = threadHostType | flutter::ThreadHost::Type::Profiler;
520525
}
521-
return {threadLabel.UTF8String, // label
522-
threadHostType};
526+
return std::make_shared<flutter::ThreadHost>(threadLabel.UTF8String, // label
527+
threadHostType);
523528
}
524529

525530
- (BOOL)createShell:(NSString*)entrypoint
@@ -566,9 +571,9 @@ - (BOOL)createShell:(NSString*)entrypoint
566571

567572
flutter::TaskRunners task_runners(threadLabel.UTF8String, // label
568573
fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
569-
_threadHost.raster_thread->GetTaskRunner(), // raster
570-
_threadHost.ui_thread->GetTaskRunner(), // ui
571-
_threadHost.io_thread->GetTaskRunner() // io
574+
_threadHost->raster_thread->GetTaskRunner(), // raster
575+
_threadHost->ui_thread->GetTaskRunner(), // ui
576+
_threadHost->io_thread->GetTaskRunner() // io
572577
);
573578

574579
// Create the shell. This is a blocking operation.
@@ -921,6 +926,42 @@ - (void)waitForFirstFrame:(NSTimeInterval)timeout
921926
});
922927
}
923928

929+
- (FlutterEngine*)spawnWithEntrypoint:(NSString*)entrypoint {
930+
assert(_shell);
931+
FlutterEngine* result =
932+
[[FlutterEngine alloc] initWithName:[_labelPrefix stringByAppendingString:@"-spawn"]
933+
project:_dartProject.get()
934+
allowHeadlessExecution:_allowHeadlessExecution];
935+
936+
flutter::Settings settings = _shell->GetSettings();
937+
if (entrypoint) {
938+
settings.advisory_script_entrypoint = entrypoint.UTF8String;
939+
settings.advisory_script_uri = std::string("main.dart");
940+
} else {
941+
settings.advisory_script_entrypoint = std::string("main");
942+
settings.advisory_script_uri = std::string("main.dart");
943+
}
944+
945+
flutter::Shell::CreateCallback<flutter::PlatformView> on_create_platform_view =
946+
[self](flutter::Shell& shell) {
947+
[self recreatePlatformViewController];
948+
return std::make_unique<flutter::PlatformViewIOS>(
949+
shell, self->_renderingApi, self->_platformViewsController, shell.GetTaskRunners());
950+
};
951+
952+
flutter::Shell::CreateCallback<flutter::Rasterizer> on_create_rasterizer =
953+
[](flutter::Shell& shell) { return std::make_unique<flutter::Rasterizer>(shell); };
954+
955+
std::unique_ptr<flutter::Shell> shell =
956+
_shell->Spawn(std::move(settings), on_create_platform_view, on_create_rasterizer);
957+
958+
result->_threadHost = _threadHost;
959+
result->_profiler = _profiler;
960+
result->_profiler_metrics = _profiler_metrics;
961+
[result setupShell:std::move(shell) withObservatoryPublication:NO];
962+
return result;
963+
}
964+
924965
@end
925966

926967
@implementation FlutterEngineRegistrar {

0 commit comments

Comments
 (0)