This commit is contained in:
agra
2026-03-04 17:17:29 +02:00
parent 343ea4bf08
commit 0782353ffa
12 changed files with 1288 additions and 131 deletions

View File

@@ -143,9 +143,14 @@ GlyphCache :: struct {
cursor_x: s32;
padding: s32;
// Glyph lookup cache (flat list, linear scan)
// Glyph lookup cache
entries: List(GlyphEntry);
// Hash table for O(1) glyph lookup (open addressing, linear probing)
hash_keys: [*]u32; // key per slot (0 = empty sentinel)
hash_vals: [*]s32; // index into entries list
hash_cap: s64; // table capacity (power of 2)
// Dirty tracking for texture upload
dirty: bool;
@@ -165,6 +170,11 @@ GlyphCache :: struct {
font_data_size: s32;
shaped_buf: List(ShapedGlyph);
// Shape cache: skip reshaping if same text + size as last call
last_shape_ptr: [*]u8;
last_shape_len: s64;
last_shape_size_q: u16;
init :: (self: *GlyphCache, path: [:0]u8, default_size: f32) {
// Zero out the entire struct first (parent may be uninitialized with = ---)
memset(self, 0, size_of(GlyphCache));
@@ -226,6 +236,14 @@ GlyphCache :: struct {
self.dpi_scale = 1.0;
self.inv_dpi = 1.0;
// Init hash table (256 slots)
self.hash_cap = 256;
hash_bytes : s64 = self.hash_cap * 4; // u32 per slot
self.hash_keys = xx context.allocator.alloc(hash_bytes);
memset(self.hash_keys, 0, hash_bytes);
val_bytes : s64 = self.hash_cap * 8; // s64 per slot (s32 would suffice but alignment)
self.hash_vals = xx context.allocator.alloc(val_bytes);
// Create OpenGL texture
glGenTextures(1, @self.texture_id);
glBindTexture(GL_TEXTURE_2D, self.texture_id);
@@ -247,13 +265,14 @@ GlyphCache :: struct {
size_q := quantize_size(font_size);
key := make_glyph_key(glyph_index, size_q);
// Cache lookup (linear scan)
i : s64 = 0;
while i < self.entries.len {
if self.entries.items[i].key == key {
return @self.entries.items[i].glyph;
// Hash table lookup (open addressing, linear probing)
mask := self.hash_cap - 1;
slot : s64 = xx ((key * 2654435761) >> 24) & xx mask;
while self.hash_keys[slot] != 0 {
if self.hash_keys[slot] == key {
return @self.entries.items[self.hash_vals[slot]].glyph;
}
i += 1;
slot = (slot + 1) & mask;
}
// Cache miss — rasterize
@@ -288,6 +307,7 @@ GlyphCache :: struct {
}
};
self.entries.append(entry);
self.hash_insert(key, self.entries.len - 1);
return @self.entries.items[self.entries.len - 1].glyph;
}
@@ -330,9 +350,58 @@ GlyphCache :: struct {
}
};
self.entries.append(entry);
self.hash_insert(key, self.entries.len - 1);
return @self.entries.items[self.entries.len - 1].glyph;
}
// Insert a key→index mapping into the hash table, growing if needed
hash_insert :: (self: *GlyphCache, key: u32, index: s64) {
// Grow if load factor > 70%
if self.entries.len * 10 > self.hash_cap * 7 {
self.hash_grow();
}
mask := self.hash_cap - 1;
slot : s64 = xx ((key * 2654435761) >> 24) & xx mask;
while self.hash_keys[slot] != 0 {
slot = (slot + 1) & mask;
}
self.hash_keys[slot] = key;
self.hash_vals[slot] = xx index;
}
// Double the hash table and rehash all entries
hash_grow :: (self: *GlyphCache) {
old_cap := self.hash_cap;
old_keys := self.hash_keys;
old_vals := self.hash_vals;
self.hash_cap = old_cap * 2;
hash_bytes : s64 = self.hash_cap * 4;
self.hash_keys = xx context.allocator.alloc(hash_bytes);
memset(self.hash_keys, 0, hash_bytes);
val_bytes : s64 = self.hash_cap * 8;
self.hash_vals = xx context.allocator.alloc(val_bytes);
// Rehash
mask := self.hash_cap - 1;
i : s64 = 0;
while i < old_cap {
k := old_keys[i];
if k != 0 {
slot : s64 = xx ((k * 2654435761) >> 24) & xx mask;
while self.hash_keys[slot] != 0 {
slot = (slot + 1) & mask;
}
self.hash_keys[slot] = k;
self.hash_vals[slot] = old_vals[i];
}
i += 1;
}
context.allocator.dealloc(old_keys);
context.allocator.dealloc(old_vals);
}
// Upload dirty atlas to GPU
flush :: (self: *GlyphCache) {
if self.dirty == false { return; }
@@ -450,6 +519,12 @@ GlyphCache :: struct {
// full kb_text_shape pipeline for Unicode/complex scripts.
// Results stored in self.shaped_buf (reused across calls).
shape_text :: (self: *GlyphCache, text: string, font_size: f32) {
// Check shape cache: skip if same text + size as last call
size_q := quantize_size(font_size);
if text.len > 0 and text.ptr == self.last_shape_ptr and text.len == self.last_shape_len and size_q == self.last_shape_size_q {
return; // shaped_buf already has the result
}
self.shaped_buf.len = 0;
if text.len == 0 { return; }
@@ -458,6 +533,11 @@ GlyphCache :: struct {
} else {
self.shape_with_kb(text, font_size);
}
// Update shape cache
self.last_shape_ptr = text.ptr;
self.last_shape_len = text.len;
self.last_shape_size_q = size_q;
}
shape_ascii :: (self: *GlyphCache, text: string, font_size: f32) {