8975cd18
Add dirty-row refresh planning helpers
a73x 2026-04-08 18:25
Commit message
src/main.zig
| Old | New | ||
|---|---|---|---|
| @@ -367,6 +367,46 @@ fn shouldRenderFrame(terminal_dirty: bool, window_dirty: bool, forced: bool) boo | |||
| 367 | return terminal_dirty or window_dirty or forced; | 367 | return terminal_dirty or window_dirty or forced; |
| 368 | } | 368 | } |
| 369 | 369 | ||
| 370 | const RowRefreshState = enum { | ||
| 371 | full, | ||
| 372 | partial, | ||
| 373 | }; | ||
| 374 | |||
| 375 | const RowRefreshContext = struct { | ||
| 376 | cursor_changed: bool, | ||
| 377 | old_cursor_row: ?usize, | ||
| 378 | new_cursor_row: ?usize, | ||
| 379 | }; | ||
| 380 | |||
| 381 | const RowRefreshPlan = struct { | ||
| 382 | full_rebuild: bool, | ||
| 383 | cursor_rebuild: bool, | ||
| 384 | rows_to_rebuild: std.StaticBitSet(256), | ||
| 385 | }; | ||
| 386 | |||
| 387 | fn planRowRefresh( | ||
| 388 | state: RowRefreshState, | ||
| 389 | dirty_rows: []const bool, | ||
| 390 | ctx: RowRefreshContext, | ||
| 391 | ) RowRefreshPlan { | ||
| 392 | var rows_to_rebuild = std.StaticBitSet(256).initEmpty(); | ||
| 393 | |||
| 394 | const full_rebuild = state == .full; | ||
| 395 | if (!full_rebuild) { | ||
| 396 | const limit = @min(dirty_rows.len, rows_to_rebuild.capacity()); | ||
| 397 | var row_idx: usize = 0; | ||
| 398 | while (row_idx < limit) : (row_idx += 1) { | ||
| 399 | if (dirty_rows[row_idx]) rows_to_rebuild.set(row_idx); | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | return .{ | ||
| 404 | .full_rebuild = full_rebuild, | ||
| 405 | .cursor_rebuild = ctx.cursor_changed, | ||
| 406 | .rows_to_rebuild = rows_to_rebuild, | ||
| 407 | }; | ||
| 408 | } | ||
| 409 | |||
| 370 | fn appendCellInstances( | 410 | fn appendCellInstances( |
| 371 | alloc: std.mem.Allocator, | 411 | alloc: std.mem.Allocator, |
| 372 | instances: *std.ArrayListUnmanaged(renderer.Instance), | 412 | instances: *std.ArrayListUnmanaged(renderer.Instance), |
| @@ -476,6 +516,42 @@ test "event loop redraws only when terminal or window state changed" { | |||
| 476 | try std.testing.expect(!shouldRenderFrame(false, false, false)); | 516 | try std.testing.expect(!shouldRenderFrame(false, false, false)); |
| 477 | } | 517 | } |
| 478 | 518 | ||
| 519 | test "planRowRefresh requests full rebuild for full dirty state" { | ||
| 520 | const plan = planRowRefresh(.full, &.{ false, true, false }, .{ | ||
| 521 | .cursor_changed = false, | ||
| 522 | .old_cursor_row = null, | ||
| 523 | .new_cursor_row = null, | ||
| 524 | }); | ||
| 525 | |||
| 526 | try std.testing.expect(plan.full_rebuild); | ||
| 527 | try std.testing.expectEqual(@as(usize, 0), plan.rows_to_rebuild.count()); | ||
| 528 | } | ||
| 529 | |||
| 530 | test "planRowRefresh selects only dirty rows for partial state" { | ||
| 531 | const plan = planRowRefresh(.partial, &.{ false, true, false, true }, .{ | ||
| 532 | .cursor_changed = false, | ||
| 533 | .old_cursor_row = null, | ||
| 534 | .new_cursor_row = null, | ||
| 535 | }); | ||
| 536 | |||
| 537 | try std.testing.expect(!plan.full_rebuild); | ||
| 538 | try std.testing.expect(plan.rows_to_rebuild.isSet(1)); | ||
| 539 | try std.testing.expect(plan.rows_to_rebuild.isSet(3)); | ||
| 540 | try std.testing.expect(!plan.rows_to_rebuild.isSet(0)); | ||
| 541 | } | ||
| 542 | |||
| 543 | test "planRowRefresh handles cursor-only updates without unrelated rows" { | ||
| 544 | const plan = planRowRefresh(.partial, &.{ false, false, false }, .{ | ||
| 545 | .cursor_changed = true, | ||
| 546 | .old_cursor_row = 1, | ||
| 547 | .new_cursor_row = 2, | ||
| 548 | }); | ||
| 549 | |||
| 550 | try std.testing.expect(!plan.full_rebuild); | ||
| 551 | try std.testing.expect(plan.cursor_rebuild); | ||
| 552 | try std.testing.expectEqual(@as(usize, 0), plan.rows_to_rebuild.count()); | ||
| 553 | } | ||
| 554 | |||
| 479 | fn runDrawSmokeTest(alloc: std.mem.Allocator) !void { | 555 | fn runDrawSmokeTest(alloc: std.mem.Allocator) !void { |
| 480 | var conn = try wayland_client.Connection.init(); | 556 | var conn = try wayland_client.Connection.init(); |
| 481 | defer conn.deinit(); | 557 | defer conn.deinit(); |