From 9d5c0bff3602c0ac9120f8e5d36f4a60fa7f6a12 Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Mon, 2 Feb 2026 09:45:06 +0900 Subject: [PATCH] [NTGDI][FREETYPE] Introduce font look-up cache (#8641) Text rendering speed is justice! Font search is heavy operation. Adding cache to it is rational. JIRA issue: CORE-19898 - Define FONT_LOOKUP_CACHE structure. - Add s_FontLookupCacheList global variable to save cache. - Use cache in FontLink_PrepareFontInfo and TextIntRealizeFont. --- win32ss/gdi/ntgdi/freetype.c | 377 +++++++++++++++++++++++------------ 1 file changed, 250 insertions(+), 127 deletions(-) diff --git a/win32ss/gdi/ntgdi/freetype.c b/win32ss/gdi/ntgdi/freetype.c index 9b9d9cc4fdd..44252a2fec1 100644 --- a/win32ss/gdi/ntgdi/freetype.c +++ b/win32ss/gdi/ntgdi/freetype.c @@ -325,6 +325,18 @@ FontLink_CleanupCache(VOID) g_nFontLinkCacheCount = 0; } +typedef struct _FONT_LOOKUP_CACHE +{ + LIST_ENTRY ListEntry; + LOGFONTW LogFont; + PSHARED_FACE SharedFace; + FONTOBJ *pFontObj; +} FONT_LOOKUP_CACHE, *PFONT_LOOKUP_CACHE; + +static RTL_STATIC_LIST_HEAD(s_FontLookupCacheList); // The list of FONT_LOOKUP_CACHE +#define MAX_FONT_LOOKUP_CACHE 64 +static ULONG s_FontLookupCacheCount = 0; + /* The ranges of the surrogate pairs */ #define HIGH_SURROGATE_MIN 0xD800U #define HIGH_SURROGATE_MAX 0xDBFFU @@ -497,57 +509,27 @@ FindBestFontFromList(FONTOBJ **FontObj, ULONG *MatchPenalty, const LOGFONTW *LogFont, const PLIST_ENTRY Head); -static BOOL -MatchFontName(PSHARED_FACE SharedFace, PUNICODE_STRING Name1, FT_UShort NameID, FT_UShort LangID); +PSHARED_FACE IntRealizeFont(const LOGFONTW *pLogFont, _Inout_opt_ PTEXTOBJ TextObj); static BOOL FontLink_PrepareFontInfo( _Inout_ PFONTLINK pFontLink) { - FONTOBJ *pFontObj; - ULONG MatchPenalty; - UNICODE_STRING FaceName; - PPROCESSINFO Win32Process; - PFONTGDI pFontGDI; - ASSERT_FREETYPE_LOCK_HELD(); if (pFontLink->bIgnore) return FALSE; - if (pFontLink->SharedFace) return TRUE; - MatchPenalty = MAXULONG; - pFontObj = NULL; - - // Search private fonts - Win32Process = PsGetCurrentProcessWin32Process(); - FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont, - &Win32Process->PrivateFontListHead); - - // Search system fonts - FindBestFontFromList(&pFontObj, &MatchPenalty, &pFontLink->LogFont, - &g_FontListHead); - - if (!pFontObj) // Not found? - { - pFontLink->bIgnore = TRUE; - return FALSE; - } - - pFontGDI = ObjToGDI(pFontObj, FONT); - pFontLink->SharedFace = pFontGDI->SharedFace; - - // FontLink uses family name - RtlInitUnicodeString(&FaceName, pFontLink->LogFont.lfFaceName); - if (!MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, LANG_ENGLISH) && - !MatchFontName(pFontLink->SharedFace, &FaceName, TT_NAME_ID_FONT_FAMILY, gusLanguageID)) + PSHARED_FACE SharedFace = IntRealizeFont(&pFontLink->LogFont, NULL); + if (!SharedFace) // Not found? { pFontLink->bIgnore = TRUE; return FALSE; } + pFontLink->SharedFace = SharedFace; return TRUE; } @@ -675,9 +657,11 @@ SharedFaceCache_Release(PSHARED_FACE_CACHE Cache) } static void -SharedFace_Release(PSHARED_FACE Ptr) +SharedFace_Release(PSHARED_FACE Ptr, BOOL bDoLock) { - IntLockFreeType(); + if (bDoLock) + IntLockFreeType(); + ASSERT(Ptr->RefCount > 0); if (Ptr->RefCount <= 0) @@ -694,7 +678,9 @@ SharedFace_Release(PSHARED_FACE Ptr) SharedFaceCache_Release(&Ptr->UserLanguage); ExFreePoolWithTag(Ptr, TAG_FONT); } - IntUnLockFreeType(); + + if (bDoLock) + IntUnLockFreeType(); } @@ -714,7 +700,7 @@ CleanupFontEntryEx(PFONT_ENTRY FontEntry, PFONTGDI FontGDI) RtlFreeUnicodeString(&FontEntry->FaceName); EngFreeMem(FontGDI); - SharedFace_Release(SharedFace); + SharedFace_Release(SharedFace, TRUE); ExFreePoolWithTag(FontEntry, TAG_FONT); } @@ -1017,6 +1003,53 @@ IntLoadFontSubstList(PLIST_ENTRY pHead) return NT_SUCCESS(Status); } +static void +FontLookUp_Destroy(PFONT_LOOKUP_CACHE pCache) +{ + ASSERT_FREETYPE_LOCK_HELD(); + SharedFace_Release(pCache->SharedFace, FALSE); + ExFreePoolWithTag(pCache, TAG_FONT); +} + +static void +FontLookUp_Cleanup( + _In_ BOOL bDoLock, + _Inout_opt_ PSHARED_FACE SharedFace) +{ + if (bDoLock) + IntLockFreeType(); + + PLIST_ENTRY pHead = &s_FontLookupCacheList, pEntry; + PFONT_LOOKUP_CACHE pCache; + + if (SharedFace) + { + for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink) + { + pCache = CONTAINING_RECORD(pEntry, FONT_LOOKUP_CACHE, ListEntry); + if (pCache->SharedFace == SharedFace) + { + RemoveEntryList(&pCache->ListEntry); + FontLookUp_Destroy(pCache); + --s_FontLookupCacheCount; + } + } + } + else + { + while (!IsListEmpty(pHead)) + { + pEntry = RemoveHeadList(pHead); + pCache = CONTAINING_RECORD(pEntry, FONT_LOOKUP_CACHE, ListEntry); + FontLookUp_Destroy(pCache); + } + s_FontLookupCacheCount = 0; + } + + if (bDoLock) + IntUnLockFreeType(); +} + BOOL FASTCALL InitFontSupport(VOID) { @@ -1070,6 +1103,9 @@ FreeFontSupport(VOID) // Cleanup the FontLink cache FontLink_CleanupCache(); + // Cleanup FONT_LOOKUP_CACHE + FontLookUp_Cleanup(FALSE, NULL); + // Free font cache list pHead = &g_FontCacheListHead; while (!IsListEmpty(pHead)) @@ -1750,7 +1786,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont, Entry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_ENTRY), TAG_FONT); if (!Entry) { - SharedFace_Release(SharedFace); + SharedFace_Release(SharedFace, TRUE); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; /* failure */ } @@ -1759,7 +1795,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont, FontGDI = EngAllocMem(FL_ZERO_MEMORY, sizeof(FONTGDI), GDITAG_RFONT); if (!FontGDI) { - SharedFace_Release(SharedFace); + SharedFace_Release(SharedFace, TRUE); ExFreePoolWithTag(Entry, TAG_FONT); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; /* failure */ @@ -1774,7 +1810,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont, if (FontGDI->Filename == NULL) { EngFreeMem(FontGDI); - SharedFace_Release(SharedFace); + SharedFace_Release(SharedFace, TRUE); ExFreePoolWithTag(Entry, TAG_FONT); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; /* failure */ @@ -1793,7 +1829,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont, if (FontGDI->Filename) ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF); EngFreeMem(FontGDI); - SharedFace_Release(SharedFace); + SharedFace_Release(SharedFace, TRUE); ExFreePoolWithTag(Entry, TAG_FONT); EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); return 0; /* failure */ @@ -1873,7 +1909,7 @@ IntGdiLoadFontsFromMemory(PGDI_LOAD_FONT pLoadFont, if (FontGDI->Filename) ExFreePoolWithTag(FontGDI->Filename, GDITAG_PFF); EngFreeMem(FontGDI); - SharedFace_Release(SharedFace); + SharedFace_Release(SharedFace, TRUE); ExFreePoolWithTag(Entry, TAG_FONT); return 0; } @@ -2324,6 +2360,9 @@ IntGdiAddFontResourceEx( pchFile += cchFile + 1; } + /* Prepare for better LOGFONT-to-face matching */ + FontLookUp_Cleanup(TRUE, NULL); + return ret; } @@ -2436,6 +2475,7 @@ IntGdiRemoveFontResourceSingle( ASSERT(FontGDI); if (FontGDI->Filename && _wcsicmp(FontGDI->Filename, pszFileTitle) == 0) { + FontLookUp_Cleanup(TRUE, FontGDI->SharedFace); RemoveEntryList(&FontEntry->ListEntry); CleanupFontEntry(FontEntry); if (dwFlags & AFRX_WRITE_REGISTRY) @@ -6022,8 +6062,7 @@ IntFontType(PFONTGDI Font) FT_ULong tmp_size = 0; FT_Face Face = Font->SharedFace->Face; - ASSERT_FREETYPE_LOCK_NOT_HELD(); - IntLockFreeType(); + ASSERT_FREETYPE_LOCK_HELD(); if (FT_HAS_MULTIPLE_MASTERS(Face)) Font->FontObj.flFontType |= FO_MULTIPLEMASTER; @@ -6046,8 +6085,6 @@ IntFontType(PFONTGDI Font) { Font->FontObj.flFontType |= (FO_CFF|FO_POSTSCRIPT); } - - IntUnLockFreeType(); } static BOOL @@ -6099,16 +6136,172 @@ MatchFontNames(PSHARED_FACE SharedFace, LPCWSTR lfFaceName) return FALSE; } +static void +IntPopulateTextObj( + PTEXTOBJ TextObj, + PFONTGDI FontGdi, + FONTOBJ *pFontObj, + const LOGFONTW *pLogFont, + PLOGFONTW SubstitutedLogFont) +{ + ASSERT_FREETYPE_LOCK_HELD(); + + TextObj->Font = pFontObj; + TextObj->TextFace[0] = UNICODE_NULL; + + PSHARED_FACE SharedFace = FontGdi->SharedFace; + if (MatchFontNames(SharedFace, SubstitutedLogFont->lfFaceName)) + { + RtlStringCchCopyW(TextObj->TextFace, _countof(TextObj->TextFace), pLogFont->lfFaceName); + } + else + { + UNICODE_STRING Name; + RtlInitUnicodeString(&Name, NULL); + NTSTATUS Status = IntGetFontLocalizedName(&Name, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID); + if (NT_SUCCESS(Status)) + { + /* truncated copy */ + IntUnicodeStringToBuffer(TextObj->TextFace, sizeof(TextObj->TextFace), &Name); + RtlFreeUnicodeString(&Name); + } + } + + // Need hdev, when freetype is loaded need to create DEVOBJ for + // Consumer and Producer. + TextObj->Font->iUniq = 1; // Now it can be cached. + TextObj->fl |= TEXTOBJECT_INIT; +} + +static void +IntPopulateFontGdi(PFONTGDI FontGdi, const FONTOBJ *pFontObj, const LOGFONTW *pLogFont) +{ + ASSERT_FREETYPE_LOCK_HELD(); + + IntFontType(FontGdi); + FontGdi->flType = pFontObj->flFontType; + FontGdi->RequestUnderline = pLogFont->lfUnderline ? 0xFF : 0; + FontGdi->RequestStrikeOut = pLogFont->lfStrikeOut ? 0xFF : 0; + FontGdi->RequestItalic = pLogFont->lfItalic ? 0xFF : 0; + if (pLogFont->lfWeight != FW_DONTCARE) + FontGdi->RequestWeight = pLogFont->lfWeight; + else + FontGdi->RequestWeight = FW_NORMAL; +} + +static PFONT_LOOKUP_CACHE +FontLookUp_LookUp(const LOGFONTW *pLogFont) +{ + ASSERT_FREETYPE_LOCK_HELD(); + + PLIST_ENTRY Entry, pHead = &s_FontLookupCacheList; + for (Entry = pHead->Flink; Entry != pHead; Entry = Entry->Flink) + { + PFONT_LOOKUP_CACHE pEntry = CONTAINING_RECORD(Entry, FONT_LOOKUP_CACHE, ListEntry); + if (RtlEqualMemory(&pEntry->LogFont, pLogFont, sizeof(LOGFONTW))) + { + // Move to head + RemoveEntryList(&pEntry->ListEntry); + InsertHeadList(&s_FontLookupCacheList, &pEntry->ListEntry); + return pEntry; + } + } + return NULL; +} + +static void +FontLookUp_Add(const LOGFONTW *LogFont, PSHARED_FACE SharedFace, FONTOBJ *pFontObj) +{ + ASSERT_FREETYPE_LOCK_HELD(); + + if (s_FontLookupCacheCount >= MAX_FONT_LOOKUP_CACHE) // Too many cache? + { + // Remove tail one + PLIST_ENTRY OldestEntry = RemoveTailList(&s_FontLookupCacheList); + PFONT_LOOKUP_CACHE pOldCache = CONTAINING_RECORD(OldestEntry, FONT_LOOKUP_CACHE, ListEntry); + FontLookUp_Destroy(pOldCache); + s_FontLookupCacheCount--; + } + + // Add new cache + PFONT_LOOKUP_CACHE pEntry = ExAllocatePoolWithTag(PagedPool, sizeof(FONT_LOOKUP_CACHE), TAG_FONT); + if (pEntry) + { + // Populate + RtlCopyMemory(&pEntry->LogFont, LogFont, sizeof(LOGFONTW)); + pEntry->SharedFace = SharedFace; + SharedFace_AddRef(SharedFace); + pEntry->pFontObj = pFontObj; + // Add to head + InsertHeadList(&s_FontLookupCacheList, &pEntry->ListEntry); + s_FontLookupCacheCount++; + } +} + +PSHARED_FACE +IntRealizeFont(const LOGFONTW *pLogFont, _Inout_opt_ PTEXTOBJ TextObj) +{ + ASSERT_FREETYPE_LOCK_HELD(); + ASSERT(pLogFont); + + LOGFONTW LogFont = *pLogFont; + RtlZeroMemory(&LogFont.lfFaceName, sizeof(LogFont.lfFaceName)); + RtlStringCchCopyW(LogFont.lfFaceName, _countof(LogFont.lfFaceName), pLogFont->lfFaceName); + pLogFont = &LogFont; + + /* Substitute */ + LOGFONTW SubstitutedLogFont = *pLogFont; + SubstituteFontRecurse(&SubstitutedLogFont); + DPRINT("Font '%S,%u' is substituted by '%S,%u'.\n", + pLogFont->lfFaceName, pLogFont->lfCharSet, + SubstitutedLogFont.lfFaceName, SubstitutedLogFont.lfCharSet); + + FONTOBJ *pFontObj; + PFONT_LOOKUP_CACHE pLookUp = FontLookUp_LookUp(pLogFont); + if (pLookUp) + { + pFontObj = pLookUp->pFontObj; + } + else + { + ULONG MatchPenalty = 0xFFFFFFFF; + PPROCESSINFO Win32Process = PsGetCurrentProcessWin32Process(); + + /* Search private fonts */ + IntLockProcessPrivateFonts(Win32Process); + FindBestFontFromList(&pFontObj, &MatchPenalty, &SubstitutedLogFont, + &Win32Process->PrivateFontListHead); + IntUnLockProcessPrivateFonts(Win32Process); + + /* Search system fonts */ + FindBestFontFromList(&pFontObj, &MatchPenalty, &SubstitutedLogFont, &g_FontListHead); + } + + if (!pFontObj) + { + DPRINT1("Request font %S not found, no fonts loaded at all\n", pLogFont->lfFaceName); + return NULL; + } + + PFONTGDI pFontGDI = ObjToGDI(pFontObj, FONT); + + if (TextObj) + IntPopulateTextObj(TextObj, pFontGDI, pFontObj, pLogFont, &SubstitutedLogFont); + + IntPopulateFontGdi(pFontGDI, pFontObj, pLogFont); + + if (!pLookUp) + FontLookUp_Add(pLogFont, pFontGDI->SharedFace, pFontObj); + + return pFontGDI->SharedFace; +} + NTSTATUS FASTCALL TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj) { - NTSTATUS Status = STATUS_SUCCESS; PTEXTOBJ TextObj; - PPROCESSINFO Win32Process; - ULONG MatchPenalty; - LOGFONTW *pLogFont; - LOGFONTW SubstitutedLogFont; + PSHARED_FACE SharedFace; if (!pTextObj) { @@ -6129,86 +6322,16 @@ TextIntRealizeFont(HFONT FontHandle, PTEXTOBJ pTextObj) TextObj = pTextObj; } - pLogFont = &TextObj->logfont.elfEnumLogfontEx.elfLogFont; - - /* substitute */ - SubstitutedLogFont = *pLogFont; - SubstituteFontRecurse(&SubstitutedLogFont); - DPRINT("Font '%S,%u' is substituted by '%S,%u'.\n", - pLogFont->lfFaceName, pLogFont->lfCharSet, - SubstitutedLogFont.lfFaceName, SubstitutedLogFont.lfCharSet); - - MatchPenalty = 0xFFFFFFFF; - TextObj->Font = NULL; - - Win32Process = PsGetCurrentProcessWin32Process(); - - /* Search private fonts */ IntLockFreeType(); - IntLockProcessPrivateFonts(Win32Process); - FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont, - &Win32Process->PrivateFontListHead); - IntUnLockProcessPrivateFonts(Win32Process); - - /* Search system fonts */ - FindBestFontFromList(&TextObj->Font, &MatchPenalty, &SubstitutedLogFont, - &g_FontListHead); + SharedFace = IntRealizeFont(&TextObj->logfont.elfEnumLogfontEx.elfLogFont, TextObj); IntUnLockFreeType(); - if (NULL == TextObj->Font) - { - DPRINT1("Request font %S not found, no fonts loaded at all\n", - pLogFont->lfFaceName); - Status = STATUS_NOT_FOUND; - } - else - { - UNICODE_STRING Name; - PFONTGDI FontGdi = ObjToGDI(TextObj->Font, FONT); - PSHARED_FACE SharedFace = FontGdi->SharedFace; + if (!pTextObj) + TEXTOBJ_UnlockText(TextObj); - TextObj->TextFace[0] = UNICODE_NULL; - IntLockFreeType(); - if (MatchFontNames(SharedFace, SubstitutedLogFont.lfFaceName)) - { - IntUnLockFreeType(); - RtlStringCchCopyW(TextObj->TextFace, _countof(TextObj->TextFace), pLogFont->lfFaceName); - } - else - { - RtlInitUnicodeString(&Name, NULL); - Status = IntGetFontLocalizedName(&Name, SharedFace, TT_NAME_ID_FONT_FAMILY, gusLanguageID); - IntUnLockFreeType(); - if (NT_SUCCESS(Status)) - { - /* truncated copy */ - IntUnicodeStringToBuffer(TextObj->TextFace, sizeof(TextObj->TextFace), &Name); - RtlFreeUnicodeString(&Name); - } - } + ASSERT((!!SharedFace ^ (NULL == TextObj->Font)) != 0); - // Need hdev, when freetype is loaded need to create DEVOBJ for - // Consumer and Producer. - TextObj->Font->iUniq = 1; // Now it can be cached. - IntFontType(FontGdi); - FontGdi->flType = TextObj->Font->flFontType; - FontGdi->RequestUnderline = pLogFont->lfUnderline ? 0xFF : 0; - FontGdi->RequestStrikeOut = pLogFont->lfStrikeOut ? 0xFF : 0; - FontGdi->RequestItalic = pLogFont->lfItalic ? 0xFF : 0; - if (pLogFont->lfWeight != FW_DONTCARE) - FontGdi->RequestWeight = pLogFont->lfWeight; - else - FontGdi->RequestWeight = FW_NORMAL; - - TextObj->fl |= TEXTOBJECT_INIT; - Status = STATUS_SUCCESS; - } - - if (!pTextObj) TEXTOBJ_UnlockText(TextObj); - - ASSERT((NT_SUCCESS(Status) ^ (NULL == TextObj->Font)) != 0); - - return Status; + return SharedFace ? STATUS_SUCCESS : STATUS_NOT_FOUND; } static