0615931a
Extract row instance rebuild logic
a73x 2026-04-08 19:09
diff --git a/src/main.zig b/src/main.zig index 22c217c..f15c3be 100644 --- a/src/main.zig +++ b/src/main.zig @@ -149,6 +149,8 @@ fn runTerminal(alloc: std.mem.Allocator) !void { var instances: std.ArrayListUnmanaged(renderer.Instance) = .empty; defer instances.deinit(alloc); try instances.ensureTotalCapacity(alloc, @as(usize, cols) * rows); var row_cache = RowInstanceCache{}; defer row_cache.deinit(alloc); // === main loop === const wl_fd = conn.display.getFd(); @@ -253,31 +255,21 @@ fn runTerminal(alloc: std.mem.Allocator) !void { const term_rows = term.render_state.row_data.items(.cells); var row_idx: u32 = 0; while (row_idx < term_rows.len) : (row_idx += 1) { const row_cells = term_rows[row_idx]; const raw_cells = row_cells.items(.raw); var col_idx: u32 = 0; while (col_idx < raw_cells.len) : (col_idx += 1) { const cp = raw_cells[col_idx].codepoint(); const colors = term.cellColors(row_cells.get(col_idx)); const glyph_uv = if (cp == 0 or cp == ' ') null else atlas.getOrInsert(&face, @intCast(cp)) catch null; try appendCellInstances( alloc, &instances, row_idx, col_idx, cell_w, cell_h, baseline, glyph_uv, bg_uv, colors, default_bg, ); } _ = try rebuildRowInstances( alloc, &row_cache, row_idx, term_rows[row_idx], term, &face, &atlas, cell_w, cell_h, baseline, default_bg, bg_uv, ); try instances.appendSlice(alloc, row_cache.instances.items); } if (term.render_state.cursor.visible) { @@ -719,6 +711,118 @@ test "repackRowCaches keeps cursor span explicit for empty and non-empty cursor try std.testing.expectEqualDeep([2]f32{ 9.0, 10.0 }, packed_instances.items[2].cell_pos); } test "rebuildRowInstances emits expected instances for a colored glyph row" { var term = try vt.Terminal.init(std.testing.allocator, .{ .cols = 80, .rows = 24, }); defer term.deinit(); term.write("\x1b[31;44mA\x1b[0m"); try term.snapshot(); var lookup = try font.lookupMonospace(std.testing.allocator); defer lookup.deinit(std.testing.allocator); var face = try font.Face.init(std.testing.allocator, lookup.path, lookup.index, 16); defer face.deinit(); var atlas = try font.Atlas.init(std.testing.allocator, 256, 256); defer atlas.deinit(); var cache = RowInstanceCache{}; defer cache.instances.deinit(std.testing.allocator); const row_cells = term.render_state.row_data.get(0).cells; const default_bg = term.backgroundColor(); const rebuilt = try rebuildRowInstances( std.testing.allocator, &cache, 0, row_cells, term, &face, &atlas, face.cellWidth(), face.cellHeight(), face.baseline(), default_bg, atlas.cursorUV(), ); try std.testing.expect(rebuilt); try std.testing.expectEqual(@as(usize, 2), cache.instances.items.len); const colors = term.cellColors(row_cells.get(0)); try std.testing.expectEqualDeep([2]f32{ 0.0, 0.0 }, cache.instances.items[0].cell_pos); try std.testing.expectEqualDeep([2]f32{ @floatFromInt(face.cellWidth()), @floatFromInt(face.cellHeight()) }, cache.instances.items[0].glyph_size); try std.testing.expectEqualDeep(colors.bg, cache.instances.items[0].fg); try std.testing.expectEqualDeep(colors.bg, cache.instances.items[0].bg); try std.testing.expectEqualDeep([2]f32{ 0.0, 0.0 }, cache.instances.items[1].cell_pos); try std.testing.expectEqualDeep(colors.fg, cache.instances.items[1].fg); try std.testing.expectEqualDeep(colors.bg, cache.instances.items[1].bg); } test "rebuildRowInstances replaces stale cached contents without layout dirtiness when count is unchanged" { var term = try vt.Terminal.init(std.testing.allocator, .{ .cols = 80, .rows = 24, }); defer term.deinit(); term.write("\x1b[31;44mA\x1b[0m"); try term.snapshot(); var lookup = try font.lookupMonospace(std.testing.allocator); defer lookup.deinit(std.testing.allocator); var face = try font.Face.init(std.testing.allocator, lookup.path, lookup.index, 16); defer face.deinit(); var atlas = try font.Atlas.init(std.testing.allocator, 256, 256); defer atlas.deinit(); var cache = RowInstanceCache{}; defer cache.instances.deinit(std.testing.allocator); try cache.instances.append(std.testing.allocator, .{ .cell_pos = .{ 99.0, 99.0 }, .glyph_size = .{ 1.0, 1.0 }, .glyph_bearing = .{ 0.0, 0.0 }, .uv_rect = .{ 0.0, 0.0, 0.0, 0.0 }, .fg = .{ 0.0, 0.0, 0.0, 0.0 }, .bg = .{ 0.0, 0.0, 0.0, 0.0 }, }); try cache.instances.append(std.testing.allocator, .{ .cell_pos = .{ 98.0, 98.0 }, .glyph_size = .{ 1.0, 1.0 }, .glyph_bearing = .{ 0.0, 0.0 }, .uv_rect = .{ 0.0, 0.0, 0.0, 0.0 }, .fg = .{ 0.0, 0.0, 0.0, 0.0 }, .bg = .{ 0.0, 0.0, 0.0, 0.0 }, }); const row_cells = term.render_state.row_data.get(0).cells; const rebuilt = try rebuildRowInstances( std.testing.allocator, &cache, 0, row_cells, term, &face, &atlas, face.cellWidth(), face.cellHeight(), face.baseline(), term.backgroundColor(), atlas.cursorUV(), ); try std.testing.expect(!rebuilt); try std.testing.expectEqual(@as(usize, 2), cache.instances.items.len); try std.testing.expectEqualDeep([2]f32{ 0.0, 0.0 }, cache.instances.items[0].cell_pos); try std.testing.expectEqualDeep([2]f32{ 0.0, 0.0 }, cache.instances.items[1].cell_pos); } test "RenderCache resizeRows preserves surviving row caches" { var cache = RenderCache.empty; defer cache.deinit(std.testing.allocator); @@ -939,6 +1043,51 @@ fn repackRowCaches( }; } fn rebuildRowInstances( alloc: std.mem.Allocator, cache: *RowInstanceCache, row_idx: u32, row_cells: anytype, term: *const vt.Terminal, face: *font.Face, atlas: *font.Atlas, cell_w: u32, cell_h: u32, baseline: u32, default_bg: [4]f32, bg_uv: font.GlyphUV, ) !bool { const old_len = cache.instances.items.len; cache.instances.clearRetainingCapacity(); const raw_cells = row_cells.items(.raw); var col_idx: u32 = 0; while (col_idx < raw_cells.len) : (col_idx += 1) { const cp = raw_cells[col_idx].codepoint(); const colors = term.cellColors(row_cells.get(col_idx)); const glyph_uv = if (cp == 0 or cp == ' ') null else atlas.getOrInsert(face, @intCast(cp)) catch null; try appendCellInstances( alloc, &cache.instances, row_idx, col_idx, cell_w, cell_h, baseline, glyph_uv, bg_uv, colors, default_bg, ); } return markLayoutDirtyOnLenChange(old_len, cache.instances.items.len); } fn markLayoutDirtyOnLenChange(old_len: usize, new_len: usize) bool { return old_len != new_len; }