Improve terminal rendering performance (#33345)

Closes #18263

Improvements:

• **Batch text rendering** - Combine adjacent cells with identical
styling into single text runs to reduce draw calls
• **Throttle hyperlink searches** - Limit hyperlink detection to every
100ms or when mouse moves >5px to reduce CPU usage
• **Pre-allocate collections** - Use `Vec::with_capacity()` for cells,
runs, and regions to minimize reallocations
• **Optimize background regions** - Merge adjacent background rectangles
to reduce number of draw operations
• **Cache selection text** - Only compute terminal selection string when
selection exists

Release Notes:

- Improved terminal rendering performance.

---------

Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
This commit is contained in:
Alisina Bahadori 2025-07-08 11:05:01 -04:00 committed by GitHub
parent bcac748c2b
commit 925464cfc6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 609 additions and 177 deletions

View file

@ -1066,7 +1066,7 @@ impl DisplaySnapshot {
}
let font_size = editor_style.text.font_size.to_pixels(*rem_size);
text_system.layout_line(&line, font_size, &runs)
text_system.layout_line(&line, font_size, &runs, None)
}
pub fn x_for_display_point(

View file

@ -1611,6 +1611,7 @@ impl EditorElement {
strikethrough: None,
underline: None,
}],
None,
)
})
} else {
@ -3263,10 +3264,12 @@ impl EditorElement {
underline: None,
strikethrough: None,
};
let line =
window
.text_system()
.shape_line(line.to_string().into(), font_size, &[run]);
let line = window.text_system().shape_line(
line.to_string().into(),
font_size,
&[run],
None,
);
LineWithInvisibles {
width: line.width,
len: line.len,
@ -6888,6 +6891,7 @@ impl EditorElement {
underline: None,
strikethrough: None,
}],
None,
);
layout.width
@ -6916,6 +6920,7 @@ impl EditorElement {
text,
self.style.text.font_size.to_pixels(window.rem_size()),
&[run],
None,
)
}
@ -7184,10 +7189,12 @@ impl LineWithInvisibles {
}]) {
if let Some(replacement) = highlighted_chunk.replacement {
if !line.is_empty() {
let shaped_line =
window
.text_system()
.shape_line(line.clone().into(), font_size, &styles);
let shaped_line = window.text_system().shape_line(
line.clone().into(),
font_size,
&styles,
None,
);
width += shaped_line.width;
len += shaped_line.len;
fragments.push(LineFragment::Text(shaped_line));
@ -7207,6 +7214,7 @@ impl LineWithInvisibles {
chunk,
font_size,
&[text_style.to_run(highlighted_chunk.text.len())],
None,
);
AvailableSpace::Definite(shaped_line.width)
} else {
@ -7251,7 +7259,7 @@ impl LineWithInvisibles {
};
let line_layout = window
.text_system()
.shape_line(x, font_size, &[run])
.shape_line(x, font_size, &[run], None)
.with_len(highlighted_chunk.text.len());
width += line_layout.width;
@ -7266,6 +7274,7 @@ impl LineWithInvisibles {
line.clone().into(),
font_size,
&styles,
None,
);
width += shaped_line.width;
len += shaped_line.len;
@ -8831,6 +8840,7 @@ impl Element for EditorElement {
underline: None,
strikethrough: None,
}],
None
);
let space_invisible = window.text_system().shape_line(
"".into(),
@ -8843,6 +8853,7 @@ impl Element for EditorElement {
underline: None,
strikethrough: None,
}],
None
);
let mode = snapshot.mode.clone();