fix(performance): keep track of scrollback buffer size (#881)
Fix #877. Avoid calculating scrollback buffer size on each render, since the calculation becomes slower proportionally to the amount of lines buffered, and to the width of each line. * wip: keep track of scrollback buffer size * account for lines dropped by bounded_push * fix: use saturating sub * fix: account for 0 width lines * remove log messages
This commit is contained in:
parent
639566df15
commit
707fedd1df
1 changed files with 85 additions and 19 deletions
|
|
@ -106,7 +106,7 @@ fn transfer_rows_from_lines_above_to_viewport(
|
|||
viewport: &mut Vec<Row>,
|
||||
count: usize,
|
||||
max_viewport_width: usize,
|
||||
) {
|
||||
) -> usize {
|
||||
let mut next_lines: Vec<Row> = vec![];
|
||||
let mut lines_added_to_viewport: isize = 0;
|
||||
loop {
|
||||
|
|
@ -137,6 +137,10 @@ fn transfer_rows_from_lines_above_to_viewport(
|
|||
let excess_row = Row::from_rows(next_lines, 0);
|
||||
bounded_push(lines_above, excess_row);
|
||||
}
|
||||
match usize::try_from(lines_added_to_viewport) {
|
||||
Ok(n) => n,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn transfer_rows_from_viewport_to_lines_above(
|
||||
|
|
@ -144,12 +148,15 @@ fn transfer_rows_from_viewport_to_lines_above(
|
|||
lines_above: &mut VecDeque<Row>,
|
||||
count: usize,
|
||||
max_viewport_width: usize,
|
||||
) {
|
||||
) -> isize {
|
||||
let mut next_lines: Vec<Row> = vec![];
|
||||
let mut transferred_rows_count: isize = 0;
|
||||
for _ in 0..count {
|
||||
if next_lines.is_empty() {
|
||||
if !viewport.is_empty() {
|
||||
let next_line = viewport.remove(0);
|
||||
transferred_rows_count +=
|
||||
calculate_row_display_height(next_line.width(), max_viewport_width) as isize;
|
||||
if !next_line.is_canonical {
|
||||
let mut bottom_canonical_row_and_wraps_in_dst =
|
||||
get_lines_above_bottom_canonical_row_and_wraps(lines_above);
|
||||
|
|
@ -161,7 +168,11 @@ fn transfer_rows_from_viewport_to_lines_above(
|
|||
break; // no more rows
|
||||
}
|
||||
}
|
||||
bounded_push(lines_above, next_lines.remove(0));
|
||||
let dropped_line_width = bounded_push(lines_above, next_lines.remove(0));
|
||||
if let Some(width) = dropped_line_width {
|
||||
transferred_rows_count -=
|
||||
calculate_row_display_height(width, max_viewport_width) as isize;
|
||||
}
|
||||
}
|
||||
if !next_lines.is_empty() {
|
||||
let excess_rows = Row::from_rows(next_lines, max_viewport_width)
|
||||
|
|
@ -170,6 +181,7 @@ fn transfer_rows_from_viewport_to_lines_above(
|
|||
viewport.insert(0, row);
|
||||
}
|
||||
}
|
||||
transferred_rows_count
|
||||
}
|
||||
|
||||
fn transfer_rows_from_lines_below_to_viewport(
|
||||
|
|
@ -212,11 +224,16 @@ fn transfer_rows_from_lines_below_to_viewport(
|
|||
}
|
||||
}
|
||||
|
||||
fn bounded_push(vec: &mut VecDeque<Row>, value: Row) {
|
||||
fn bounded_push(vec: &mut VecDeque<Row>, value: Row) -> Option<usize> {
|
||||
let mut dropped_line_width = None;
|
||||
if vec.len() >= SCROLL_BACK {
|
||||
vec.pop_front();
|
||||
let line = vec.pop_front();
|
||||
if let Some(line) = line {
|
||||
dropped_line_width = Some(line.width());
|
||||
}
|
||||
}
|
||||
vec.push_back(value)
|
||||
vec.push_back(value);
|
||||
dropped_line_width
|
||||
}
|
||||
|
||||
pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet<usize> {
|
||||
|
|
@ -232,6 +249,21 @@ pub fn create_horizontal_tabstops(columns: usize) -> BTreeSet<usize> {
|
|||
horizontal_tabstops
|
||||
}
|
||||
|
||||
fn calculate_row_display_height(row_width: usize, viewport_width: usize) -> usize {
|
||||
if row_width <= viewport_width {
|
||||
return 1;
|
||||
}
|
||||
(row_width as f64 / viewport_width as f64).ceil() as usize
|
||||
}
|
||||
|
||||
fn subtract_isize_from_usize(u: usize, i: isize) -> usize {
|
||||
if i.is_negative() {
|
||||
u - i.abs() as usize
|
||||
} else {
|
||||
u + i as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CharacterChunk {
|
||||
pub terminal_characters: Vec<TerminalCharacter>,
|
||||
|
|
@ -362,6 +394,7 @@ pub struct Grid {
|
|||
pub selection: Selection,
|
||||
pub title: Option<String>,
|
||||
pub is_scrolled: bool,
|
||||
scrollback_buffer_lines: usize,
|
||||
}
|
||||
|
||||
impl Debug for Grid {
|
||||
|
|
@ -407,6 +440,7 @@ impl Grid {
|
|||
title: None,
|
||||
changed_colors: None,
|
||||
is_scrolled: false,
|
||||
scrollback_buffer_lines: 0,
|
||||
}
|
||||
}
|
||||
pub fn render_full_viewport(&mut self) {
|
||||
|
|
@ -453,23 +487,28 @@ impl Grid {
|
|||
pub fn cursor_shape(&self) -> CursorShape {
|
||||
self.cursor.get_shape()
|
||||
}
|
||||
pub fn scrollback_position_and_length(&self) -> (usize, usize) {
|
||||
pub fn scrollback_position_and_length(&mut self) -> (usize, usize) {
|
||||
// (position, length)
|
||||
(
|
||||
self.lines_below.len(),
|
||||
(self.scrollback_buffer_lines + self.lines_below.len()),
|
||||
)
|
||||
}
|
||||
|
||||
fn recalculate_scrollback_buffer_count(&self) -> usize {
|
||||
let mut scrollback_buffer_count = 0;
|
||||
for row in self.lines_above.iter() {
|
||||
let row_width = row.width();
|
||||
// rows in lines_above are unwrapped, so we need to account for that
|
||||
if row_width > self.width {
|
||||
scrollback_buffer_count += (row_width as f64 / self.width as f64).ceil() as usize;
|
||||
scrollback_buffer_count += calculate_row_display_height(row_width, self.width);
|
||||
} else {
|
||||
scrollback_buffer_count += 1;
|
||||
}
|
||||
}
|
||||
(
|
||||
self.lines_below.len(),
|
||||
(scrollback_buffer_count + self.lines_below.len()),
|
||||
)
|
||||
scrollback_buffer_count
|
||||
}
|
||||
|
||||
fn set_horizontal_tabstop(&mut self) {
|
||||
self.horizontal_tabstops.insert(self.cursor.x);
|
||||
}
|
||||
|
|
@ -543,12 +582,15 @@ impl Grid {
|
|||
let line_to_push_down = self.viewport.pop().unwrap();
|
||||
self.lines_below.insert(0, line_to_push_down);
|
||||
|
||||
transfer_rows_from_lines_above_to_viewport(
|
||||
let transferred_rows_height = transfer_rows_from_lines_above_to_viewport(
|
||||
&mut self.lines_above,
|
||||
&mut self.viewport,
|
||||
1,
|
||||
self.width,
|
||||
);
|
||||
self.scrollback_buffer_lines = self
|
||||
.scrollback_buffer_lines
|
||||
.saturating_sub(transferred_rows_height);
|
||||
|
||||
self.selection.move_down(1);
|
||||
}
|
||||
|
|
@ -557,12 +599,25 @@ impl Grid {
|
|||
pub fn scroll_down_one_line(&mut self) {
|
||||
if !self.lines_below.is_empty() && self.viewport.len() == self.height {
|
||||
let mut line_to_push_up = self.viewport.remove(0);
|
||||
if line_to_push_up.is_canonical {
|
||||
bounded_push(&mut self.lines_above, line_to_push_up);
|
||||
|
||||
self.scrollback_buffer_lines +=
|
||||
calculate_row_display_height(line_to_push_up.width(), self.width);
|
||||
|
||||
let line_to_push_up = if line_to_push_up.is_canonical {
|
||||
line_to_push_up
|
||||
} else {
|
||||
let mut last_line_above = self.lines_above.pop_back().unwrap();
|
||||
last_line_above.append(&mut line_to_push_up.columns);
|
||||
bounded_push(&mut self.lines_above, last_line_above);
|
||||
last_line_above
|
||||
};
|
||||
|
||||
let dropped_line_width = bounded_push(&mut self.lines_above, line_to_push_up);
|
||||
if let Some(width) = dropped_line_width {
|
||||
let dropped_line_height = calculate_row_display_height(width, self.width);
|
||||
|
||||
self.scrollback_buffer_lines = self
|
||||
.scrollback_buffer_lines
|
||||
.saturating_sub(dropped_line_height);
|
||||
}
|
||||
|
||||
transfer_rows_from_lines_below_to_viewport(
|
||||
|
|
@ -753,6 +808,7 @@ impl Grid {
|
|||
if self.scroll_region.is_some() {
|
||||
self.set_scroll_region_to_viewport_size();
|
||||
}
|
||||
self.scrollback_buffer_lines = self.recalculate_scrollback_buffer_count();
|
||||
self.output_buffer.update_all_lines();
|
||||
}
|
||||
pub fn as_character_lines(&self) -> Vec<Vec<TerminalCharacter>> {
|
||||
|
|
@ -846,12 +902,16 @@ impl Grid {
|
|||
}
|
||||
pub fn fill_viewport(&mut self, character: TerminalCharacter) {
|
||||
let row_count_to_transfer = self.viewport.len();
|
||||
transfer_rows_from_viewport_to_lines_above(
|
||||
let transferred_rows_count = transfer_rows_from_viewport_to_lines_above(
|
||||
&mut self.viewport,
|
||||
&mut self.lines_above,
|
||||
row_count_to_transfer,
|
||||
self.width,
|
||||
);
|
||||
|
||||
self.scrollback_buffer_lines =
|
||||
subtract_isize_from_usize(self.scrollback_buffer_lines, transferred_rows_count);
|
||||
|
||||
for _ in 0..self.height {
|
||||
let columns = VecDeque::from(vec![character; self.width]);
|
||||
self.viewport.push(Row::from_columns(columns).canonical());
|
||||
|
|
@ -893,12 +953,15 @@ impl Grid {
|
|||
if self.cursor.y == self.height - 1 {
|
||||
if self.scroll_region.is_none() {
|
||||
let row_count_to_transfer = 1;
|
||||
transfer_rows_from_viewport_to_lines_above(
|
||||
let transferred_rows_count = transfer_rows_from_viewport_to_lines_above(
|
||||
&mut self.viewport,
|
||||
&mut self.lines_above,
|
||||
row_count_to_transfer,
|
||||
self.width,
|
||||
);
|
||||
self.scrollback_buffer_lines =
|
||||
subtract_isize_from_usize(self.scrollback_buffer_lines, transferred_rows_count);
|
||||
|
||||
self.selection.move_up(1);
|
||||
}
|
||||
self.output_buffer.update_all_lines();
|
||||
|
|
@ -968,12 +1031,14 @@ impl Grid {
|
|||
self.cursor.x = 0;
|
||||
if self.cursor.y == self.height - 1 {
|
||||
let row_count_to_transfer = 1;
|
||||
transfer_rows_from_viewport_to_lines_above(
|
||||
let transferred_rows_count = transfer_rows_from_viewport_to_lines_above(
|
||||
&mut self.viewport,
|
||||
&mut self.lines_above,
|
||||
row_count_to_transfer,
|
||||
self.width,
|
||||
);
|
||||
self.scrollback_buffer_lines =
|
||||
subtract_isize_from_usize(self.scrollback_buffer_lines, transferred_rows_count);
|
||||
let wrapped_row = Row::new(self.width);
|
||||
self.viewport.push(wrapped_row);
|
||||
self.selection.move_up(1);
|
||||
|
|
@ -1264,6 +1329,7 @@ impl Grid {
|
|||
self.cursor.change_shape(CursorShape::Initial);
|
||||
self.output_buffer.update_all_lines();
|
||||
self.changed_colors = None;
|
||||
self.scrollback_buffer_lines = 0;
|
||||
}
|
||||
fn set_preceding_character(&mut self, terminal_character: TerminalCharacter) {
|
||||
self.preceding_char = Some(terminal_character);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue