From eb385e02b7917cbab15b0eedec91de35f962d1f0 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Wed, 9 Oct 2024 07:14:27 +0000 Subject: [PATCH] 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 --- gfx/thebes/AndroidSystemFontIterator.cpp | 106 +++++++++++++++++++++++ gfx/thebes/AndroidSystemFontIterator.h | 65 ++++++++++++++ gfx/thebes/gfxAndroidPlatform.cpp | 64 ++++++++++++++ gfx/thebes/gfxAndroidPlatform.h | 13 +++ gfx/thebes/gfxFT2FontList.cpp | 96 +++++--------------- gfx/thebes/moz.build | 3 + toolkit/xre/nsAppRunner.cpp | 7 ++ 7 files changed, 282 insertions(+), 72 deletions(-) create mode 100644 gfx/thebes/AndroidSystemFontIterator.cpp create mode 100644 gfx/thebes/AndroidSystemFontIterator.h diff --git a/gfx/thebes/AndroidSystemFontIterator.cpp b/gfx/thebes/AndroidSystemFontIterator.cpp new file mode 100644 index 000000000000..4590df4dbafe --- /dev/null +++ b/gfx/thebes/AndroidSystemFontIterator.cpp @@ -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 + +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 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 diff --git a/gfx/thebes/AndroidSystemFontIterator.h b/gfx/thebes/AndroidSystemFontIterator.h new file mode 100644 index 000000000000..f1507585129b --- /dev/null +++ b/gfx/thebes/AndroidSystemFontIterator.h @@ -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 Next(); + + private: + void* mIterator = nullptr; + + static _ASystemFontIterator_open sSystemFontIterator_open; + static _ASystemFontIterator_next sSystemFontIterator_next; + static _ASystemFontIterator_close sSystemFontIterator_close; +}; + +} // namespace mozilla + +#endif diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index fbc3d3fa80af..211c28ee0a70 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -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; diff --git a/gfx/thebes/gfxAndroidPlatform.h b/gfx/thebes/gfxAndroidPlatform.h index 598acdb571fb..7ea10db46b5c 100644 --- a/gfx/thebes/gfxAndroidPlatform.h +++ b/gfx/thebes/gfxAndroidPlatform.h @@ -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 */ diff --git a/gfx/thebes/gfxFT2FontList.cpp b/gfx/thebes/gfxFT2FontList.cpp index 2eba69dc6c94..1d5550badb89 100644 --- a/gfx/thebes/gfxFT2FontList.cpp +++ b/gfx/thebes/gfxFT2FontList.cpp @@ -60,8 +60,8 @@ #ifdef MOZ_WIDGET_ANDROID # include "AndroidBuild.h" +# include "AndroidSystemFontIterator.h" # include "mozilla/jni/Utils.h" -# include #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 = 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) diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index 48f2849e2025..14cdaf80524b 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -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", diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 61e55fd41801..94f6e7215bfc 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -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