mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-25 13:16:11 +03:00
process ASCII events manually to avoid function call overhead
This commit is contained in:
@ -1271,9 +1271,43 @@ fn ttyRead(t: *libuv.Tty, n: isize, buf: []const u8) void {
|
|||||||
// Schedule a render
|
// Schedule a render
|
||||||
win.render_timer.schedule() catch unreachable;
|
win.render_timer.schedule() catch unreachable;
|
||||||
|
|
||||||
// Process the terminal data
|
// Process the terminal data. This is an extremely hot part of the
|
||||||
win.terminal_stream.nextSlice(buf[0..@intCast(usize, n)]) catch |err|
|
// terminal emulator, so we do some abstraction leakage to avoid
|
||||||
log.err("error processing terminal data: {}", .{err});
|
// function calls and unnecessary logic.
|
||||||
|
//
|
||||||
|
// The ground state is the only state that we can see and print/execute
|
||||||
|
// ASCII, so we only execute this hot path if we're already in the ground
|
||||||
|
// state.
|
||||||
|
//
|
||||||
|
// Empirically, this alone improved throughput of large text output by ~20%.
|
||||||
|
var i: usize = 0;
|
||||||
|
const end = @intCast(usize, n);
|
||||||
|
if (win.terminal_stream.parser.state == .ground) {
|
||||||
|
for (buf[i..end]) |c| {
|
||||||
|
switch (terminal.parse_table.table[c][@enumToInt(terminal.Parser.State.ground)].action) {
|
||||||
|
// Print, call directly.
|
||||||
|
.print => win.print(@intCast(u21, c)) catch |err|
|
||||||
|
log.err("error processing terminal data: {}", .{err}),
|
||||||
|
|
||||||
|
// C0 execute, let our stream handle this one but otherwise
|
||||||
|
// continue since we're guaranteed to be back in ground.
|
||||||
|
.execute => win.terminal_stream.next(c) catch |err|
|
||||||
|
log.err("error processing terminal data: {}", .{err}),
|
||||||
|
|
||||||
|
// Otherwise, break out and go the slow path until we're
|
||||||
|
// back in ground.
|
||||||
|
else => break,
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < end) {
|
||||||
|
//log.warn("SLOW={}", .{end - i});
|
||||||
|
win.terminal_stream.nextSlice(buf[i..end]) catch |err|
|
||||||
|
log.err("error processing terminal data: {}", .{err});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ttyWrite(req: *libuv.WriteReq, status: i32) void {
|
fn ttyWrite(req: *libuv.WriteReq, status: i32) void {
|
||||||
|
@ -6,6 +6,7 @@ const Parser = @This();
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const trace = @import("tracy").trace;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const table = @import("parse_table.zig").table;
|
const table = @import("parse_table.zig").table;
|
||||||
const osc = @import("osc.zig");
|
const osc = @import("osc.zig");
|
||||||
@ -212,6 +213,9 @@ pub fn init() Parser {
|
|||||||
/// Up to 3 actions may need to be exected -- in order -- representing
|
/// Up to 3 actions may need to be exected -- in order -- representing
|
||||||
/// the state exit, transition, and entry actions.
|
/// the state exit, transition, and entry actions.
|
||||||
pub fn next(self: *Parser, c: u8) [3]?Action {
|
pub fn next(self: *Parser, c: u8) [3]?Action {
|
||||||
|
const tracy = trace(@src());
|
||||||
|
defer tracy.end();
|
||||||
|
|
||||||
// If we're processing UTF-8, we handle this manually.
|
// If we're processing UTF-8, we handle this manually.
|
||||||
if (self.state == .utf8) {
|
if (self.state == .utf8) {
|
||||||
return .{ self.next_utf8(c), null, null };
|
return .{ self.next_utf8(c), null, null };
|
||||||
|
@ -22,6 +22,7 @@ const assert = std.debug.assert;
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const utf8proc = @import("utf8proc");
|
const utf8proc = @import("utf8proc");
|
||||||
|
const trace = @import("tracy").trace;
|
||||||
const color = @import("color.zig");
|
const color = @import("color.zig");
|
||||||
const point = @import("point.zig");
|
const point = @import("point.zig");
|
||||||
const CircBuf = @import("circ_buf.zig").CircBuf;
|
const CircBuf = @import("circ_buf.zig").CircBuf;
|
||||||
@ -432,6 +433,9 @@ pub fn rowIterator(self: *Screen, tag: RowIndexTag) RowIterator {
|
|||||||
/// Returns the row at the given index. This row is writable, although
|
/// Returns the row at the given index. This row is writable, although
|
||||||
/// only the active area should probably be written to.
|
/// only the active area should probably be written to.
|
||||||
pub fn getRow(self: *Screen, index: RowIndex) Row {
|
pub fn getRow(self: *Screen, index: RowIndex) Row {
|
||||||
|
const tracy = trace(@src());
|
||||||
|
defer tracy.end();
|
||||||
|
|
||||||
// Get our offset into storage
|
// Get our offset into storage
|
||||||
const offset = index.toScreen(self).screen * (self.cols + 1);
|
const offset = index.toScreen(self).screen * (self.cols + 1);
|
||||||
|
|
||||||
|
@ -464,7 +464,7 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
|
|
||||||
switch (width) {
|
switch (width) {
|
||||||
// Single cell is very easy: just write in the cell
|
// Single cell is very easy: just write in the cell
|
||||||
1 => _ = self.printCell(c),
|
1 => _ = @call(.{ .modifier = .always_inline }, self.printCell, .{c}),
|
||||||
|
|
||||||
// Wide character requires a spacer. We print this by
|
// Wide character requires a spacer. We print this by
|
||||||
// using two cells: the first is flagged "wide" and has the
|
// using two cells: the first is flagged "wide" and has the
|
||||||
@ -505,6 +505,9 @@ pub fn print(self: *Terminal, c: u21) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
|
fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
|
||||||
|
// const tracy = trace(@src());
|
||||||
|
// defer tracy.end();
|
||||||
|
|
||||||
const c = c: {
|
const c = c: {
|
||||||
// TODO: non-utf8 handling, gr
|
// TODO: non-utf8 handling, gr
|
||||||
|
|
||||||
@ -558,6 +561,9 @@ fn printCell(self: *Terminal, unmapped_c: u21) *Screen.Cell {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn printWrap(self: *Terminal) !void {
|
fn printWrap(self: *Terminal) !void {
|
||||||
|
const tracy = trace(@src());
|
||||||
|
defer tracy.end();
|
||||||
|
|
||||||
const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
|
const row = self.screen.getRow(.{ .active = self.screen.cursor.y });
|
||||||
row.setWrapped(true);
|
row.setWrapped(true);
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ const csi = @import("csi.zig");
|
|||||||
const sgr = @import("sgr.zig");
|
const sgr = @import("sgr.zig");
|
||||||
pub const point = @import("point.zig");
|
pub const point = @import("point.zig");
|
||||||
pub const color = @import("color.zig");
|
pub const color = @import("color.zig");
|
||||||
|
pub const parse_table = @import("parse_table.zig");
|
||||||
|
|
||||||
pub const Charset = charsets.Charset;
|
pub const Charset = charsets.Charset;
|
||||||
pub const CharsetSlot = charsets.Slots;
|
pub const CharsetSlot = charsets.Slots;
|
||||||
|
Reference in New Issue
Block a user