Bug 1895926 - Call ASystemFontIterator_open on background thread at start up. r=jfkthame

When Android 12+, `ASystemFontIterator_open` doesn't read font
configuration XML file directly to support font update. So it causes
that first call of it becomes too slow.

So I would like to call it on background thread at start up to
initialize internal data in Android. This fox improves 50-100ms on
`gfxPlatform::Init`.

Gecko already uses similar to it for macOS and Windows (DirectWrite).

Differential Revision: https://phabricator.services.mozilla.com/D224896
This commit is contained in:
Makoto Kato
2024-10-09 07:14:27 +00:00
parent a889274628
commit eb385e02b7
7 changed files with 282 additions and 72 deletions

View File

@@ -0,0 +1,106 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AndroidSystemFontIterator.h"
#include "mozilla/Assertions.h"
#include "nsDebug.h"
#include <dlfcn.h>
namespace mozilla {
_ASystemFontIterator_open AndroidSystemFontIterator::sSystemFontIterator_open =
nullptr;
_ASystemFontIterator_next AndroidSystemFontIterator::sSystemFontIterator_next =
nullptr;
_ASystemFontIterator_close
AndroidSystemFontIterator::sSystemFontIterator_close = nullptr;
_AFont_getFontFilePath AndroidFont::sFont_getFontFilePath = nullptr;
_AFont_close AndroidFont::sFont_close = nullptr;
AndroidSystemFontIterator::~AndroidSystemFontIterator() {
if (!sSystemFontIterator_open) {
return;
}
if (!mIterator) {
return;
}
sSystemFontIterator_close(mIterator);
}
bool AndroidSystemFontIterator::Init() {
if (!sSystemFontIterator_open) {
void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
MOZ_ASSERT(handle);
sSystemFontIterator_open =
(_ASystemFontIterator_open)dlsym(handle, "ASystemFontIterator_open");
sSystemFontIterator_next =
(_ASystemFontIterator_next)dlsym(handle, "ASystemFontIterator_next");
sSystemFontIterator_close =
(_ASystemFontIterator_close)dlsym(handle, "ASystemFontIterator_close");
AndroidFont::sFont_getFontFilePath =
(_AFont_getFontFilePath)dlsym(handle, "AFont_getFontFilePath");
AndroidFont::sFont_close = (_AFont_close)dlsym(handle, "AFont_close");
if (NS_WARN_IF(!sSystemFontIterator_open) ||
NS_WARN_IF(!sSystemFontIterator_next) ||
NS_WARN_IF(!sSystemFontIterator_close) ||
NS_WARN_IF(!AndroidFont::sFont_getFontFilePath) ||
NS_WARN_IF(!AndroidFont::sFont_close)) {
sSystemFontIterator_open = nullptr;
return false;
}
}
mIterator = sSystemFontIterator_open();
return true;
}
Maybe<AndroidFont> AndroidSystemFontIterator::Next() {
if (NS_WARN_IF(!sSystemFontIterator_open)) {
return Nothing();
}
if (!mIterator) {
return Nothing();
}
void* font = sSystemFontIterator_next(mIterator);
if (!font) {
sSystemFontIterator_close(mIterator);
mIterator = nullptr;
return Nothing();
}
return Some(AndroidFont(font));
}
AndroidFont::~AndroidFont() {
if (NS_WARN_IF(!sFont_close)) {
return;
}
if (!mFont) {
return;
}
sFont_close(mFont);
}
const char* AndroidFont::GetFontFilePath() {
if (NS_WARN_IF(!sFont_getFontFilePath) || NS_WARN_IF(!mFont)) {
return nullptr;
}
return sFont_getFontFilePath(mFont);
}
} // namespace mozilla

View File

@@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef AndroidSystemFontIterator_h__
#define AndroidSystemFontIterator_h__
#include "mozilla/Maybe.h"
namespace mozilla {
typedef void* (*_ASystemFontIterator_open)();
typedef void* (*_ASystemFontIterator_next)(void*);
typedef void (*_ASystemFontIterator_close)(void*);
typedef const char* (*_AFont_getFontFilePath)(const void*);
typedef void (*_AFont_close)(void*);
class AndroidFont final {
public:
explicit AndroidFont(void* aFont) : mFont(aFont) {};
AndroidFont() = delete;
AndroidFont(AndroidFont&) = delete;
AndroidFont(AndroidFont&& aSrc) {
mFont = aSrc.mFont;
aSrc.mFont = nullptr;
}
~AndroidFont();
const char* GetFontFilePath();
private:
void* mFont;
static _AFont_getFontFilePath sFont_getFontFilePath;
static _AFont_close sFont_close;
friend class AndroidSystemFontIterator;
};
class AndroidSystemFontIterator final {
public:
AndroidSystemFontIterator() = default;
~AndroidSystemFontIterator();
bool Init();
Maybe<AndroidFont> Next();
private:
void* mIterator = nullptr;
static _ASystemFontIterator_open sSystemFontIterator_open;
static _ASystemFontIterator_next sSystemFontIterator_next;
static _ASystemFontIterator_close sSystemFontIterator_close;
};
} // namespace mozilla
#endif

View File

@@ -20,6 +20,9 @@
#include "mozilla/StaticPrefs_webgl.h"
#include "mozilla/widget/AndroidVsync.h"
#include "AndroidBuild.h"
#include "AndroidSystemFontIterator.h"
#include "GeckoProfiler.h"
#include "gfx2DGlue.h"
#include "gfxFT2FontList.h"
#include "gfxImageSurface.h"
@@ -73,6 +76,67 @@ NS_IMPL_ISUPPORTS(FreetypeReporter, nsIMemoryReporter)
static FT_MemoryRec_ sFreetypeMemoryRecord;
void gfxAndroidPlatform::FontAPIInitializeCallback(void* aUnused) {
AUTO_PROFILER_REGISTER_THREAD("InitializingFontAPI");
PR_SetCurrentThreadName("InitializingFontAPI");
// Call ASystemFontIterator_open
AndroidSystemFontIterator iterator;
iterator.Init();
}
PRThread* gfxAndroidPlatform::sFontAPIInitializeThread = nullptr;
nsCString gfxAndroidPlatform::sManufacturer;
// static
bool gfxAndroidPlatform::IsFontAPIDisabled(bool aDontCheckPref) {
if (!aDontCheckPref &&
StaticPrefs::gfx_font_list_use_font_match_api_force_enabled_AtStartup()) {
return false;
}
// OPPO, realme and OnePlus device seem to crash when using font match API
// (Bug 1787551).
if (sManufacturer.IsEmpty()) {
sManufacturer = java::sdk::Build::MANUFACTURER()->ToCString();
}
return (sManufacturer.EqualsLiteral("OPPO") ||
sManufacturer.EqualsLiteral("realme") ||
sManufacturer.EqualsLiteral("OnePlus"));
}
// static
void gfxAndroidPlatform::InitializeFontAPI() {
if (XRE_GetProcessType() != GeckoProcessType_Default) {
return;
}
// Android 12+ doesn't read font configuration XML files directly.
// It means that it is slow to call font API at first.
if (jni::GetAPIVersion() < 31) {
return;
}
// This will be called before XPCOM service isn't started. So don't check
// preferences.
if (IsFontAPIDisabled(true)) {
return;
}
sFontAPIInitializeThread = PR_CreateThread(
PR_USER_THREAD, FontAPIInitializeCallback, nullptr, PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0);
}
// static
void gfxAndroidPlatform::WaitForInitializeFontAPI() {
if (sFontAPIInitializeThread) {
PR_JoinThread(sFontAPIInitializeThread);
sFontAPIInitializeThread = nullptr;
}
}
gfxAndroidPlatform::gfxAndroidPlatform() {
// A custom allocator. It counts allocations, enabling memory reporting.
sFreetypeMemoryRecord.user = nullptr;

View File

@@ -42,13 +42,26 @@ class gfxAndroidPlatform final : public gfxPlatform {
static bool CheckVariationFontSupport();
// From Android 12, Font API doesn't read XML files only. To handle updated
// font, initializing font API causes that it analyzes all font files. So we
// have to call this API at start up on another thread to initialize it.
static void InitializeFontAPI();
static void WaitForInitializeFontAPI();
static bool IsFontAPIDisabled(bool aDontCheckPref = false);
protected:
void InitAcceleration() override;
bool AccelerateLayersByDefault() override { return true; }
private:
static void FontAPIInitializeCallback(void*);
gfxImageFormat mOffscreenFormat;
static PRThread* sFontAPIInitializeThread;
static nsCString sManufacturer;
};
#endif /* GFX_PLATFORM_ANDROID_H */

View File

@@ -60,8 +60,8 @@
#ifdef MOZ_WIDGET_ANDROID
# include "AndroidBuild.h"
# include "AndroidSystemFontIterator.h"
# include "mozilla/jni/Utils.h"
# include <dlfcn.h>
#endif
using namespace mozilla;
@@ -1556,19 +1556,6 @@ void gfxFT2FontList::FindFonts() {
#if defined(MOZ_WIDGET_ANDROID)
// Android API 29+ provides system font and font matcher API for native code.
typedef void* (*_ASystemFontIterator_open)();
typedef void* (*_ASystemFontIterator_next)(void*);
typedef void (*_ASystemFontIterator_close)(void*);
typedef const char* (*_AFont_getFontFilePath)(const void*);
typedef void (*_AFont_close)(void*);
static _ASystemFontIterator_open systemFontIterator_open = nullptr;
static _ASystemFontIterator_next systemFontIterator_next = nullptr;
static _ASystemFontIterator_close systemFontIterator_close = nullptr;
static _AFont_getFontFilePath font_getFontFilePath = nullptr;
static _AFont_close font_close = nullptr;
static bool firstTime = true;
nsAutoCString androidFontsRoot = [&] {
// ANDROID_ROOT is the root of the android system, typically /system;
@@ -1584,71 +1571,36 @@ void gfxFT2FontList::FindFonts() {
return root;
}();
if (firstTime) {
if (jni::GetAPIVersion() >= 29) {
void* handle = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
MOZ_ASSERT(handle);
systemFontIterator_open =
(_ASystemFontIterator_open)dlsym(handle, "ASystemFontIterator_open");
systemFontIterator_next =
(_ASystemFontIterator_next)dlsym(handle, "ASystemFontIterator_next");
systemFontIterator_close = (_ASystemFontIterator_close)dlsym(
handle, "ASystemFontIterator_close");
font_getFontFilePath =
(_AFont_getFontFilePath)dlsym(handle, "AFont_getFontFilePath");
font_close = (_AFont_close)dlsym(handle, "AFont_close");
if (NS_WARN_IF(!systemFontIterator_next) ||
NS_WARN_IF(!systemFontIterator_close) ||
NS_WARN_IF(!font_getFontFilePath) || NS_WARN_IF(!font_close)) {
// Since any functions aren't resolved, use old way to enumerate fonts.
systemFontIterator_open = nullptr;
}
}
firstTime = false;
}
bool useSystemFontAPI = !!systemFontIterator_open;
if (useSystemFontAPI &&
!StaticPrefs::
gfx_font_list_use_font_match_api_force_enabled_AtStartup()) {
// OPPO, realme and OnePlus device seem to crash when using font match API
// (Bug 1787551).
nsCString manufacturer = java::sdk::Build::MANUFACTURER()->ToCString();
if (manufacturer.EqualsLiteral("OPPO") ||
manufacturer.EqualsLiteral("realme") ||
manufacturer.EqualsLiteral("OnePlus")) {
useSystemFontAPI = false;
}
}
bool useSystemFontAPI = !gfxAndroidPlatform::IsFontAPIDisabled();
if (useSystemFontAPI) {
void* iter = systemFontIterator_open();
if (iter) {
void* font = systemFontIterator_next(iter);
while (font) {
nsDependentCString path(font_getFontFilePath(font));
AppendFacesFromFontFile(path, mFontNameCache.get(), kStandard);
font_close(font);
font = systemFontIterator_next(iter);
}
gfxAndroidPlatform::WaitForInitializeFontAPI();
if (!StaticPrefs::gfx_font_rendering_colr_v1_enabled()) {
// We turn off COLRv1 fonts support. Newer android versions have
// COLRv1 emoji font, and a legacy and hidden CBDT font we understand,
// so try to find NotoColorEmojiLegacy.ttf explicitly for now.
nsAutoCString legacyEmojiFont(androidFontsRoot);
legacyEmojiFont.Append("/NotoColorEmojiLegacy.ttf");
AppendFacesFromFontFile(legacyEmojiFont, mFontNameCache.get(),
kStandard);
bool noFontByFontAPI = true;
AndroidSystemFontIterator iter;
if (iter.Init()) {
while (Maybe<AndroidFont> androidFont = iter.Next()) {
if (const char* fontPath = androidFont->GetFontFilePath()) {
noFontByFontAPI = false;
AppendFacesFromFontFile(nsDependentCString(fontPath),
mFontNameCache.get(), kStandard);
}
}
}
systemFontIterator_close(iter);
} else {
if (noFontByFontAPI) {
// Font API doesn't seem to work. Use legacy way.
useSystemFontAPI = false;
}
if (!StaticPrefs::gfx_font_rendering_colr_v1_enabled()) {
// We turn off COLRv1 fonts support. Newer android versions have
// COLRv1 emoji font, and a legacy and hidden CBDT font we understand,
// so try to find NotoColorEmojiLegacy.ttf explicitly for now.
nsAutoCString legacyEmojiFont(androidFontsRoot);
legacyEmojiFont.Append("/NotoColorEmojiLegacy.ttf");
AppendFacesFromFontFile(legacyEmojiFont, mFontNameCache.get(), kStandard);
}
}
if (!useSystemFontAPI)

View File

@@ -96,6 +96,9 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
"gfxFT2Utils.cpp",
"PrintTargetPDF.cpp",
]
UNIFIED_SOURCES += [
"AndroidSystemFontIterator.cpp",
]
elif CONFIG["MOZ_WIDGET_TOOLKIT"] in ("cocoa", "uikit"):
EXPORTS += [
"gfxMacUtils.h",

View File

@@ -229,6 +229,7 @@
#include "GTestRunner.h"
#ifdef MOZ_WIDGET_ANDROID
# include "gfxAndroidPlatform.h"
# include "mozilla/java/GeckoAppShellWrappers.h"
#endif
@@ -5845,6 +5846,12 @@ int XREMain::XRE_main(int argc, char* argv[], const BootstrapConfig& aConfig) {
gfxPlatformMac::RegisterSupplementalFonts();
#endif
#ifdef MOZ_WIDGET_ANDROID
// We call the early because we run system font API on a background-thread
// to initialize internal font API data in Android. (Bug 1895926)
gfxAndroidPlatform::InitializeFontAPI();
#endif
#ifdef MOZ_CODE_COVERAGE
CodeCoverageHandler::Init();
#endif