[Tizen] Add FontClientPreCache API to FontClient 42/290542/1 accepted/tizen/7.0/unified/20230330.014304
authorBowon Ryu <bowon.ryu@samsung.com>
Fri, 24 Mar 2023 02:30:02 +0000 (11:30 +0900)
committerBowon Ryu <bowon.ryu@samsung.com>
Tue, 28 Mar 2023 09:24:13 +0000 (18:24 +0900)
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 <bowon.ryu@samsung.com>
dali/devel-api/text-abstraction/font-client.cpp
dali/devel-api/text-abstraction/font-client.h
dali/devel-api/text-abstraction/font-list.h
dali/internal/text/text-abstraction/font-client-impl.cpp
dali/internal/text/text-abstraction/font-client-impl.h
dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.cpp
dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h

index 27c9160..63a9e80 100644 (file)
@@ -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
index 2f75e8c..0ea768e 100644 (file)
@@ -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
index 13e459d..9cb44cc 100644 (file)
@@ -33,6 +33,7 @@ namespace TextAbstraction
 typedef std::string FontPath;
 typedef std::string FontFamily;
 typedef std::string FontStyle;
+typedef std::vector<FontFamily> FontFamilyList;
 
 namespace FontWidth
 {
index fad420c..af1257a 100644 (file)
 #include <dali/internal/text/text-abstraction/font-client-impl.h>
 
 // EXTERNAL INCLUDES
+#include <thread>
 #if defined(VCONF_ENABLED)
 #include <vconf.h>
 #endif
 
 // INTERNAL INCLUDES
 #include <dali/devel-api/common/singleton-service.h>
+#include <dali/internal/system/common/logging.h>
 #include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
 #include <dali/internal/window-system/common/window-system.h>
 
 #include <dali/devel-api/text-abstraction/glyph-info.h>
 
+#define FONT_LOG_MESSAGE(level, format, ...)                                   \
+  do                                                                           \
+  {                                                                            \
+    char buffer[256];                                                          \
+    int result = std::snprintf(buffer, sizeof(buffer), format, ##__VA_ARGS__); \
+    if (result >= static_cast<int>(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();
index d93a053..abe8ba9 100644 (file)
@@ -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
 
index e319b46..d690405 100644 (file)
@@ -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);
index 8961200..2a0c5ee 100644 (file)
@@ -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