Add font thread sync creation option
[platform/core/uifw/dali-adaptor.git] / dali / internal / text / text-abstraction / font-client-impl.cpp
index ea171dc..e8fd123 100644 (file)
 #include <dali/internal/text/text-abstraction/font-client-impl.h>
 
 // EXTERNAL INCLUDES
+#include <condition_variable>
+#include <mutex>
 #include <thread>
 #if defined(VCONF_ENABLED)
 #include <vconf.h>
 #endif
 
 // INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/thread-settings.h>
 #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>
@@ -32,6 +35,7 @@
 
 #include <dali/devel-api/text-abstraction/glyph-info.h>
 
+// Use this macro only if need to log messages before the log function is set.
 #define FONT_LOG_MESSAGE(level, format, ...)                                    \
   do                                                                            \
   {                                                                             \
@@ -55,10 +59,18 @@ namespace Internal
 {
 Dali::TextAbstraction::FontClient FontClient::gPreCreatedFontClient(NULL);
 std::thread                       gPreCacheThread;
+std::thread                       gPreLoadThread;
+std::mutex                        gMutex;
+std::condition_variable           gPreCacheCond;
+std::condition_variable           gPreLoadCond;
+bool                              gPreCacheThreadReady;
+bool                              gPreLoadThreadReady;
+
 /* 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;
+bool gFontPreLoadAvailable  = true;
 
 FontClient::FontClient()
 : mPlugin(nullptr),
@@ -95,6 +107,12 @@ Dali::TextAbstraction::FontClient FontClient::Get()
         FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreCache thread join\n");
       }
 
+      if(gPreLoadThread.joinable())
+      {
+        gPreLoadThread.join();
+        FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient PreLoad thread join\n");
+      }
+
       if(gPreCreatedFontClient)
       {
         fontClientHandle = gPreCreatedFontClient;
@@ -103,13 +121,12 @@ Dali::TextAbstraction::FontClient FontClient::Get()
       else
       {
         fontClientHandle = Dali::TextAbstraction::FontClient(new FontClient);
-
-        // Make DefaultFontDescription cached
-        Dali::TextAbstraction::FontDescription defaultFontDescription;
-        fontClientHandle.GetDefaultPlatformFontDescription(defaultFontDescription);
       }
 
+      fontClientHandle.InitDefaultFontDescription();
+
       gFontPreCacheAvailable = false;
+      gFontPreLoadAvailable  = false;
 
       uint32_t horizontalDpi, verticalDpi;
       fontClientHandle.GetDpi(horizontalDpi, verticalDpi);
@@ -131,68 +148,153 @@ Dali::TextAbstraction::FontClient FontClient::PreInitialize()
 {
   // Pre-cached font client already exists or pre-cache thread already running.
   // Font client pre-cache includes caching of the default font description.
-  if((gPreCreatedFontClient && !gFontPreCacheAvailable) ||
-     (gPreCacheThread.joinable()))
+  if(gPreCreatedFontClient && !gFontPreCacheAvailable)
   {
     return gPreCreatedFontClient;
   }
 
-  gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  if(!gPreCreatedFontClient)
+  {
+    gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  }
 
-  // Make DefaultFontDescription cached
-  Dali::TextAbstraction::FontDescription defaultFontDescription;
-  gPreCreatedFontClient.GetDefaultPlatformFontDescription(defaultFontDescription);
+  gPreCreatedFontClient.InitDefaultFontDescription();
 
   return gPreCreatedFontClient;
 }
 
-void FontClient::PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily)
+void FontClient::PreCacheRun(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool syncCreation)
 {
-  if(gFontPreCacheAvailable)
+  SetThreadName("FontThread-fc");
+
+  if(syncCreation)
   {
-    gFontPreCacheAvailable = false;
-    FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n");
-    if(!gPreCreatedFontClient)
-    {
-      gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
-    }
-    GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
-    FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n");
+    std::unique_lock<std::mutex> lock(gMutex);
+    gPreCacheThreadReady = true;
+    gPreCacheCond.notify_one();
+    lock.unlock();
   }
-  else
+
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_RUN\n");
+  GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_RUN\n");
+}
+
+void FontClient::PreLoadRun(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool syncCreation)
+{
+  SetThreadName("FontThread-ft");
+
+  if(syncCreation)
   {
-    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\n");
+    std::unique_lock<std::mutex> lock(gMutex);
+    gPreLoadThreadReady = true;
+    gPreLoadCond.notify_one();
+    lock.unlock();
   }
+
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_RUN\n");
+  GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_RUN\n");
 }
 
-void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread)
+void FontClient::PreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread, bool syncCreation)
 {
   if(!gFontPreCacheAvailable)
   {
-    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache has been completed or the font client has already been created.\n");
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient pre-cache run failed, as a pre-cached font client already exists.\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");
   }
+
+  if(!gPreCreatedFontClient)
+  {
+    gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  }
+
+  gFontPreCacheAvailable = false;
+
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache fallbackFamilyList : %zu\n", fallbackFamilyList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache extraFamilyList    : %zu\n", extraFamilyList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache localeFamily       : %s\n", localeFamily.c_str());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient FontConfig PreCache useThread          : %d\n", useThread);
+
+  if(useThread)
+  {
+    if(syncCreation)
+    {
+      // The main thread wakes up upon receiving a notification from the pre-cache thread.
+      // If it doesn't receive a notification within the specified time, it wakes up due to a timeout.
+      const std::chrono::milliseconds timeout(1000);
+      gPreCacheThreadReady = false;
+      std::unique_lock<std::mutex> lock(gMutex);
+      FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_PRECACHE_THREAD_SYNC_CREATION\n");
+      gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily, syncCreation);
+      gPreCacheCond.wait_for(lock, timeout, []{return gPreCacheThreadReady;});
+      FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_PRECACHE_THREAD_SYNC_CREATION\n");
+    }
+    else
+    {
+      gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily, syncCreation);
+    }
+  }
   else
   {
-    if(useThread)
+    GetImplementation(gPreCreatedFontClient).FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
+  }
+}
+
+void FontClient::PreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread, bool syncCreation)
+{
+  if(!gFontPreLoadAvailable)
+  {
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load run failed, as a pre-loaded font client already exists.\n");
+    return;
+  }
+
+  if(gPreLoadThread.joinable())
+  {
+    FONT_LOG_MESSAGE(Dali::Integration::Log::ERROR, "FontClient font pre-load thread already running.\n");
+    return;
+  }
+
+  if(!gPreCreatedFontClient)
+  {
+    gPreCreatedFontClient = Dali::TextAbstraction::FontClient(new FontClient);
+  }
+
+  gFontPreLoadAvailable = false;
+
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad fontPathList       : %zu\n", fontPathList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad memoryFontPathList : %zu\n", memoryFontPathList.size());
+  FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "FontClient Font PreLoad useThread          : %d\n", useThread);
+
+  if(useThread)
+  {
+    if(syncCreation)
     {
-      gPreCacheThread = std::thread(PreCacheRun, fallbackFamilyList, extraFamilyList, localeFamily);
+      // The main thread wakes up upon receiving a notification from the pre-load thread.
+      // If it doesn't receive a notification within the specified time, it wakes up due to a timeout.
+      const std::chrono::milliseconds timeout(1000);
+      gPreLoadThreadReady = false;
+      std::unique_lock<std::mutex> lock(gMutex);
+      FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "BEGIN: DALI_TEXT_FONT_PRELOAD_THREAD_SYNC_CREATION\n");
+      gPreLoadThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList, syncCreation);
+      gPreLoadCond.wait_for(lock, timeout, []{return gPreLoadThreadReady;});
+      FONT_LOG_MESSAGE(Dali::Integration::Log::INFO, "END: DALI_TEXT_FONT_PRELOAD_THREAD_SYNC_CREATION\n");
     }
     else
     {
-      PreCacheRun(fallbackFamilyList, extraFamilyList, localeFamily);
+      gPreLoadThread = std::thread(PreLoadRun, fontPathList, memoryFontPathList, syncCreation);
     }
   }
+  else
+  {
+    GetImplementation(gPreCreatedFontClient).FontPreLoad(fontPathList, memoryFontPathList);
+  }
 }
 
 void FontClient::ClearCache()
@@ -253,6 +355,20 @@ void FontClient::FontPreCache(const FontFamilyList& fallbackFamilyList, const Fo
   mPlugin->FontPreCache(fallbackFamilyList, extraFamilyList, localeFamily);
 }
 
+void FontClient::FontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList)
+{
+  CreatePlugin();
+
+  mPlugin->FontPreLoad(fontPathList, memoryFontPathList);
+}
+
+void FontClient::InitDefaultFontDescription()
+{
+  CreatePlugin();
+
+  mPlugin->InitDefaultFontDescription();
+}
+
 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
 {
   CreatePlugin();
@@ -536,6 +652,7 @@ HarfBuzzFontHandle FontClient::GetHarfBuzzFont(FontId fontId)
 
 void FontClient::CreatePlugin()
 {
+  std::scoped_lock lock(gMutex);
   if(!mPlugin)
   {
     mPlugin = new Plugin(mDpiHorizontal, mDpiVertical);