mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
font: faster glyph hashing (#7677)
There are two main improvements being made here. First, we move away from using autohash and instead use a one-shot strategy similar to the Style hashing. Since the GlyphKey includes the Metrics struct, which contains quite a few fields, autohash was performing expensive and unnecessary repeated updates. The second improvement is actually just, not hashing Metrics. By ignoring the Metrics field, we can fit the rest of the GlyphKey into a 64-bit packed struct and just return that as the hash! It ends up being unique for each GlyphKey in renderGlyph, and is nearly a zero-cost operation. This ends up boosting the performance (on my machine at least), from around 560fps to 590fps on the DOOM-fire benchmark.
This commit is contained in:
@ -40,7 +40,7 @@ const log = std.log.scoped(.font_shared_grid);
|
||||
codepoints: std.AutoHashMapUnmanaged(CodepointKey, ?Collection.Index) = .{},
|
||||
|
||||
/// Cache for glyph renders into the atlas.
|
||||
glyphs: std.AutoHashMapUnmanaged(GlyphKey, Render) = .{},
|
||||
glyphs: std.HashMapUnmanaged(GlyphKey, Render, GlyphKey.Context, 80) = .{},
|
||||
|
||||
/// The texture atlas to store renders in. The Glyph data in the glyphs
|
||||
/// cache is dependent on the atlas matching.
|
||||
@ -307,6 +307,39 @@ const GlyphKey = struct {
|
||||
index: Collection.Index,
|
||||
glyph: u32,
|
||||
opts: RenderOptions,
|
||||
|
||||
const Context = struct {
|
||||
pub fn hash(_: Context, key: GlyphKey) u64 {
|
||||
return @bitCast(Packed.from(key));
|
||||
}
|
||||
|
||||
pub fn eql(_: Context, a: GlyphKey, b: GlyphKey) bool {
|
||||
return Packed.from(a) == Packed.from(b);
|
||||
}
|
||||
};
|
||||
|
||||
const Packed = packed struct(u64) {
|
||||
index: Collection.Index,
|
||||
glyph: u32,
|
||||
opts: packed struct(u16) {
|
||||
cell_width: u2,
|
||||
thicken: bool,
|
||||
thicken_strength: u8,
|
||||
_padding: u5 = 0,
|
||||
},
|
||||
|
||||
inline fn from(key: GlyphKey) Packed {
|
||||
return .{
|
||||
.index = key.index,
|
||||
.glyph = key.glyph,
|
||||
.opts = .{
|
||||
.cell_width = key.opts.cell_width orelse 0,
|
||||
.thicken = key.opts.thicken,
|
||||
.thicken_strength = key.opts.thicken_strength,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const TestMode = enum { normal };
|
||||
|
@ -8,9 +8,6 @@ const Offset = size.Offset;
|
||||
const OffsetBuf = size.OffsetBuf;
|
||||
const RefCountedSet = @import("ref_counted_set.zig").RefCountedSet;
|
||||
|
||||
const XxHash3 = std.hash.XxHash3;
|
||||
const autoHash = std.hash.autoHash;
|
||||
|
||||
/// The unique identifier for a style. This is at most the number of cells
|
||||
/// that can fit into a terminal page.
|
||||
pub const Id = size.CellCountInt;
|
||||
@ -313,12 +310,15 @@ pub const Style = struct {
|
||||
|
||||
pub fn hash(self: *const Style) u64 {
|
||||
const packed_style = PackedStyle.fromStyle(self.*);
|
||||
return XxHash3.hash(0, std.mem.asBytes(&packed_style));
|
||||
return std.hash.XxHash3.hash(0, std.mem.asBytes(&packed_style));
|
||||
}
|
||||
|
||||
comptime {
|
||||
assert(@sizeOf(PackedStyle) == 16);
|
||||
assert(std.meta.hasUniqueRepresentation(PackedStyle));
|
||||
for (@typeInfo(PackedStyle.Data).@"union".fields) |field| {
|
||||
assert(@bitSizeOf(field.type) == @bitSizeOf(PackedStyle.Data));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user