From 95e20866bb507d2a398389a9537e94fda44f9d87 Mon Sep 17 00:00:00 2001 From: Bowon Ryu Date: Fri, 24 Mar 2023 11:30:02 +0900 Subject: [PATCH] [Tizen] Add FontClientPreCache API to FontClient This patch adds a new FontClientPreCache API to enable pre-caching of fonts and improve the runtime performance of the application. Pre-caching default fonts and fallback lists can enhance the responsiveness performance by minimizing the calls to FcFontSort and FcFontMatch, particularly during the early stages of boot-up. Change-Id: Ib07adb7ef701f0f7a83cf17b73b049686bad4b12 Signed-off-by: Bowon Ryu --- dali/devel-api/text-abstraction/font-client.cpp | 5 ++ dali/devel-api/text-abstraction/font-client.h | 10 +++ dali/devel-api/text-abstraction/font-list.h | 1 + .../text/text-abstraction/font-client-impl.cpp | 91 ++++++++++++++++++++++ .../text/text-abstraction/font-client-impl.h | 22 ++++++ .../plugin/font-client-plugin-impl.cpp | 54 +++++++++++++ .../plugin/font-client-plugin-impl.h | 5 ++ 7 files changed, 188 insertions(+) diff --git a/dali/devel-api/text-abstraction/font-client.cpp b/dali/devel-api/text-abstraction/font-client.cpp index 27c9160..63a9e80 100644 --- a/dali/devel-api/text-abstraction/font-client.cpp +++ b/dali/devel-api/text-abstraction/font-client.cpp @@ -854,6 +854,11 @@ FontClient FontClientPreInitialize() return Internal::FontClient::PreInitialize(); } +void FontClientPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread) +{ + Internal::FontClient::PreCache(fallbackFamilyList, extraFamilyList, localeFamily, useThread); +} + } // namespace TextAbstraction } // namespace Dali diff --git a/dali/devel-api/text-abstraction/font-client.h b/dali/devel-api/text-abstraction/font-client.h index 2f75e8c..0ea768e 100644 --- a/dali/devel-api/text-abstraction/font-client.h +++ b/dali/devel-api/text-abstraction/font-client.h @@ -648,6 +648,16 @@ public: // Not intended for application developers */ DALI_ADAPTOR_API FontClient FontClientPreInitialize(); +/** + * @brief This is used to pre-cache fonts in order to improve the runtime performance of the application. + * + * @param[in] fallbackFamilyList A list of fallback font families to be pre-cached. + * @param[in] extraFamilyList A list of additional font families to be pre-cached. + * @param[in] localeFamily A locale font family to be pre-cached. + * @param[in] useThread True if the font client should create thread and perform pre-caching, false otherwise. + */ +DALI_ADAPTOR_API void FontClientPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread); + } // namespace TextAbstraction } // namespace Dali diff --git a/dali/devel-api/text-abstraction/font-list.h b/dali/devel-api/text-abstraction/font-list.h index 13e459d..9cb44cc 100644 --- a/dali/devel-api/text-abstraction/font-list.h +++ b/dali/devel-api/text-abstraction/font-list.h @@ -33,6 +33,7 @@ namespace TextAbstraction typedef std::string FontPath; typedef std::string FontFamily; typedef std::string FontStyle; +typedef std::vector FontFamilyList; namespace FontWidth { diff --git a/dali/internal/text/text-abstraction/font-client-impl.cpp b/dali/internal/text/text-abstraction/font-client-impl.cpp index fad420c..af1257a 100644 --- a/dali/internal/text/text-abstraction/font-client-impl.cpp +++ b/dali/internal/text/text-abstraction/font-client-impl.cpp @@ -19,17 +19,34 @@ #include // EXTERNAL INCLUDES +#include #if defined(VCONF_ENABLED) #include #endif // INTERNAL INCLUDES #include +#include #include #include #include +#define FONT_LOG_MESSAGE(level, format, ...) \ + do \ + { \ + char buffer[256]; \ + int result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \ + if (result >= static_cast(sizeof(buffer))) \ + { \ + std::string log("Font log message is too long to fit in the buffer.\n"); \ + Dali::TizenPlatform::LogMessage(Dali::Integration::Log::ERROR, log); \ + break; \ + } \ + std::string log(buffer); \ + Dali::TizenPlatform::LogMessage(level, log); \ + } while(0) + namespace Dali { namespace TextAbstraction @@ -37,6 +54,12 @@ namespace TextAbstraction namespace Internal { Dali::TextAbstraction::FontClient FontClient::gPreInitializedFontClient(NULL); +Dali::TextAbstraction::FontClient FontClient::gPreCachedFontClient(NULL); +std::thread gPreCacheThread; +/* TODO: This is to prevent duplicate calls of font pre-cache. + * We may support this later, but currently we can't guarantee the behaviour + * if there is a pre-cache call from the user after the font client has been created. */ +bool gFontPreCacheAvailable = true; FontClient::FontClient() : mPlugin(nullptr), @@ -67,16 +90,30 @@ Dali::TextAbstraction::FontClient FontClient::Get() } else // create and register the object { + if(gPreCacheThread.joinable()) + { + gPreCacheThread.join(); + FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache thread join\n"); + } + if(gPreInitializedFontClient) { fontClientHandle = gPreInitializedFontClient; gPreInitializedFontClient.Reset(); // No longer needed } + else if(gPreCachedFontClient) + { + // TODO: Currently font pre-caching is not available in the candidate process. + fontClientHandle = gPreCachedFontClient; + gPreCachedFontClient.Reset(); // No longer needed + } else { fontClientHandle = Dali::TextAbstraction::FontClient(new FontClient); } + gFontPreCacheAvailable = false; + uint32_t horizontalDpi, verticalDpi; fontClientHandle.GetDpi(horizontalDpi, verticalDpi); if(horizontalDpi == 0u || verticalDpi == 0u) @@ -104,6 +141,53 @@ Dali::TextAbstraction::FontClient FontClient::PreInitialize() return gPreInitializedFontClient; } +void FontClient::PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) +{ + if(!gPreCachedFontClient) + { + FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n"); + Dali::TextAbstraction::FontClient fontClient = Dali::TextAbstraction::FontClient(new FontClient); + GetImplementation(fontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily); + gPreCachedFontClient = fontClient; + gFontPreCacheAvailable = false; + FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n"); + } + else + { + FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\n"); + } +} + +void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread) +{ + if(!gFontPreCacheAvailable) + { + FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache has been completed or the font client has already been created.\n"); + return; + } + + FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache fallbackFamilyList : %zu\n", fallbackFamilyList.size()); + FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache extraFamilyList : %zu\n", extraFamilyList.size()); + FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache localeFamily : %s\n", localeFamily.c_str()); + FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache useThread : %d\n", useThread); + + if(gPreCacheThread.joinable()) + { + FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache thread already running.\n"); + } + else + { + if(useThread) + { + gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily); + } + else + { + PreCacheRun(fallbackFamilyList, extraFamilyList, localeFamily); + } + } +} + void FontClient::ClearCache() { if(mPlugin) @@ -155,6 +239,13 @@ void FontClient::GetDefaultFonts(FontList& defaultFonts) mPlugin->GetDefaultFonts(defaultFonts); } +void FontClient::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) +{ + CreatePlugin(); + + mPlugin->FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily); +} + void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription) { CreatePlugin(); diff --git a/dali/internal/text/text-abstraction/font-client-impl.h b/dali/internal/text/text-abstraction/font-client-impl.h index d93a053..abe8ba9 100644 --- a/dali/internal/text/text-abstraction/font-client-impl.h +++ b/dali/internal/text/text-abstraction/font-client-impl.h @@ -64,6 +64,18 @@ public: // API for Dali::TextAbstraction::FontClient used. static Dali::TextAbstraction::FontClient PreInitialize(); /** + * @brief This is used to pre-cache fonts in order to improve the runtime performance of the application. + * + * @see Dali::TextAbstraction::FontClientPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread); + */ + static void PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useUiThread); + + /** + * @brief This is used to creates a global font client and pre-caches the fonts. + */ + static void PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily); + + /** * @copydoc Dali::TextAbstraction::FontClient::ClearCache() */ void ClearCache(); @@ -293,6 +305,15 @@ public: // API for Dali::TextAbstraction::Internal::FontClient used. */ HarfBuzzFontHandle GetHarfBuzzFont(FontId fontId); + /** + * @brief This is used to pre-cache fonts in order to improve the runtime performance of the application. + * + * @param[in] fallbackFamilyList A list of fallback font families to be pre-cached. + * @param[in] extraFamilyList A list of additional font families to be pre-cached. + * @param[in] localeFamily A locale font family to be pre-cached. + */ + void FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily); + private: /** * Helper for lazy initialization. @@ -314,6 +335,7 @@ private: unsigned int mDpiVertical; static Dali::TextAbstraction::FontClient gPreInitializedFontClient; + static Dali::TextAbstraction::FontClient gPreCachedFontClient; }; // class FontClient diff --git a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp index e319b46..d690405 100644 --- a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp +++ b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp @@ -265,6 +265,60 @@ void FontClient::Plugin::ResetSystemDefaults() const mCacheHandler->ResetSystemDefaults(); } +void FontClient::Plugin::FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) const +{ + mCacheHandler->InitDefaultFontDescription(); + + FontFamilyList familyList; + familyList.reserve(extraFamilyList.size() + 1); + + for (const auto& fallbackFont : fallbackFamilyList) + { + FontList* fontList = nullptr; + CharacterSetList* characterSetList = nullptr; + FontDescriptionId fontDescriptionId = 0u; + FontDescription fontDescription; + fontDescription.family = FontFamily(fallbackFont); + fontDescription.weight = DefaultFontWeight(); + fontDescription.width = DefaultFontWidth(); + fontDescription.slant = DefaultFontSlant(); + + if(!mCacheHandler->FindFallbackFontList(fontDescription, fontList, characterSetList)) + { + mCacheHandler->CacheFallbackFontList(std::move(fontDescription), fontList, characterSetList); + } + if(!mCacheHandler->FindValidatedFont(fontDescription, fontDescriptionId)) + { + mCacheHandler->ValidateFont(fontDescription, fontDescriptionId); + } + + if(extraFamilyList.empty() && localeFamily.empty()) + { + continue; + } + + familyList.clear(); + familyList.insert(familyList.end(), extraFamilyList.begin(), extraFamilyList.end()); + if(!localeFamily.empty()) + { + familyList.push_back(localeFamily); + } + + for(const auto& font : *fontList) + { + auto it = std::find(familyList.begin(), familyList.end(), font.family); + if(it != familyList.end()) + { + if(!mCacheHandler->FindValidatedFont(font, fontDescriptionId)) + { + mCacheHandler->ValidateFont(font, fontDescriptionId); + } + familyList.erase(it); + } + } + } +} + void FontClient::Plugin::GetDefaultPlatformFontDescription(FontDescription& fontDescription) const { DALI_LOG_TRACE_METHOD(gFontClientLogFilter); diff --git a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h index 8961200..2a0c5ee 100644 --- a/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h +++ b/dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h @@ -313,6 +313,11 @@ public: // Dali::TextAbstraction::Internal::FontClient */ HarfBuzzFontHandle GetHarfBuzzFont(FontId fontId) const; + /** + * @copydoc Dali::TextAbstraction::Internal::FontClient::FontPreCache() + */ + void FontPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily) const; + private: /** * Get the cached font item for the given font -- 2.7.4