This fixes "out of realized font handles" errors seen with Valve games like Half-Life 2 (which uses more than 1000 font handles).
The new limit of 5000 is roughly inspired by the Windows limit, although there are differences. On Windows 11 I can call `CreateFont()` (and then `SetMapMode()`, `SelectObject()`, `SetTextAlign()` to realize the font) exactly 5000 times before it hits the handle limit.
Under Wine, child fonts use handles as well, so the limit is usually hit before 5000 `CreateFont()` calls. But, there is caching so that `CreateFont()` calls with identical arguments do not count towards the limit. Ultimately the old limit of 256 was sufficient for almost all applications, and 5000 should be enough for all but the worst-behaved apps.
-- v2: win32u: Make font handle table dynamically growable. win32u: Track free font handles through indices instead of pointers.
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/win32u/font.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 5776c6dccc5..efb42e40282 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -26,6 +26,7 @@
#include <limits.h> #include <stdarg.h> +#include <stdint.h> #include <stdlib.h> #include <string.h> #include <assert.h> @@ -2311,8 +2312,8 @@ struct font_handle_entry };
static struct font_handle_entry font_handles[MAX_FONT_HANDLES]; -static struct font_handle_entry *next_free; -static struct font_handle_entry *next_unused = font_handles; +static unsigned int next_free = UINT_MAX; +static unsigned int next_unused = 0;
static struct font_handle_entry *handle_entry( unsigned int handle ) { @@ -2338,13 +2339,14 @@ static struct gdi_font *get_font_from_handle( unsigned int handle )
static DWORD alloc_font_handle( struct gdi_font *font ) { - struct font_handle_entry *entry; + struct font_handle_entry *entry = NULL;
- entry = next_free; + if (next_free != UINT_MAX) + entry = &font_handles[next_free]; if (entry) - next_free = (struct font_handle_entry *)entry->font; - else if (next_unused < font_handles + MAX_FONT_HANDLES) - entry = next_unused++; + next_free = (uintptr_t)entry->font; + else if (next_unused < MAX_FONT_HANDLES) + entry = &font_handles[next_unused++]; else { ERR( "out of realized font handles\n" ); @@ -2361,8 +2363,8 @@ static void free_font_handle( DWORD handle )
if ((entry = handle_entry( handle ))) { - entry->font = (struct gdi_font *)next_free; - next_free = entry; + entry->font = (struct gdi_font *)(uintptr_t)next_free; + next_free = entry - font_handles; } }
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/win32u/font.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index efb42e40282..84f57ea6bbf 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -2303,7 +2303,7 @@ static struct gdi_font_face *find_matching_face( const LOGFONTW *lf, CHARSETINFO /* realized font objects */
#define FIRST_FONT_HANDLE 1 -#define MAX_FONT_HANDLES 256 +#define MAX_FONT_HANDLES 5000
struct font_handle_entry { @@ -2311,15 +2311,16 @@ struct font_handle_entry WORD generation; /* generation count for reusing handle values */ };
-static struct font_handle_entry font_handles[MAX_FONT_HANDLES]; +static struct font_handle_entry *font_handles; static unsigned int next_free = UINT_MAX; static unsigned int next_unused = 0; +static unsigned int allocated_font_handles = 256;
static struct font_handle_entry *handle_entry( unsigned int handle ) { unsigned int idx = LOWORD(handle) - FIRST_FONT_HANDLE;
- if (idx < MAX_FONT_HANDLES) + if (idx < allocated_font_handles) { if (!HIWORD( handle ) || HIWORD( handle ) == font_handles[idx].generation) return &font_handles[idx]; @@ -2345,12 +2346,26 @@ static DWORD alloc_font_handle( struct gdi_font *font ) entry = &font_handles[next_free]; if (entry) next_free = (uintptr_t)entry->font; - else if (next_unused < MAX_FONT_HANDLES) + else if (next_unused < allocated_font_handles) entry = &font_handles[next_unused++]; else { - ERR( "out of realized font handles\n" ); - return 0; + struct font_handle_entry *new_handles; + + if (allocated_font_handles == MAX_FONT_HANDLES) + { + ERR( "out of realized font handles\n" ); + return 0; + } + allocated_font_handles = min( allocated_font_handles * 2, MAX_FONT_HANDLES ); + new_handles = realloc( font_handles, allocated_font_handles * sizeof(*font_handles) ); + if (!new_handles) + { + ERR( "unable to grow font handle table\n" ); + return 0; + } + font_handles = new_handles; + entry = &font_handles[next_unused++]; } entry->font = font; if (++entry->generation == 0xffff) entry->generation = 1; @@ -6731,6 +6746,8 @@ UINT font_init(void) {'S','o','f','t','w','a','r','e','\','W','i','n','e','\','F','o','n','t','s'}; static const WCHAR cacheW[] = {'C','a','c','h','e'};
+ font_handles = malloc( allocated_font_handles * sizeof(*font_handles) ); + if (!(hkcu_key = open_hkcu())) return 0; wine_fonts_key = reg_create_key( hkcu_key, wine_fonts_keyW, sizeof(wine_fonts_keyW), 0, NULL ); if (wine_fonts_key) dpi = init_font_options();