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

Setup default font manager after engine created, to improve startup performance #18225

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions lib/ui/text/font_collection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ void _LoadFontFromList(Dart_NativeArguments args) {

FontCollection::FontCollection()
: collection_(std::make_shared<txt::FontCollection>()) {
collection_->SetupDefaultFontManager();

dynamic_font_manager_ = sk_make_sp<txt::DynamicFontManager>();
collection_->SetDynamicFontManager(dynamic_font_manager_);
}
Expand All @@ -68,6 +66,10 @@ std::shared_ptr<txt::FontCollection> FontCollection::GetFontCollection() const {
return collection_;
}

void FontCollection::SetupDefaultFontManager() {
collection_->SetupDefaultFontManager();
}

void FontCollection::RegisterFonts(
std::shared_ptr<AssetManager> asset_manager) {
std::unique_ptr<fml::Mapping> manifest_mapping =
Expand Down
2 changes: 2 additions & 0 deletions lib/ui/text/font_collection.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class FontCollection {

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

void SetupDefaultFontManager();

void RegisterFonts(std::shared_ptr<AssetManager> asset_manager);

void RegisterTestFonts();
Expand Down
5 changes: 5 additions & 0 deletions shell/common/engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ fml::WeakPtr<Engine> Engine::GetWeakPtr() const {
return weak_factory_.GetWeakPtr();
}

void Engine::SetupDefaultFontManager() {
TRACE_EVENT0("flutter", "Engine::SetupDefaultFontManager");
font_collection_.SetupDefaultFontManager();
}

bool Engine::UpdateAssetManager(
std::shared_ptr<AssetManager> new_asset_manager) {
if (asset_manager_ == new_asset_manager) {
Expand Down
5 changes: 5 additions & 0 deletions shell/common/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,11 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
///
[[nodiscard]] bool Restart(RunConfiguration configuration);

//----------------------------------------------------------------------------
/// @brief Setup default font manager according to specific platform.
///
void SetupDefaultFontManager();

//----------------------------------------------------------------------------
/// @brief Updates the asset manager referenced by the root isolate of a
/// Flutter application. This happens implicitly in the call to
Expand Down
8 changes: 8 additions & 0 deletions shell/common/shell.cc
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,14 @@ bool Shell::Setup(std::unique_ptr<PlatformView> platform_view,
weak_rasterizer_ = rasterizer_->GetWeakPtr();
weak_platform_view_ = platform_view_->GetWeakPtr();

// Setup the time-consuming default font manager right after engine created.
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = weak_engine_] {
if (engine) {
engine->SetupDefaultFontManager();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What protection do we have that someone won't use FontCollection.collection_ before this executes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FontCollection is only usable on the UI thread, and UI thread tasks are executed in the order that they were queued. Therefore the task that completes setup of the FontCollection will execute before any task that might query the FontCollection.

Copy link
Member

@gaaclarke gaaclarke May 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There could already be something in the UI event queue that will use the FontCollection though.

platform_thread->execute({
  ui_thread->execute({ shell->setup(); });
  ui_thread->execute({ someFontCollectionUsingFunction(); });
});

There is no guarantee in this case that SetupDefaultFontManager will execute before someFontCollectionUsingFunction.

edit: If this is how things work this should be safe. All usages of the font collection on the ui thread are given a reference to a shell that has already been setup. This might be the case.

platform_thread->execute({
  shell->setup();
  ui_thread->execute({ someFontCollectionUsingFunction(shell); });
});

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The embedder APIs will not return a shell to the caller until Shell::Setup has completed and the SetupDefaultFontManager task has already been queued.

}
});

is_setup_ = true;

vm_->GetServiceProtocol()->AddHandler(this, GetServiceProtocolDescription());
Expand Down
11 changes: 11 additions & 0 deletions shell/platform/android/flutter_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "flutter/shell/common/shell.h"
#include "flutter/shell/common/switches.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"
#include "third_party/skia/include/core/SkFontMgr.h"

namespace flutter {

Expand Down Expand Up @@ -155,6 +156,11 @@ void FlutterMain::SetupObservatoryUriCallback(JNIEnv* env) {
});
}

static void PrefetchDefaultFontManager(JNIEnv* env, jclass jcaller) {
// Initialize a singleton owned by Skia.
SkFontMgr::RefDefault();
}

bool FlutterMain::Register(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{
Expand All @@ -163,6 +169,11 @@ bool FlutterMain::Register(JNIEnv* env) {
"lang/String;Ljava/lang/String;Ljava/lang/String;J)V",
.fnPtr = reinterpret_cast<void*>(&Init),
},
{
.name = "nativePrefetchDefaultFontManager",
.signature = "()V",
.fnPtr = reinterpret_cast<void*>(&PrefetchDefaultFontManager),
},
};

jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ public static native void nativeInit(
@NonNull String engineCachesPath,
long initTimeMillis);

/**
* Prefetch the default font manager provided by SkFontMgr::RefDefault() which is a process-wide
* singleton owned by Skia. Note that, the first call to SkFontMgr::RefDefault() will take
* noticeable time, but later calls will return a reference to the preexisting font manager.
*/
public static native void nativePrefetchDefaultFontManager();

// TODO(mattcarroll): add javadocs
@UiThread
public native boolean nativeGetIsSoftwareRenderingEnabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,17 @@ public InitResult call() {

System.loadLibrary("flutter");

// Prefetch the default font manager as soon as possible on a background thread.
// It helps to reduce time cost of engine setup that blocks the platform thread.
Executors.newSingleThreadExecutor()
.execute(
new Runnable() {
@Override
public void run() {
FlutterJNI.nativePrefetchDefaultFontManager();
}
});

if (resourceExtractor != null) {
resourceExtractor.waitForCompletion();
}
Expand Down