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

Commit 2d86d1b

Browse files
authored
Avoid reloading font collection for spawned engines with compatible asset managers (#50897)
@jason-simmons @jiahaog fyi I need to figure out a test for this still but it seems to work. Hot restart is kind of a mess though, because `Engine::Restart` nulls out the asset manager it ends up reloading the font collection for every engine after a restart. But we separately need to fix hot restart, it's not very usable on the example app Fixes flutter/flutter#143701
1 parent 247971f commit 2d86d1b

File tree

12 files changed

+225
-5
lines changed

12 files changed

+225
-5
lines changed

assets/asset_manager.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,21 @@ AssetResolver::AssetResolverType AssetManager::GetType() const {
111111
return AssetResolverType::kAssetManager;
112112
}
113113

114+
bool AssetManager::operator==(const AssetResolver& other) const {
115+
const AssetManager* other_manager = other.as_asset_manager();
116+
if (!other_manager) {
117+
return false;
118+
}
119+
if (resolvers_.size() != other_manager->resolvers_.size()) {
120+
return false;
121+
}
122+
123+
for (size_t i = 0; i < resolvers_.size(); i++) {
124+
if (*resolvers_[i] != *other_manager->resolvers_[i]) {
125+
return false;
126+
}
127+
}
128+
return true;
129+
}
130+
114131
} // namespace flutter

assets/asset_manager.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ class AssetManager final : public AssetResolver {
8888
const std::string& asset_pattern,
8989
const std::optional<std::string>& subdir) const override;
9090

91+
// |AssetResolver|
92+
bool operator==(const AssetResolver& other) const override;
93+
94+
// |AssetResolver|
95+
const AssetManager* as_asset_manager() const override { return this; }
96+
9197
private:
9298
std::deque<std::unique_ptr<AssetResolver>> resolvers_;
9399

assets/asset_resolver.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414

1515
namespace flutter {
1616

17+
class AssetManager;
18+
class APKAssetProvider;
19+
class DirectoryAssetBundle;
20+
1721
class AssetResolver {
1822
public:
1923
AssetResolver() = default;
@@ -29,6 +33,14 @@ class AssetResolver {
2933
kDirectoryAssetBundle
3034
};
3135

36+
virtual const AssetManager* as_asset_manager() const { return nullptr; }
37+
virtual const APKAssetProvider* as_apk_asset_provider() const {
38+
return nullptr;
39+
}
40+
virtual const DirectoryAssetBundle* as_directory_asset_bundle() const {
41+
return nullptr;
42+
}
43+
3244
virtual bool IsValid() const = 0;
3345

3446
//----------------------------------------------------------------------------
@@ -81,6 +93,12 @@ class AssetResolver {
8193
return {};
8294
};
8395

96+
virtual bool operator==(const AssetResolver& other) const = 0;
97+
98+
bool operator!=(const AssetResolver& other) const {
99+
return !operator==(other);
100+
}
101+
84102
private:
85103
FML_DISALLOW_COPY_AND_ASSIGN(AssetResolver);
86104
};

assets/directory_asset_bundle.cc

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,14 @@ std::vector<std::unique_ptr<fml::Mapping>> DirectoryAssetBundle::GetAsMappings(
110110
return mappings;
111111
}
112112

113+
bool DirectoryAssetBundle::operator==(const AssetResolver& other) const {
114+
auto other_bundle = other.as_directory_asset_bundle();
115+
if (!other_bundle) {
116+
return false;
117+
}
118+
return is_valid_after_asset_manager_change_ ==
119+
other_bundle->is_valid_after_asset_manager_change_ &&
120+
descriptor_.get() == other_bundle->descriptor_.get();
121+
}
122+
113123
} // namespace flutter

assets/directory_asset_bundle.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ class DirectoryAssetBundle : public AssetResolver {
4343
const std::string& asset_pattern,
4444
const std::optional<std::string>& subdir) const override;
4545

46+
// |AssetResolver|
47+
bool operator==(const AssetResolver& other) const override;
48+
49+
// |AssetResolver|
50+
const DirectoryAssetBundle* as_directory_asset_bundle() const override {
51+
return this;
52+
}
53+
4654
FML_DISALLOW_COPY_AND_ASSIGN(DirectoryAssetBundle);
4755
};
4856

lib/ui/text/font_collection.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,15 @@ class FontCollection {
2020
public:
2121
FontCollection();
2222

23-
~FontCollection();
23+
virtual ~FontCollection();
2424

2525
std::shared_ptr<txt::FontCollection> GetFontCollection() const;
2626

2727
void SetupDefaultFontManager(uint32_t font_initialization_data);
2828

29-
void RegisterFonts(const std::shared_ptr<AssetManager>& asset_manager);
29+
// Virtual for testing.
30+
virtual void RegisterFonts(
31+
const std::shared_ptr<AssetManager>& asset_manager);
3032

3133
void RegisterTestFonts();
3234

shell/common/engine.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ std::unique_ptr<Engine> Engine::Spawn(
146146
/*image_generator_registry=*/result->GetImageGeneratorRegistry(),
147147
/*snapshot_delegate=*/std::move(snapshot_delegate));
148148
result->initial_route_ = initial_route;
149+
result->asset_manager_ = asset_manager_;
149150
return result;
150151
}
151152

@@ -174,7 +175,8 @@ fml::WeakPtr<ImageGeneratorRegistry> Engine::GetImageGeneratorRegistry() {
174175

175176
bool Engine::UpdateAssetManager(
176177
const std::shared_ptr<AssetManager>& new_asset_manager) {
177-
if (asset_manager_ == new_asset_manager) {
178+
if (asset_manager_ && new_asset_manager &&
179+
*asset_manager_ == *new_asset_manager) {
178180
return false;
179181
}
180182

shell/common/engine_unittests.cc

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@
1313
#include "flutter/shell/common/thread_host.h"
1414
#include "flutter/testing/fixture_test.h"
1515
#include "flutter/testing/testing.h"
16+
#include "fml/mapping.h"
1617
#include "gmock/gmock.h"
18+
#include "lib/ui/text/font_collection.h"
1719
#include "rapidjson/document.h"
1820
#include "rapidjson/stringbuffer.h"
1921
#include "rapidjson/writer.h"
22+
#include "runtime/isolate_configuration.h"
23+
#include "shell/common/run_configuration.h"
2024

2125
namespace flutter {
2226

@@ -37,6 +41,41 @@ void PostSync(const fml::RefPtr<fml::TaskRunner>& task_runner,
3741
latch.Wait();
3842
}
3943

44+
class FontManifestAssetResolver : public AssetResolver {
45+
public:
46+
FontManifestAssetResolver() {}
47+
48+
bool IsValid() const override { return true; }
49+
50+
bool IsValidAfterAssetManagerChange() const override { return true; }
51+
52+
AssetResolver::AssetResolverType GetType() const override {
53+
return AssetResolver::AssetResolverType::kApkAssetProvider;
54+
}
55+
56+
mutable size_t mapping_call_count = 0u;
57+
std::unique_ptr<fml::Mapping> GetAsMapping(
58+
const std::string& asset_name) const override {
59+
mapping_call_count++;
60+
if (asset_name == "FontManifest.json") {
61+
return std::make_unique<fml::DataMapping>("[{},{},{}]");
62+
}
63+
return nullptr;
64+
}
65+
66+
std::vector<std::unique_ptr<fml::Mapping>> GetAsMappings(
67+
const std::string& asset_pattern,
68+
const std::optional<std::string>& subdir) const override {
69+
return {};
70+
};
71+
72+
bool operator==(const AssetResolver& other) const override {
73+
auto mapping = GetAsMapping("FontManifest.json");
74+
return memcmp(other.GetAsMapping("FontManifest.json")->GetMapping(),
75+
mapping->GetMapping(), mapping->GetSize()) == 0;
76+
}
77+
};
78+
4079
class MockDelegate : public Engine::Delegate {
4180
public:
4281
MOCK_METHOD(void,
@@ -181,6 +220,14 @@ class MockPlatformMessageHandler : public PlatformMessageHandler {
181220
(override));
182221
};
183222

223+
class MockFontCollection : public FontCollection {
224+
public:
225+
MOCK_METHOD(void,
226+
RegisterFonts,
227+
(const std::shared_ptr<AssetManager>& asset_manager),
228+
(override));
229+
};
230+
184231
std::unique_ptr<PlatformMessage> MakePlatformMessage(
185232
const std::string& channel,
186233
const std::map<std::string, std::string>& values,
@@ -697,6 +744,89 @@ TEST_F(EngineTest, AnimatorSubmitWarmUpImplicitView) {
697744
draw_latch.Wait();
698745
}
699746

747+
TEST_F(EngineTest, SpawnedEngineInheritsAssetManager) {
748+
PostUITaskSync([this] {
749+
MockRuntimeDelegate client;
750+
auto mock_runtime_controller =
751+
std::make_unique<MockRuntimeController>(client, task_runners_);
752+
auto vm_ref = DartVMRef::Create(settings_);
753+
EXPECT_CALL(*mock_runtime_controller, GetDartVM())
754+
.WillRepeatedly(::testing::Return(vm_ref.get()));
755+
756+
// auto mock_font_collection = std::make_shared<MockFontCollection>();
757+
// EXPECT_CALL(*mock_font_collection, RegisterFonts(::testing::_))
758+
// .WillOnce(::testing::Return());
759+
auto engine = std::make_unique<Engine>(
760+
/*delegate=*/delegate_,
761+
/*dispatcher_maker=*/dispatcher_maker_,
762+
/*image_decoder_task_runner=*/image_decoder_task_runner_,
763+
/*task_runners=*/task_runners_,
764+
/*settings=*/settings_,
765+
/*animator=*/std::move(animator_),
766+
/*io_manager=*/io_manager_,
767+
/*font_collection=*/std::make_shared<FontCollection>(),
768+
/*runtime_controller=*/std::move(mock_runtime_controller),
769+
/*gpu_disabled_switch=*/std::make_shared<fml::SyncSwitch>());
770+
771+
EXPECT_EQ(engine->GetAssetManager(), nullptr);
772+
773+
auto asset_manager = std::make_shared<AssetManager>();
774+
asset_manager->PushBack(std::make_unique<FontManifestAssetResolver>());
775+
engine->UpdateAssetManager(asset_manager);
776+
EXPECT_EQ(engine->GetAssetManager(), asset_manager);
777+
778+
auto spawn =
779+
engine->Spawn(delegate_, dispatcher_maker_, settings_, nullptr,
780+
std::string(), io_manager_, snapshot_delegate_, nullptr);
781+
EXPECT_TRUE(spawn != nullptr);
782+
EXPECT_EQ(engine->GetAssetManager(), spawn->GetAssetManager());
783+
});
784+
}
785+
786+
TEST_F(EngineTest, UpdateAssetManagerWithEqualManagers) {
787+
PostUITaskSync([this] {
788+
MockRuntimeDelegate client;
789+
auto mock_runtime_controller =
790+
std::make_unique<MockRuntimeController>(client, task_runners_);
791+
auto vm_ref = DartVMRef::Create(settings_);
792+
EXPECT_CALL(*mock_runtime_controller, GetDartVM())
793+
.WillRepeatedly(::testing::Return(vm_ref.get()));
794+
795+
auto mock_font_collection = std::make_shared<MockFontCollection>();
796+
EXPECT_CALL(*mock_font_collection, RegisterFonts(::testing::_))
797+
.WillOnce(::testing::Return());
798+
auto engine = std::make_unique<Engine>(
799+
/*delegate=*/delegate_,
800+
/*dispatcher_maker=*/dispatcher_maker_,
801+
/*image_decoder_task_runner=*/image_decoder_task_runner_,
802+
/*task_runners=*/task_runners_,
803+
/*settings=*/settings_,
804+
/*animator=*/std::move(animator_),
805+
/*io_manager=*/io_manager_,
806+
/*font_collection=*/mock_font_collection,
807+
/*runtime_controller=*/std::move(mock_runtime_controller),
808+
/*gpu_disabled_switch=*/std::make_shared<fml::SyncSwitch>());
809+
810+
EXPECT_EQ(engine->GetAssetManager(), nullptr);
811+
812+
auto asset_manager = std::make_shared<AssetManager>();
813+
asset_manager->PushBack(std::make_unique<FontManifestAssetResolver>());
814+
815+
auto asset_manager_2 = std::make_shared<AssetManager>();
816+
asset_manager_2->PushBack(std::make_unique<FontManifestAssetResolver>());
817+
818+
EXPECT_NE(asset_manager, asset_manager_2);
819+
EXPECT_TRUE(*asset_manager == *asset_manager_2);
820+
821+
engine->UpdateAssetManager(asset_manager);
822+
EXPECT_EQ(engine->GetAssetManager(), asset_manager);
823+
824+
engine->UpdateAssetManager(asset_manager_2);
825+
// Didn't change because they're equivalent.
826+
EXPECT_EQ(engine->GetAssetManager(), asset_manager);
827+
});
828+
}
829+
700830
// The warm up frame should work if only some of the registered views are
701831
// included.
702832
//

shell/common/shell_unittests.cc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// found in the LICENSE file.
44

55
#include <strstream>
6-
#include "impeller/core/runtime_types.h"
76
#define FML_USED_ON_EMBEDDER
87

98
#include <algorithm>
@@ -19,6 +18,7 @@
1918
#include <EGL/egl.h>
2019
#endif // SHELL_ENABLE_GL
2120

21+
#include "assets/asset_resolver.h"
2222
#include "assets/directory_asset_bundle.h"
2323
#include "common/graphics/persistent_cache.h"
2424
#include "flutter/flow/layers/backdrop_filter_layer.h"
@@ -47,6 +47,7 @@
4747
#include "flutter/testing/mock_canvas.h"
4848
#include "flutter/testing/testing.h"
4949
#include "gmock/gmock.h"
50+
#include "impeller/core/runtime_types.h"
5051
#include "third_party/rapidjson/include/rapidjson/writer.h"
5152
#include "third_party/skia/include/codec/SkCodecAnimation.h"
5253
#include "third_party/tonic/converter/dart_converter.h"
@@ -246,6 +247,10 @@ class TestAssetResolver : public AssetResolver {
246247
return {};
247248
};
248249

250+
bool operator==(const AssetResolver& other) const override {
251+
return this == &other;
252+
}
253+
249254
private:
250255
bool valid_;
251256
AssetResolver::AssetResolverType type_;
@@ -283,6 +288,10 @@ class ThreadCheckingAssetResolver : public AssetResolver {
283288

284289
mutable std::vector<std::string> mapping_requests;
285290

291+
bool operator==(const AssetResolver& other) const override {
292+
return this == &other;
293+
}
294+
286295
private:
287296
std::shared_ptr<fml::ConcurrentMessageLoop> concurrent_loop_;
288297
};

shell/platform/android/apk_asset_provider.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <sstream>
1111
#include <utility>
1212

13+
#include "assets/asset_resolver.h"
1314
#include "flutter/fml/logging.h"
1415

1516
namespace flutter {
@@ -103,4 +104,12 @@ std::unique_ptr<APKAssetProvider> APKAssetProvider::Clone() const {
103104
return std::make_unique<APKAssetProvider>(impl_);
104105
}
105106

107+
bool APKAssetProvider::operator==(const AssetResolver& other) const {
108+
auto other_provider = other.as_apk_asset_provider();
109+
if (!other_provider) {
110+
return false;
111+
}
112+
return impl_ == other_provider->impl_;
113+
}
114+
106115
} // namespace flutter

0 commit comments

Comments
 (0)