-
Notifications
You must be signed in to change notification settings - Fork 109
Avoid render output on idle frames and add benchmark #285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| const std = @import("std"); | ||
| const vaxis = @import("vaxis"); | ||
|
|
||
| fn parseIterations(allocator: std.mem.Allocator) !usize { | ||
| var args = try std.process.argsWithAllocator(allocator); | ||
| defer args.deinit(); | ||
| _ = args.next(); | ||
| if (args.next()) |val| { | ||
| return std.fmt.parseUnsigned(usize, val, 10); | ||
| } | ||
| return 200; | ||
| } | ||
|
|
||
| fn printResults(writer: anytype, label: []const u8, iterations: usize, elapsed_ns: u64, total_bytes: usize) !void { | ||
| const ns_per_frame = elapsed_ns / @as(u64, @intCast(iterations)); | ||
| const bytes_per_frame = total_bytes / iterations; | ||
| try writer.print( | ||
| "{s}: frames={d} total_ns={d} ns/frame={d} bytes={d} bytes/frame={d}\n", | ||
| .{ label, iterations, elapsed_ns, ns_per_frame, total_bytes, bytes_per_frame }, | ||
| ); | ||
| } | ||
|
|
||
| pub fn main() !void { | ||
| var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||
| defer _ = gpa.deinit(); | ||
| const allocator = gpa.allocator(); | ||
|
|
||
| const iterations = try parseIterations(allocator); | ||
|
|
||
| var vx = try vaxis.init(allocator, .{}); | ||
| var init_writer = std.io.Writer.Allocating.init(allocator); | ||
| defer init_writer.deinit(); | ||
| defer vx.deinit(allocator, &init_writer.writer); | ||
|
|
||
| const winsize = vaxis.Winsize{ .rows = 24, .cols = 80, .x_pixel = 0, .y_pixel = 0 }; | ||
| try vx.resize(allocator, &init_writer.writer, winsize); | ||
|
|
||
| const stdout = std.fs.File.stdout().deprecatedWriter(); | ||
|
|
||
| var idle_writer = std.io.Writer.Allocating.init(allocator); | ||
| defer idle_writer.deinit(); | ||
| var timer = try std.time.Timer.start(); | ||
| var i: usize = 0; | ||
| while (i < iterations) : (i += 1) { | ||
| try vx.render(&idle_writer.writer); | ||
| } | ||
| const idle_ns = timer.read(); | ||
| const idle_bytes: usize = idle_writer.writer.end; | ||
| try printResults(stdout, "idle", iterations, idle_ns, idle_bytes); | ||
|
|
||
| var dirty_writer = std.io.Writer.Allocating.init(allocator); | ||
| defer dirty_writer.deinit(); | ||
|
Comment on lines
+31
to
+52
|
||
| timer.reset(); | ||
| i = 0; | ||
| while (i < iterations) : (i += 1) { | ||
| vx.queueRefresh(); | ||
| try vx.render(&dirty_writer.writer); | ||
| } | ||
| const dirty_ns = timer.read(); | ||
| const dirty_bytes: usize = dirty_writer.writer.end; | ||
|
Comment on lines
+48
to
+60
|
||
| try printResults(stdout, "dirty", iterations, dirty_ns, dirty_bytes); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -360,48 +360,72 @@ pub fn render(self: *Vaxis, tty: *IoWriter) !void { | |||||||||
| assert(self.screen.buf.len == @as(usize, @intCast(self.screen.width)) * self.screen.height); // correct size | ||||||||||
| assert(self.screen.buf.len == self.screen_last.buf.len); // same size | ||||||||||
|
|
||||||||||
| // Set up sync before we write anything | ||||||||||
| // TODO: optimize sync so we only sync _when we have changes_. This | ||||||||||
| // requires a smarter buffered writer, we'll probably have to write | ||||||||||
| // our own | ||||||||||
| try tty.writeAll(ctlseqs.sync_set); | ||||||||||
| errdefer tty.writeAll(ctlseqs.sync_reset) catch {}; | ||||||||||
|
|
||||||||||
| // Send the cursor to 0,0 | ||||||||||
| // TODO: this needs to move after we optimize writes. We only do | ||||||||||
| // this if we have an update to make. We also need to hide cursor | ||||||||||
| // and then reshow it if needed | ||||||||||
| try tty.writeAll(ctlseqs.hide_cursor); | ||||||||||
| if (self.state.alt_screen) | ||||||||||
| try tty.writeAll(ctlseqs.home) | ||||||||||
| else { | ||||||||||
| try tty.writeByte('\r'); | ||||||||||
| for (0..self.state.cursor.row) |_| { | ||||||||||
| try tty.writeAll(ctlseqs.ri); | ||||||||||
| } | ||||||||||
| } | ||||||||||
| try tty.writeAll(ctlseqs.sgr_reset); | ||||||||||
| var started: bool = false; | ||||||||||
| var sync_active: bool = false; | ||||||||||
| errdefer if (sync_active) tty.writeAll(ctlseqs.sync_reset) catch {}; | ||||||||||
|
|
||||||||||
| const cursor_vis_changed = self.screen.cursor_vis != self.screen_last.cursor_vis; | ||||||||||
| const cursor_shape_changed = self.screen.cursor_shape != self.screen_last.cursor_shape; | ||||||||||
| const mouse_shape_changed = self.screen.mouse_shape != self.screen_last.mouse_shape; | ||||||||||
| const cursor_pos_changed = self.screen.cursor_vis and | ||||||||||
| (self.screen.cursor_row != self.state.cursor.row or | ||||||||||
| self.screen.cursor_col != self.state.cursor.col); | ||||||||||
|
Comment on lines
+371
to
+372
|
||||||||||
| (self.screen.cursor_row != self.state.cursor.row or | |
| self.screen.cursor_col != self.state.cursor.col); | |
| (self.screen.cursor_row != self.screen_last.cursor_row or | |
| self.screen.cursor_col != self.screen_last.cursor_col); |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After updating screen_last.cursor_vis at line 792, the code should also update screen_last.cursor_row and screen_last.cursor_col to track the last rendered cursor position. This is necessary for the cursor position change detection at lines 370-372 to work correctly. Add:
self.screen_last.cursor_row = self.screen.cursor_row;
self.screen_last.cursor_col = self.screen.cursor_col;
| self.screen_last.cursor_vis = self.screen.cursor_vis; | |
| self.screen_last.cursor_vis = self.screen.cursor_vis; | |
| self.screen_last.cursor_row = self.screen.cursor_row; | |
| self.screen_last.cursor_col = self.screen.cursor_col; |
Copilot
AI
Dec 28, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test uses std.io.Writer.Allocating (lowercase 'io'), but the existing code in tty.zig uses std.Io.Writer.Allocating (capital 'I'). This inconsistency will cause a compilation error since std.io.Writer.Allocating does not exist in Zig's standard library. It should be std.Io.Writer.Allocating to match the existing pattern in the codebase.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method
deprecatedWriter()does not exist onstd.fs.File. This should bewriter()to get a writer from stdout.