From 013cf65a16630b5c4f28942ea9e759777814f367 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 10 Jun 2026 01:06:58 +0300 Subject: [PATCH] gg: cache text attribute dictionaries on macOS native renderer darwin_draw_string built a fresh NSFont (font lookup), NSColor and NSDictionary on every text-run draw, every frame. The distinct set of (size, style, color) combinations a UI uses is tiny, so memoize the finished attribute dict in a static NSMutableDictionary keyed by color.rgb|size|bold|mono|force_mono. Cuts per-frame Obj-C allocation churn (CPU + autorelease-pool pressure); render output is unchanged. --- vlib/gg/gg_darwin.m | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/vlib/gg/gg_darwin.m b/vlib/gg/gg_darwin.m index 8d3ef6039..5718d4435 100644 --- a/vlib/gg/gg_darwin.m +++ b/vlib/gg/gg_darwin.m @@ -87,7 +87,34 @@ void gg_macos_set_window_resizable(void *window_ptr, bool resizable) { int g_gg_force_mono = 0; void gg_set_force_mono(int on) { g_gg_force_mono = on; } -void darwin_draw_string(int x, int y, string s, gg__TextCfg cfg) { +// Cache of the (font + color) attribute dictionaries passed to -drawAtPoint:. +// darwin_draw_string is called once per text run, every frame; building a fresh +// NSFont (font lookup), NSColor, and NSDictionary on each call was pure ObjC +// churn (CPU + autorelease-pool pressure). The set of distinct (size, style, +// color) combinations a UI actually uses is tiny (a few sizes × a theme palette), +// so we memoize the finished attribute dict. Keyed on everything that changes the +// dict — color rgb (nscolor ignores alpha), size, bold, mono, and the global +// force-mono flag. The dictionary retains each cached attr dict (ARC), so they +// persist for the process; bounded by the number of distinct combinations. +static NSMutableDictionary* g_text_attr_cache = nil; + +static NSDictionary* darwin_text_attrs(gg__TextCfg cfg) { + uint64_t key = ((uint64_t)cfg.color.r) + | ((uint64_t)cfg.color.g << 8) + | ((uint64_t)cfg.color.b << 16) + | (((uint64_t)cfg.size & 0xFFFF) << 24) + | ((uint64_t)(cfg.bold ? 1 : 0) << 40) + | ((uint64_t)(cfg.mono ? 1 : 0) << 41) + | ((uint64_t)(g_gg_force_mono ? 1 : 0) << 42); + if (g_text_attr_cache == nil) { + g_text_attr_cache = [[NSMutableDictionary alloc] init]; + } + NSNumber* k = @(key); + NSDictionary* cached = [g_text_attr_cache objectForKey:k]; + if (cached != nil) { + return cached; + } + NSFont* font = [NSFont userFontOfSize:cfg.size]; // # NSFont* font = [NSFont fontWithName:@"Roboto Mono" size:cfg.size]; if (g_gg_force_mono) { @@ -107,6 +134,12 @@ void darwin_draw_string(int x, int y, string s, gg__TextCfg cfg) { // NSParagraphStyleAttributeName: paragraphStyle, NSFontAttributeName : font, }; + [g_text_attr_cache setObject:attr forKey:k]; + return attr; +} + +void darwin_draw_string(int x, int y, string s, gg__TextCfg cfg) { + NSDictionary* attr = darwin_text_attrs(cfg); [nsstring(s) drawAtPoint:NSMakePoint(x, y - 15) withAttributes:attr]; } -- 2.39.5