Position-independent font shaper caching

Use relative cluster positioning to allow identical texts runs in
different row positions to share the same cache entry.
This commit is contained in:
Jesse Miller
2025-09-06 11:26:23 -06:00
parent e4c3a56242
commit ecf3e2ad7d
2 changed files with 12 additions and 4 deletions

View File

@@ -17,6 +17,10 @@ pub const TextRun = struct {
/// lower the chance of hash collisions if they become a problem. If /// lower the chance of hash collisions if they become a problem. If
/// there are hash collisions, it would result in rendering issues but /// there are hash collisions, it would result in rendering issues but
/// the core data would be correct. /// the core data would be correct.
///
/// The hash is position-independent within the row by using relative
/// cluster positions. This allows identical runs in different positions
/// to share the same cache entry, improving cache efficiency.
hash: u64, hash: u64,
/// The offset in the row where this run started /// The offset in the row where this run started
@@ -77,7 +81,11 @@ pub const RunIterator = struct {
// Go through cell by cell and accumulate while we build our run. // Go through cell by cell and accumulate while we build our run.
var j: usize = self.i; var j: usize = self.i;
while (j < max) : (j += 1) { while (j < max) : (j += 1) {
const cluster = j; // Use relative cluster positions (offset from run start) to make
// the shaping cache position-independent. This ensures that runs
// with identical content but different starting positions in the
// row produce the same hash, enabling cache reuse.
const cluster = j - self.i;
const cell = &cells[j]; const cell = &cells[j];
// If we have a selection and we're at a boundary point, then // If we have a selection and we're at a boundary point, then

View File

@@ -2530,7 +2530,7 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// Advance our index until we reach or pass // Advance our index until we reach or pass
// our current x position in the shaper cells. // our current x position in the shaper cells.
while (shaper_cells.?[shaper_cells_i].x < x) { while (shaper_cells.?[shaper_cells_i].x + run.offset < x) {
shaper_cells_i += 1; shaper_cells_i += 1;
} }
} }
@@ -2769,13 +2769,13 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
// If we encounter a shaper cell to the left of the current // If we encounter a shaper cell to the left of the current
// cell then we have some problems. This logic relies on x // cell then we have some problems. This logic relies on x
// position monotonically increasing. // position monotonically increasing.
assert(cells[shaper_cells_i].x >= x); assert(cells[shaper_cells_i].x + run.offset >= x);
// NOTE: An assumption is made here that a single cell will never // NOTE: An assumption is made here that a single cell will never
// be present in more than one shaper run. If that assumption is // be present in more than one shaper run. If that assumption is
// violated, this logic breaks. // violated, this logic breaks.
while (shaper_cells_i < cells.len and cells[shaper_cells_i].x == x) : ({ while (shaper_cells_i < cells.len and cells[shaper_cells_i].x + run.offset == x) : ({
shaper_cells_i += 1; shaper_cells_i += 1;
}) { }) {
self.addGlyph( self.addGlyph(