https://git.reactos.org/?p=reactos.git;a=commitdiff;h=cd992d022ff8b6151a59214ca0883d8f04bd4569
commit cd992d022ff8b6151a59214ca0883d8f04bd4569 Author: Thomas Faber <thomas.fa...@reactos.org> AuthorDate: Sat Jan 20 13:41:14 2018 +0100 Commit: Thomas Faber <thomas.fa...@reactos.org> CommitDate: Sat Jan 20 15:55:07 2018 +0100 [USP10] Re-use script caches for the same font. CORE-14192 This significantly speeds up WM_SETTEXT in multiline edit controls. --- dll/win32/usp10/usp10.c | 69 ++++++++++++++++-- dll/win32/usp10/usp10_internal.h | 3 + modules/rostests/winetests/usp10/usp10.c | 118 +++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 6 deletions(-) diff --git a/dll/win32/usp10/usp10.c b/dll/win32/usp10/usp10.c index 058fc6f368..abc40fc891 100644 --- a/dll/win32/usp10/usp10.c +++ b/dll/win32/usp10/usp10.c @@ -664,6 +664,16 @@ static const SCRIPT_PROPERTIES *script_props[] = &scriptInformation[80].props, &scriptInformation[81].props }; +static CRITICAL_SECTION cs_script_cache; +static CRITICAL_SECTION_DEBUG cs_script_cache_dbg = +{ + 0, 0, &cs_script_cache, + { &cs_script_cache_dbg.ProcessLocksList, &cs_script_cache_dbg.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": script_cache") } +}; +static CRITICAL_SECTION cs_script_cache = { &cs_script_cache_dbg, -1, 0, 0, 0, 0 }; +static struct list script_cache_list = LIST_INIT(script_cache_list); + typedef struct { ScriptCache *sc; int numGlyphs; @@ -842,12 +852,34 @@ static inline BOOL set_cache_glyph_widths(SCRIPT_CACHE *psc, WORD glyph, ABC *ab static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc) { ScriptCache *sc; - int size; + unsigned size; + LOGFONTW lf; if (!psc) return E_INVALIDARG; if (*psc) return S_OK; if (!hdc) return E_PENDING; + if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(lf), &lf)) + { + return E_INVALIDARG; + } + /* Ensure canonical result by zeroing extra space in lfFaceName */ + size = strlenW(lf.lfFaceName); + memset(lf.lfFaceName + size, 0, sizeof(lf.lfFaceName) - size * sizeof(WCHAR)); + + EnterCriticalSection(&cs_script_cache); + LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry) + { + if (!memcmp(&sc->lf, &lf, sizeof(lf))) + { + sc->refcount++; + LeaveCriticalSection(&cs_script_cache); + *psc = sc; + return S_OK; + } + } + LeaveCriticalSection(&cs_script_cache); + if (!(sc = heap_alloc_zero(sizeof(ScriptCache)))) return E_OUTOFMEMORY; if (!GetTextMetricsW(hdc, &sc->tm)) { @@ -861,18 +893,32 @@ static HRESULT init_script_cache(const HDC hdc, SCRIPT_CACHE *psc) sc->otm->otmSize = size; GetOutlineTextMetricsW(hdc, size, sc->otm); } - if (!GetObjectW(GetCurrentObject(hdc, OBJ_FONT), sizeof(LOGFONTW), &sc->lf)) - { - heap_free(sc); - return E_INVALIDARG; - } sc->sfnt = (GetFontData(hdc, MS_MAKE_TAG('h','e','a','d'), 0, NULL, 0)!=GDI_ERROR); if (!set_cache_font_properties(hdc, sc)) { heap_free(sc); return E_INVALIDARG; } + sc->lf = lf; + sc->refcount = 1; *psc = sc; + + EnterCriticalSection(&cs_script_cache); + list_add_head(&script_cache_list, &sc->entry); + LIST_FOR_EACH_ENTRY(sc, &script_cache_list, ScriptCache, entry) + { + if (sc != *psc && !memcmp(&sc->lf, &lf, sizeof(lf))) + { + /* Another thread won the race. Use their cache instead of ours */ + list_remove(&sc->entry); + sc->refcount++; + LeaveCriticalSection(&cs_script_cache); + heap_free(*psc); + *psc = sc; + return S_OK; + } + } + LeaveCriticalSection(&cs_script_cache); TRACE("<- %p\n", sc); return S_OK; } @@ -1025,6 +1071,17 @@ HRESULT WINAPI ScriptFreeCache(SCRIPT_CACHE *psc) { unsigned int i; INT n; + + EnterCriticalSection(&cs_script_cache); + if (--((ScriptCache *)*psc)->refcount > 0) + { + LeaveCriticalSection(&cs_script_cache); + *psc = NULL; + return S_OK; + } + list_remove(&((ScriptCache *)*psc)->entry); + LeaveCriticalSection(&cs_script_cache); + for (i = 0; i < GLYPH_MAX / GLYPH_BLOCK_SIZE; i++) { heap_free(((ScriptCache *)*psc)->widths[i]); diff --git a/dll/win32/usp10/usp10_internal.h b/dll/win32/usp10/usp10_internal.h index da5f3fe8eb..c0e28d542c 100644 --- a/dll/win32/usp10/usp10_internal.h +++ b/dll/win32/usp10/usp10_internal.h @@ -37,6 +37,7 @@ #include <wine/debug.h> #include <wine/unicode.h> +#include <wine/list.h> #define MS_MAKE_TAG( _x1, _x2, _x3, _x4 ) \ ( ( (ULONG)_x4 << 24 ) | \ @@ -194,6 +195,8 @@ typedef struct { } CacheGlyphPage; typedef struct { + struct list entry; + DWORD refcount; LOGFONTW lf; TEXTMETRICW tm; OUTLINETEXTMETRICW *otm; diff --git a/modules/rostests/winetests/usp10/usp10.c b/modules/rostests/winetests/usp10/usp10.c index 417d0b8888..1ea873786a 100644 --- a/modules/rostests/winetests/usp10/usp10.c +++ b/modules/rostests/winetests/usp10/usp10.c @@ -1836,6 +1836,7 @@ static void test_ScriptShape(HDC hdc) static const WCHAR test3[] = {0x30b7}; HRESULT hr; SCRIPT_CACHE sc = NULL; + SCRIPT_CACHE sc2 = NULL; WORD glyphs[4], glyphs2[4], logclust[4], glyphs3[4]; SCRIPT_VISATTR attrs[4]; SCRIPT_ITEM items[4]; @@ -1865,6 +1866,10 @@ static void test_ScriptShape(HDC hdc) ok(hr == S_OK, "ScriptShape should return S_OK not %08x\n", hr); ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n"); + hr = ScriptShape(hdc, &sc2, test1, 4, 4, &items[0].a, glyphs, logclust, attrs, &nb); + ok(hr == S_OK, "ScriptShape should return S_OK not %08x\n", hr); + ok(sc2 == sc, "caches %p, %p not identical\n", sc, sc2); + ScriptFreeCache(&sc2); memset(glyphs,-1,sizeof(glyphs)); memset(logclust,-1,sizeof(logclust)); @@ -2090,6 +2095,7 @@ static void test_ScriptPlace(HDC hdc) BOOL ret; HRESULT hr; SCRIPT_CACHE sc = NULL; + SCRIPT_CACHE sc2 = NULL; WORD glyphs[4], logclust[4]; SCRIPT_VISATTR attrs[4]; SCRIPT_ITEM items[2]; @@ -2127,6 +2133,11 @@ static void test_ScriptPlace(HDC hdc) ok(hr == S_OK, "ScriptPlace should return S_OK not %08x\n", hr); ok(items[0].a.fNoGlyphIndex == FALSE, "fNoGlyphIndex TRUE\n"); + hr = ScriptPlace(hdc, &sc2, glyphs, 4, attrs, &items[0].a, widths, offset, NULL); + ok(hr == S_OK, "ScriptPlace should return S_OK not %08x\n", hr); + ok(sc2 == sc, "caches %p, %p not identical\n", sc, sc2); + ScriptFreeCache(&sc2); + if (widths[0] != 0) { int old_width = widths[0]; @@ -4112,6 +4123,112 @@ static void test_ScriptString_pSize(HDC hdc) ok(hr == S_OK, "Failed to free ssa, hr %#x.\n", hr); } +static void test_script_cache_reuse(void) +{ + HRESULT hr; + HWND hwnd1, hwnd2; + HDC hdc1, hdc2; + LOGFONTA lf; + HFONT hfont1, hfont2; + HFONT prev_hfont1, prev_hfont2; + SCRIPT_CACHE sc = NULL; + SCRIPT_CACHE sc2; + LONG height; + + hwnd1 = create_test_window(); + hwnd2 = create_test_window(); + + hdc1 = GetDC(hwnd1); + hdc2 = GetDC(hwnd2); + ok(hdc1 != NULL && hdc2 != NULL, "Failed to get window dc.\n"); + + memset(&lf, 0, sizeof(LOGFONTA)); + lstrcpyA(lf.lfFaceName, "Tahoma"); + + lf.lfHeight = 10; + hfont1 = CreateFontIndirectA(&lf); + ok(hfont1 != NULL, "CreateFontIndirectA failed\n"); + hfont2 = CreateFontIndirectA(&lf); + ok(hfont2 != NULL, "CreateFontIndirectA failed\n"); + ok(hfont1 != hfont2, "Expected fonts %p and %p to differ\n", hfont1, hfont2); + + prev_hfont1 = SelectObject(hdc1, hfont1); + ok(prev_hfont1 != NULL, "SelectObject failed: %p\n", prev_hfont1); + prev_hfont2 = SelectObject(hdc2, hfont1); + ok(prev_hfont2 != NULL, "SelectObject failed: %p\n", prev_hfont2); + + /* Get a script cache */ + hr = ScriptCacheGetHeight(hdc1, &sc, &height); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(sc != NULL, "Script cache is NULL\n"); + + /* Same font, same DC -> same SCRIPT_CACHE */ + sc2 = NULL; + hr = ScriptCacheGetHeight(hdc1, &sc2, &height); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(sc2 != NULL, "Script cache is NULL\n"); + ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2); + ScriptFreeCache(&sc2); + + /* Same font in different DC -> same SCRIPT_CACHE */ + sc2 = NULL; + hr = ScriptCacheGetHeight(hdc2, &sc2, &height); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(sc2 != NULL, "Script cache is NULL\n"); + ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2); + ScriptFreeCache(&sc2); + + /* Same font face & size, but different font handle */ + ok(SelectObject(hdc1, hfont2) != NULL, "SelectObject failed\n"); + ok(SelectObject(hdc2, hfont2) != NULL, "SelectObject failed\n"); + + sc2 = NULL; + hr = ScriptCacheGetHeight(hdc1, &sc2, &height); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(sc2 != NULL, "Script cache is NULL\n"); + ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2); + ScriptFreeCache(&sc2); + + sc2 = NULL; + hr = ScriptCacheGetHeight(hdc2, &sc2, &height); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(sc2 != NULL, "Script cache is NULL\n"); + ok(sc == sc2, "Expected caches %p, %p to be identical\n", sc, sc2); + ScriptFreeCache(&sc2); + + /* Different font size -- now we get a different SCRIPT_CACHE */ + SelectObject(hdc1, prev_hfont1); + SelectObject(hdc2, prev_hfont2); + DeleteObject(hfont2); + lf.lfHeight = 20; + hfont2 = CreateFontIndirectA(&lf); + ok(hfont2 != NULL, "CreateFontIndirectA failed\n"); + ok(SelectObject(hdc1, hfont2) != NULL, "SelectObject failed\n"); + ok(SelectObject(hdc2, hfont2) != NULL, "SelectObject failed\n"); + + sc2 = NULL; + hr = ScriptCacheGetHeight(hdc1, &sc2, &height); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(sc2 != NULL, "Script cache is NULL\n"); + ok(sc != sc2, "Expected caches %p, %p to be different\n", sc, sc2); + ScriptFreeCache(&sc2); + + sc2 = NULL; + hr = ScriptCacheGetHeight(hdc2, &sc2, &height); + ok(hr == S_OK, "expected S_OK, got 0x%08x\n", hr); + ok(sc2 != NULL, "Script cache is NULL\n"); + ok(sc != sc2, "Expected caches %p, %p to be different\n", sc, sc2); + ScriptFreeCache(&sc2); + + ScriptFreeCache(&sc); + SelectObject(hdc1, prev_hfont1); + SelectObject(hdc2, prev_hfont2); + DeleteObject(hfont1); + DeleteObject(hfont2); + DestroyWindow(hwnd1); + DestroyWindow(hwnd2); +} + static void init_tests(void) { HMODULE module = GetModuleHandleA("usp10.dll"); @@ -4185,6 +4302,7 @@ START_TEST(usp10) test_ScriptGetLogicalWidths(); test_ScriptIsComplex(); + test_script_cache_reuse(); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd);