Files
ghostty-rs/crates/ghostty-renderer/src/row.rs
T
ddidderr abb89a80e9 perf: close the last benchmark gaps to Zig (render state, parser)
- RenderState stores styles sparsely (per-cell u16 style ids plus one
  Style per distinct run) and skips plain cells with a single mask test
  (Cell::is_plain). screen-clone render: 11.5 ms -> 4.1 ms (Zig 4.7).
  row::never_extend_bg now takes a style accessor instead of a slice.
- Parser::next_with(byte, |action| ..) hands actions to a closure
  instead of moving a 64-byte action array out; Parser::next remains a
  wrapper over the same core (equivalence test added).
  terminal-parser SGR: 538 ms -> 231 ms (Zig 354).

All Ghostty benchmarks are now at or below Zig's time; table in
docs/rewrite/notes/perf-P1.md (the parser benchmark uses next_with with
a discarding closure, which like Zig's `_ = actions` lets the optimizer
skip unused work).

Test Plan:
- cargo test --workspace
- ghostty-bench +screen-clone --mode=render / +terminal-parser
2026-10-12 05:22:01 +02:00

152 lines
4.5 KiB
Rust

//! Row heuristics for the renderer (`renderer/row.zig`).
use ghostty_terminal::{
page::{Cell, ContentTag, Row, SemanticPrompt},
style::Style,
};
use ghostty_vt::color::{Palette, Rgb};
/// Returns true if the row of this pin should never have its background
/// color extended for filling padding space in the renderer. This is
/// a set of heuristics that help making our padding look better.
///
/// `style_of` returns the style of the render-state row's cell at `x`.
pub fn never_extend_bg(
row: Row,
cells: &[Cell],
style_of: impl Fn(usize) -> Style,
palette: &Palette,
default_background: Rgb,
) -> bool {
// Any semantic prompts should not have their background extended
// because prompts often contain special formatting (such as
// powerline) that looks bad when extended.
match row.semantic_prompt() {
SemanticPrompt::Prompt | SemanticPrompt::PromptContinuation => return true,
SemanticPrompt::None => {}
}
for (x, cell) in cells.iter().enumerate() {
// If any cell has a default background color then we don't
// extend because the default background color probably looks
// good enough as an extension.
match cell.content_tag() {
// If it is a background color cell, we check the color.
ContentTag::BgColorPalette | ContentTag::BgColorRgb => {
let s = style_at(*cell, &style_of, x);
let Some(bg) = s.bg(cell, palette) else {
return true;
};
if bg == default_background {
return true;
}
}
// If its a codepoint cell we can check the style.
ContentTag::Codepoint | ContentTag::CodepointGrapheme => {
// For codepoint containing, we also never extend bg
// if any cell has a powerline glyph because these are
// perfect-fit.
if matches!(
cell.codepoint(),
// Powerline
0xE0B0..=0xE0C8 | 0xE0CA | 0xE0CC..=0xE0D2 | 0xE0D4
) {
return true;
}
// Never extend a cell that has a default background.
// A default background is applied if there is no background
// on the style or the explicitly set background
// matches our default background.
let s = style_at(*cell, &style_of, x);
let Some(bg) = s.bg(cell, palette) else {
return true;
};
if bg == default_background {
return true;
}
}
}
}
false
}
fn style_at(cell: Cell, style_of: &impl Fn(usize) -> Style, x: usize) -> Style {
if cell.has_styling() {
style_of(x)
} else {
Style::default()
}
}
#[cfg(test)]
mod tests {
use ghostty_vt::color;
use super::*;
const BG: Rgb = Rgb { r: 1, g: 2, b: 3 };
#[test]
fn default_background_cells_never_extend() {
let cells = [Cell::init(u32::from('a'))];
assert!(never_extend_bg(
Row::default(),
&cells,
|_| Style::default(),
&color::DEFAULT,
BG
));
}
#[test]
fn prompt_rows_never_extend() {
let mut row = Row::default();
row.set_semantic_prompt(SemanticPrompt::Prompt);
assert!(never_extend_bg(
row,
&[],
|_| Style::default(),
&color::DEFAULT,
BG
));
}
#[test]
fn powerline_never_extends() {
let cells = [Cell::init(0xE0B0)];
assert!(never_extend_bg(
Row::default(),
&cells,
|_| Style::default(),
&color::DEFAULT,
BG
));
}
#[test]
fn explicit_bg_color_cell_extends() {
let mut c = Cell::default();
c.set_content_tag(ContentTag::BgColorRgb);
c.set_color_rgb(Rgb { r: 9, g: 9, b: 9 });
assert!(!never_extend_bg(
Row::default(),
&[c],
|_| Style::default(),
&color::DEFAULT,
BG
));
// ... unless it equals the default background.
c.set_color_rgb(BG);
assert!(never_extend_bg(
Row::default(),
&[c],
|_| Style::default(),
&color::DEFAULT,
BG
));
}
}