core: convert surface/app to use GroupCacheSet

This commit is contained in:
Mitchell Hashimoto
2024-04-01 15:37:09 -07:00
parent 9f34edfa83
commit 6d7053a1ad
3 changed files with 15 additions and 141 deletions

View File

@ -41,10 +41,9 @@ mailbox: Mailbox.Queue,
/// Set to true once we're quitting. This never goes false again. /// Set to true once we're quitting. This never goes false again.
quit: bool, quit: bool,
/// Font discovery mechanism. This is only safe to use from the main thread. /// The set of font GroupCache instances shared by surfaces with the
/// This is lazily initialized on the first call to fontDiscover so do /// same font configuration.
/// not access this directly. font_group_set: font.GroupCacheSet,
font_discover: ?font.Discover = null,
/// Initialize the main app instance. This creates the main window, sets /// Initialize the main app instance. This creates the main window, sets
/// up the renderer state, compiles the shaders, etc. This is the primary /// up the renderer state, compiles the shaders, etc. This is the primary
@ -55,11 +54,15 @@ pub fn create(
var app = try alloc.create(App); var app = try alloc.create(App);
errdefer alloc.destroy(app); errdefer alloc.destroy(app);
var font_group_set = try font.GroupCacheSet.init(alloc);
errdefer font_group_set.deinit();
app.* = .{ app.* = .{
.alloc = alloc, .alloc = alloc,
.surfaces = .{}, .surfaces = .{},
.mailbox = .{}, .mailbox = .{},
.quit = false, .quit = false,
.font_group_set = font_group_set,
}; };
errdefer app.surfaces.deinit(alloc); errdefer app.surfaces.deinit(alloc);
@ -71,9 +74,9 @@ pub fn destroy(self: *App) void {
for (self.surfaces.items) |surface| surface.deinit(); for (self.surfaces.items) |surface| surface.deinit();
self.surfaces.deinit(self.alloc); self.surfaces.deinit(self.alloc);
if (comptime font.Discover != void) { // Clean up our font group cache
if (self.font_discover) |*v| v.deinit(); // TODO(fontmem): assert all ref counts are zero
} self.font_group_set.deinit();
self.alloc.destroy(self); self.alloc.destroy(self);
} }
@ -166,20 +169,6 @@ pub fn needsConfirmQuit(self: *const App) bool {
return false; return false;
} }
/// Initialize once and return the font discovery mechanism. This remains
/// initialized throughout the lifetime of the application because some
/// font discovery mechanisms (i.e. fontconfig) are unsafe to reinit.
pub fn fontDiscover(self: *App) !?*font.Discover {
// If we're built without a font discovery mechanism, return null
if (comptime font.Discover == void) return null;
// If we initialized, use it
if (self.font_discover) |*v| return v;
self.font_discover = font.Discover.init();
return &self.font_discover.?;
}
/// Drain the mailbox. /// Drain the mailbox.
fn drainMailbox(self: *App, rt_app: *apprt.App) !void { fn drainMailbox(self: *App, rt_app: *apprt.App) !void {
while (self.mailbox.pop()) |message| { while (self.mailbox.pop()) |message| {

View File

@ -54,8 +54,6 @@ rt_app: *apprt.runtime.App,
rt_surface: *apprt.runtime.Surface, rt_surface: *apprt.runtime.Surface,
/// The font structures /// The font structures
font_lib: font.Library,
font_group: *font.GroupCache,
font_size: font.face.DesiredSize, font_size: font.face.DesiredSize,
/// The renderer for this surface. /// The renderer for this surface.
@ -320,117 +318,9 @@ pub fn init(
.ydpi = @intFromFloat(y_dpi), .ydpi = @intFromFloat(y_dpi),
}; };
// Create our font group key. This is used to determine if we have // Setup our font group. This will reuse an existing font group if
// a cached font group we can use already. Otherwise, this can be // it was already loaded.
// used to build the group. const font_group = try app.font_group_set.groupInit(config, font_size);
var font_group_key = try font.GroupCacheSet.Key.init(alloc, config);
defer font_group_key.deinit();
// Find all the fonts for this surface
//
// Future: we can share the font group amongst all surfaces to save
// some new surface init time and some memory. This will require making
// thread-safe changes to font structs.
var font_lib = try font.Library.init();
errdefer font_lib.deinit();
var font_group = try alloc.create(font.GroupCache);
errdefer alloc.destroy(font_group);
font_group.* = try font.GroupCache.init(alloc, group: {
var group = try font.Group.init(alloc, font_lib, font_size);
errdefer group.deinit();
group.metric_modifiers = font_group_key.metric_modifiers;
group.codepoint_map = font_group_key.codepoint_map;
// Set our styles
group.styles.set(.bold, config.@"font-style-bold" != .false);
group.styles.set(.italic, config.@"font-style-italic" != .false);
group.styles.set(.bold_italic, config.@"font-style-bold-italic" != .false);
// Search for fonts
if (font.Discover != void) discover: {
const disco = try app.fontDiscover() orelse {
log.warn("font discovery not available, cannot search for fonts", .{});
break :discover;
};
group.discover = disco;
// A buffer we use to store the font names for logging.
var name_buf: [256]u8 = undefined;
inline for (@typeInfo(font.Style).Enum.fields) |field| {
const style = @field(font.Style, field.name);
for (font_group_key.descriptorsForStyle(style)) |desc| {
var disco_it = try disco.discover(alloc, desc);
defer disco_it.deinit();
if (try disco_it.next()) |face| {
log.info("font {s}: {s}", .{
field.name,
try face.name(&name_buf),
});
_ = try group.addFace(style, .{ .deferred = face });
} else log.warn("font-family {s} not found: {s}", .{
field.name,
desc.family.?,
});
}
}
}
// Our built-in font will be used as a backup
_ = try group.addFace(
.regular,
.{ .fallback_loaded = try font.Face.init(
font_lib,
face_ttf,
group.faceOptions(),
) },
);
_ = try group.addFace(
.bold,
.{ .fallback_loaded = try font.Face.init(
font_lib,
face_bold_ttf,
group.faceOptions(),
) },
);
// Auto-italicize if we have to.
try group.italicize();
// On macOS, always search for and add the Apple Emoji font
// as our preferred emoji font for fallback. We do this in case
// people add other emoji fonts to their system, we always want to
// prefer the official one. Users can override this by explicitly
// specifying a font-family for emoji.
if (comptime builtin.target.isDarwin()) apple_emoji: {
const disco = group.discover orelse break :apple_emoji;
var disco_it = try disco.discover(alloc, .{
.family = "Apple Color Emoji",
});
defer disco_it.deinit();
if (try disco_it.next()) |face| {
_ = try group.addFace(.regular, .{ .fallback_deferred = face });
}
}
// Emoji fallback. We don't include this on Mac since Mac is expected
// to always have the Apple Emoji available on the system.
if (comptime !builtin.target.isDarwin() or font.Discover == void) {
_ = try group.addFace(
.regular,
.{ .fallback_loaded = try font.Face.init(font_lib, face_emoji_ttf, group.faceOptions()) },
);
_ = try group.addFace(
.regular,
.{ .fallback_loaded = try font.Face.init(font_lib, face_emoji_text_ttf, group.faceOptions()) },
);
}
break :group group;
});
errdefer font_group.deinit(alloc);
log.info("font loading complete, any non-logged faces are using the built-in font", .{});
// Pre-calculate our initial cell size ourselves. // Pre-calculate our initial cell size ourselves.
const cell_size = try renderer.CellSize.init(alloc, font_group); const cell_size = try renderer.CellSize.init(alloc, font_group);
@ -516,8 +406,6 @@ pub fn init(
.app = app, .app = app,
.rt_app = rt_app, .rt_app = rt_app,
.rt_surface = rt_surface, .rt_surface = rt_surface,
.font_lib = font_lib,
.font_group = font_group,
.font_size = font_size, .font_size = font_size,
.renderer = renderer_impl, .renderer = renderer_impl,
.renderer_thread = render_thread, .renderer_thread = render_thread,
@ -632,10 +520,6 @@ pub fn deinit(self: *Surface) void {
self.io_thread.deinit(); self.io_thread.deinit();
self.io.deinit(); self.io.deinit();
self.font_group.deinit(self.alloc);
self.font_lib.deinit();
self.alloc.destroy(self.font_group);
if (self.inspector) |v| { if (self.inspector) |v| {
v.deinit(); v.deinit();
self.alloc.destroy(v); self.alloc.destroy(v);

View File

@ -87,7 +87,7 @@ pub fn groupInit(
var key = try Key.init(self.alloc, config); var key = try Key.init(self.alloc, config);
errdefer key.deinit(); errdefer key.deinit();
const gop = try self.map.getOrPut(key); const gop = try self.map.getOrPut(self.alloc, key);
if (gop.found_existing) { if (gop.found_existing) {
// We can deinit the key because we found a cached value. // We can deinit the key because we found a cached value.
key.deinit(); key.deinit();
@ -205,6 +205,7 @@ pub fn groupInit(
); );
} }
log.info("font loading complete, any non-logged faces are using the built-in font", .{});
break :group group; break :group group;
}); });
errdefer cache.deinit(self.alloc); errdefer cache.deinit(self.alloc);