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.
From: Brendan Shanks bshanks@codeweavers.com
--- dlls/win32u/font.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index 7d7aa26adc7..e782426d51b 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -2311,8 +2311,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 +2338,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 +2362,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 e782426d51b..a1cd6df7a16 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -2302,7 +2302,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 { @@ -2310,15 +2310,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]; @@ -2344,12 +2345,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; @@ -6691,6 +6706,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();
If this about GetFontRealizationInfo(), I think we are managing those "handles" incorrectly. It's supposed to mean more or less a unique physical font, so similar to {font file, index, simulations} vector. Do you see different instance_id values returned for the same font file on Windows?
On Tue Apr 8 23:59:55 2025 +0000, Nikolay Sivov wrote:
If this about GetFontRealizationInfo(), I think we are managing those "handles" incorrectly. It's supposed to mean more or less a unique physical font, so similar to {font file, index, simulations} vector. Do you see different instance_id values returned for the same font file on Windows?
On Windows and Wine, I get the same `instance_id` for the same font file/weight/height.