Bug 602792. Initialize dwrite factory at app startup to avoid startup slowdown. r=bas, a=blocker
authorJohn Daggett <jdaggett@mozilla.com>
Sat, 22 Jan 2011 01:44:33 +0900
changeset 61085 5c836d01b70a82e75e33fa538b7bc1cdb5fe8acb
parent 61084 3428fc1009fe85194867d91679a90116b1ad6ff9
child 61086 2a14b48cc2877c945798f99b4c13719e7c944f91
push idunknown
push userunknown
push dateunknown
reviewersbas, blocker
bugs602792
milestone2.0b10pre
Bug 602792. Initialize dwrite factory at app startup to avoid startup slowdown. r=bas, a=blocker
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
toolkit/xre/nsAppRunner.cpp
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -432,16 +432,17 @@ gfxDWriteFontEntry::InitLogFont(IDWriteF
     hr = gdi->ConvertFontToLOGFONT(aFont, aLogFont, &isInSystemCollection);
     return (FAILED(hr) ? PR_FALSE : PR_TRUE);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // gfxDWriteFontList
 
 gfxDWriteFontList::gfxDWriteFontList()
+    : mInitialized(PR_FALSE)
 {
     mFontSubstitutes.Init();
 }
 
 // bug 602792 - CJK systems default to large CJK fonts which cause excessive
 //   I/O strain during cold startup due to dwrite caching bugs.  Default to
 //   Arial to avoid this.
 
@@ -575,23 +576,47 @@ gfxDWriteFontList::MakePlatformFont(cons
         // We don't know how to deal with 0 faces either.
         delete entry;
         return nsnull;
     }
 
     return entry;
 }
 
+#ifdef DEBUG_DWRITE_STARTUP
+
+#define LOGREGISTRY(msg) LogRegistryEvent(msg)
+
+// for use when monitoring process
+static void LogRegistryEvent(const wchar_t *msg)
+{
+    HKEY dummyKey;
+    HRESULT hr;
+    wchar_t buf[512];
+
+    wsprintfW(buf, L" log %s", msg);
+    hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey);
+    if (SUCCEEDED(hr)) {
+        RegCloseKey(dummyKey);
+    }
+}
+#else
+
+#define LOGREGISTRY(msg)
+
+#endif
+
 nsresult
 gfxDWriteFontList::InitFontList()
 {
+    LOGREGISTRY(L"InitFontList start");
 
 #ifdef PR_LOGGING
     LARGE_INTEGER frequency;        // ticks per second
-    LARGE_INTEGER t1, t2, t3, t4, t5, t6;           // ticks
+    LARGE_INTEGER t1, t2, t3;           // ticks
     double elapsedTime, upTime;
     char nowTime[256], nowDate[256];
 
     if (LOG_FONTINIT_ENABLED()) {    
         GetTimeFormat(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, 
                       NULL, NULL, nowTime, 256);
         GetDateFormat(LOCALE_INVARIANT, NULL, NULL, NULL, nowDate, 256);
         upTime = (double) GetTickCount();
@@ -622,40 +647,86 @@ gfxDWriteFontList::InitFontList()
     mNonExistingFonts.Clear();
 
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
         QueryPerformanceCounter(&t2);
     }
 #endif
 
+    hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
+        GetGdiInterop(getter_AddRefs(mGDIInterop));
+    if (FAILED(hr)) {
+        return NS_ERROR_FAILURE;
+    }
+
+    LOGREGISTRY(L"InitFontList end");
+
+#ifdef PR_LOGGING
+    if (LOG_FONTINIT_ENABLED()) {
+        QueryPerformanceCounter(&t3);
+
+        // determine dwrite version
+        nsAutoString dwriteVers;
+        gfxWindowsPlatform::GetPlatform()->GetDLLVersion(L"dwrite.dll",
+                                                         dwriteVers);
+        LOG_FONTINIT(("InitFontList\n"));
+        LOG_FONTINIT(("Start: %s %s\n", nowDate, nowTime));
+        LOG_FONTINIT(("Uptime: %9.3f s\n", upTime/1000));
+        LOG_FONTINIT(("dwrite version: %s\n", 
+                      NS_ConvertUTF16toUTF8(dwriteVers).get()));
+        elapsedTime = (t3.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
+        LOG_FONTINIT(("Total time in InitFontList:    %9.3f ms\n", elapsedTime));
+        elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
+        LOG_FONTINIT((" --- gfxPlatformFontList init: %9.3f ms\n", elapsedTime));
+        elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
+        LOG_FONTINIT((" --- GdiInterop object:        %9.3f ms\n", elapsedTime));
+    }
+#endif
+
+    return NS_OK;
+}
+
+nsresult
+gfxDWriteFontList::DelayedInitFontList()
+{
+    LOGREGISTRY(L"DelayedInitFontList start");
+
+#ifdef PR_LOGGING
+    LARGE_INTEGER frequency;        // ticks per second
+    LARGE_INTEGER t1, t2, t3;           // ticks
+    double elapsedTime, upTime;
+    char nowTime[256], nowDate[256];
+
+    if (LOG_FONTINIT_ENABLED()) {    
+        GetTimeFormat(LOCALE_INVARIANT, TIME_FORCE24HOURFORMAT, 
+                      NULL, NULL, nowTime, 256);
+        GetDateFormat(LOCALE_INVARIANT, NULL, NULL, NULL, nowDate, 256);
+        upTime = (double) GetTickCount();
+        QueryPerformanceFrequency(&frequency);
+        QueryPerformanceCounter(&t1);
+    }
+#endif
+
+    HRESULT hr;
+
+    LOGREGISTRY(L"calling GetSystemFontCollection");
     nsRefPtr<IDWriteFontCollection> systemFonts;
     hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
         GetSystemFontCollection(getter_AddRefs(systemFonts));
     NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
+    LOGREGISTRY(L"GetSystemFontCollection done");
 
     if (FAILED(hr)) {
         return NS_ERROR_FAILURE;
     }
 
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
-        QueryPerformanceCounter(&t3);
-    }
-#endif
-
-    hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
-        GetGdiInterop(getter_AddRefs(mGDIInterop));
-    if (FAILED(hr)) {
-        return NS_ERROR_FAILURE;
-    }
-
-#ifdef PR_LOGGING
-    if (LOG_FONTINIT_ENABLED()) {
-        QueryPerformanceCounter(&t4);
+        QueryPerformanceCounter(&t2);
     }
 #endif
 
     for (UINT32 i = 0; i < systemFonts->GetFontFamilyCount(); i++) {
         nsRefPtr<IDWriteFontFamily> family;
         systemFonts->GetFontFamily(i, getter_AddRefs(family));
 
         nsRefPtr<IDWriteLocalizedStrings> names;
@@ -755,41 +826,39 @@ gfxDWriteFontList::InitFontList()
         fam->SetOtherFamilyNamesInitialized();
     }
 
     mOtherFamilyNamesInitialized = PR_TRUE;
     GetFontSubstitutes();
 
     StartLoader(kDelayBeforeLoadingFonts, kIntervalBetweenLoadingFonts);
 
+    LOGREGISTRY(L"DelayedInitFontList end");
+
 #ifdef PR_LOGGING
     if (LOG_FONTINIT_ENABLED()) {
-        QueryPerformanceCounter(&t5);
+        QueryPerformanceCounter(&t3);
 
         // determine dwrite version
         nsAutoString dwriteVers;
         gfxWindowsPlatform::GetPlatform()->GetDLLVersion(L"dwrite.dll",
                                                          dwriteVers);
-        LOG_FONTINIT(("InitFontList\n"));
+        LOG_FONTINIT(("DelayedInitFontList\n"));
         LOG_FONTINIT(("Start: %s %s\n", nowDate, nowTime));
         LOG_FONTINIT(("Uptime: %9.3f s\n", upTime/1000));
         LOG_FONTINIT(("dwrite version: %s\n", 
                       NS_ConvertUTF16toUTF8(dwriteVers).get()));
-        elapsedTime = (t5.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
+        elapsedTime = (t3.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
         LOG_FONTINIT((
-          "Total time in InitFontList:    %9.3f ms (families: %d, %s)\n",
+          "Total time in DelayedInitFontList:    %9.3f ms (families: %d, %s)\n",
           elapsedTime, systemFonts->GetFontFamilyCount(),
           (mGDIFontTableAccess ? "gdi table access" : "dwrite table access")));
         elapsedTime = (t2.QuadPart - t1.QuadPart) * 1000.0 / frequency.QuadPart;
-        LOG_FONTINIT((" --- gfxPlatformFontList init: %9.3f ms\n", elapsedTime));
+        LOG_FONTINIT((" --- GetSystemFontCollection:  %9.3f ms\n", elapsedTime));
         elapsedTime = (t3.QuadPart - t2.QuadPart) * 1000.0 / frequency.QuadPart;
-        LOG_FONTINIT((" --- GetSystemFontCollection:  %9.3f ms\n", elapsedTime));
-        elapsedTime = (t4.QuadPart - t3.QuadPart) * 1000.0 / frequency.QuadPart;
-        LOG_FONTINIT((" --- GdiInterop object:        %9.3f ms\n", elapsedTime));
-        elapsedTime = (t5.QuadPart - t4.QuadPart) * 1000.0 / frequency.QuadPart;
         LOG_FONTINIT((" --- iterate over families:    %9.3f ms\n", elapsedTime));
     }
 #endif
 
     return NS_OK;
 }
 
 static void
@@ -859,20 +928,35 @@ gfxDWriteFontList::GetStandardFamilyName
     if (family) {
         family->LocalizedName(aFamilyName);
         return PR_TRUE;
     }
 
     return PR_FALSE;
 }
 
+gfxFontFamily* gfxDWriteFontList::FindFamily(const nsAString& aFamily)
+{
+    if (!mInitialized) {
+        mInitialized = PR_TRUE;
+        DelayedInitFontList();
+    }
+
+    return gfxPlatformFontList::FindFamily(aFamily);
+}
+
 PRBool 
 gfxDWriteFontList::ResolveFontName(const nsAString& aFontName,
                                    nsAString& aResolvedFontName)
 {
+    if (!mInitialized) {
+        mInitialized = PR_TRUE;
+        DelayedInitFontList();
+    }
+
     nsAutoString keyName(aFontName);
     BuildKeyNameFromFontName(keyName);
 
     nsRefPtr<gfxFontFamily> ff;
     if (mFontSubstitutes.Get(keyName, &ff)) {
         aResolvedFontName = ff->Name();
         return PR_TRUE;
     }
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -210,16 +210,18 @@ public:
                                    nsAString& aResolvedFontName);
 
     PRBool GetStandardFamilyName(const nsAString& aFontName,
                                  nsAString& aFamilyName);
 
     IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
     PRBool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
 
+    virtual gfxFontFamily* FindFamily(const nsAString& aFamily);
+
 private:
     friend class gfxDWriteFontFamily;
 
     nsresult GetFontSubstitutes();
 
     /**
      * Fonts listed in the registry as substitutes but for which no actual
      * font family is found.
@@ -229,15 +231,18 @@ private:
     typedef nsDataHashtable<nsStringHashKey, nsRefPtr<gfxFontFamily> > FontTable;
 
     /**
      * Table of font substitutes, we grab this from the registry to get
      * alternative font names.
      */
     FontTable mFontSubstitutes;
 
+    PRBool mInitialized;
+    virtual nsresult DelayedInitFontList();
+
     // whether to use GDI font table access routines
     PRBool mGDIFontTableAccess;
     nsRefPtr<IDWriteGdiInterop> mGDIInterop;
 };
 
 
 #endif /* GFX_DWRITEFONTLIST_H */
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -432,16 +432,34 @@ gfxPlatformFontList::FindFontForCharProc
 {
     FontSearch *data = static_cast<FontSearch*>(userArg);
 
     // evaluate all fonts in this family for a match
     aFamilyEntry->FindFontForChar(data);
     return PL_DHASH_NEXT;
 }
 
+#ifdef XP_WIN
+#include <windows.h>
+
+// crude hack for using when monitoring process
+static void LogRegistryEvent(const wchar_t *msg)
+{
+  HKEY dummyKey;
+  HRESULT hr;
+  wchar_t buf[512];
+
+  wsprintfW(buf, L" log %s", msg);
+  hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey);
+  if (SUCCEEDED(hr)) {
+    RegCloseKey(dummyKey);
+  }
+}
+#endif
+
 gfxFontFamily* 
 gfxPlatformFontList::FindFamily(const nsAString& aFamily)
 {
     nsAutoString key;
     gfxFontFamily *familyEntry;
     PRBool found;
     GenerateFontListKey(aFamily, key);
 
--- a/gfx/thebes/gfxPlatformFontList.h
+++ b/gfx/thebes/gfxPlatformFontList.h
@@ -93,17 +93,17 @@ public:
 
     void ClearPrefFonts() { mPrefFonts.Clear(); }
 
     void GetFontFamilyList(nsTArray<nsRefPtr<gfxFontFamily> >& aFamilyArray);
 
     gfxFontEntry* FindFontForChar(const PRUint32 aCh, gfxFont *aPrevFont);
 
     // TODO: make this virtual, for lazily adding to the font list
-    gfxFontFamily* FindFamily(const nsAString& aFamily);
+    virtual gfxFontFamily* FindFamily(const nsAString& aFamily);
 
     gfxFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle, PRBool& aNeedsBold);
 
     PRBool GetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> > *array);
     void SetPrefFontFamilyEntries(eFontPrefLang aLangGroup, nsTArray<nsRefPtr<gfxFontFamily> >& array);
 
     // name lookup table methods
 
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -2717,16 +2717,70 @@ NS_VISIBILITY_DEFAULT PRBool nspr_use_zo
 #ifdef MOZ_SPLASHSCREEN
 #define MOZ_SPLASHSCREEN_UPDATE(_i)  do { if (splashScreen) splashScreen->Update(_i); } while(0)
 #else
 #define MOZ_SPLASHSCREEN_UPDATE(_i)  do { } while(0)
 #endif
 
 #ifdef XP_WIN
 typedef BOOL (WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags);
+
+#include <dwrite.h>
+
+typedef HRESULT (WINAPI*DWriteCreateFactoryFunc)(
+  __in   DWRITE_FACTORY_TYPE factoryType,
+  __in   REFIID iid,
+  __out  IUnknown **factory
+);
+
+#ifdef DEBUG_DWRITE_STARTUP
+
+#define LOGREGISTRY(msg) LogRegistryEvent(msg)
+
+// for use when monitoring process
+static void LogRegistryEvent(const wchar_t *msg)
+{
+  HKEY dummyKey;
+  HRESULT hr;
+  wchar_t buf[512];
+
+  wsprintf(buf, L" log %s", msg);
+  hr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey);
+  if (SUCCEEDED(hr)) {
+    RegCloseKey(dummyKey);
+  }
+}
+#else
+
+#define LOGREGISTRY(msg)
+
+#endif
+
+static DWORD InitDwriteBG(LPVOID lpdwThreadParam)
+{
+  SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
+  LOGREGISTRY(L"loading dwrite.dll");
+  HMODULE dwdll = LoadLibraryW(L"dwrite.dll");
+  DWriteCreateFactoryFunc createDWriteFactory = (DWriteCreateFactoryFunc)
+    GetProcAddress(dwdll, "DWriteCreateFactory");
+  if (createDWriteFactory) {
+    LOGREGISTRY(L"creating dwrite factory");
+    IDWriteFactory *factory;
+    HRESULT hr = createDWriteFactory(
+      DWRITE_FACTORY_TYPE_SHARED,
+      __uuidof(IDWriteFactory),
+      reinterpret_cast<IUnknown**>(&factory));
+    
+    LOGREGISTRY(L"dwrite factory done");
+    factory->Release();
+    LOGREGISTRY(L"freed factory");
+  }
+  SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END);
+  return 0;
+}
 #endif
 
 PRTime gXRE_mainTimestamp = 0;
 
 int
 XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
 {
   NS_TIME_FUNCTION;
@@ -2743,16 +2797,36 @@ XRE_main(int argc, char* argv[], const n
 
 #ifdef DEBUG
   if (PR_GetEnv("XRE_MAIN_BREAK"))
     NS_BREAK();
 #endif
 
   SetupErrorHandling(argv[0]);
 
+#ifdef XP_WIN
+
+  // Bug 602792 - when DWriteCreateFactory is called the dwrite client dll
+  // starts the FntCache service if it isn't already running (it's set
+  // to manual startup by default in Windows 7 RTM).  Subsequent DirectWrite
+  // calls cause the IDWriteFactory object to communicate with the FntCache
+  // service with a timeout; if there's no response after the timeout, the
+  // DirectWrite client library will assume the service isn't around and do
+  // manual font file I/O on _all_ system fonts.  To avoid this, load the
+  // dwrite library and create a factory as early as possible so that the
+  // FntCache service is ready by the time it's needed.
+      
+  OSVERSIONINFO vinfo;
+  vinfo.dwOSVersionInfoSize = sizeof(vinfo);
+  if (GetVersionEx(&vinfo) && vinfo.dwMajorVersion >= 6) {
+    CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&InitDwriteBG, NULL, 0, NULL);
+  }
+
+#endif
+
 #ifdef XP_UNIX
   const char *home = PR_GetEnv("HOME");
   if (!home || !*home) {
     struct passwd *pw = getpwuid(geteuid());
     if (!pw || !pw->pw_dir) {
       Output(PR_TRUE, "Could not determine HOME directory");
       return 1;
     }