diff --git a/src/App.zig b/src/App.zig index cf7316e8c..8f749890f 100644 --- a/src/App.zig +++ b/src/App.zig @@ -78,9 +78,36 @@ pub fn create(alloc: Allocator) CreateError!*App { var app = try alloc.create(App); errdefer alloc.destroy(app); try app.init(alloc); + + // Warm up system services on background threads. These are + // multi-millisecond one-time costs that would otherwise be paid + // during the first surface's initialization. Doing it here overlaps + // them with the rest of app startup (config, app runtime, window + // creation). + threadedWarmup(); + return app; } +/// Warm up system services whose first use in a process is slow. Each +/// subsystem gets its own background thread so that one slow subsystem +/// doesn't delay the others. Only operations that are safe to run on +/// any thread belong here. +fn threadedWarmup() void { + // If font discovery supports warmup, then we call it. Some font + // mechanisms (e.g. CoreText) have a multi-millisecond one-time cost + // on startup. + if (comptime @hasDecl(font.Discover, "warmup")) { + if (std.Thread.spawn( + .{}, + font.Discover.warmup, + .{}, + )) |thr| thr.detach() else |err| { + log.warn("font warmup thread spawn failed err={}", .{err}); + } + } +} + /// Initialize the main app instance. This creates the main window, sets /// up the renderer state, compiles the shaders, etc. This is the primary /// "startup" logic. diff --git a/src/font/discovery.zig b/src/font/discovery.zig index 1e44026ce..1847693a6 100644 --- a/src/font/discovery.zig +++ b/src/font/discovery.zig @@ -350,6 +350,22 @@ pub const CoreText = struct { _ = self; } + /// Warm up the system font registry. + /// + /// The first CoreText query in a process initializes the system font + /// database, which takes multiple milliseconds, while subsequent + /// queries are microseconds. + pub fn warmup() void { + const name = macos.foundation.String.createWithBytes( + "AppleColorEmoji", + .utf8, + false, + ) catch return; + defer name.release(); + const ct_font = macos.text.Font.createWithName(name, 12) catch return; + ct_font.release(); + } + /// Discover fonts from a descriptor. This returns an iterator that can /// be used to build up the deferred fonts. pub fn discover(self: *const CoreText, alloc: Allocator, desc: Descriptor) !DiscoverIterator {