1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-18 13:55:23 -05:00

WIP optimize scroll in region

This intends to optimize the case where the top of the scrolling region
is the top of the screen. In theory, scrolling in this case can be
optimized to shifting the start/end of the visible region, and then
rearranging any lines that were not supposed to be scrolled (at the
bottom of the region). However, this didn't produce quite the speedup I
expected.
This commit is contained in:
Joe Wilm 2018-01-14 21:51:56 -08:00
parent d0a1ff4736
commit b7578a5236
2 changed files with 187 additions and 32 deletions

View file

@ -21,7 +21,6 @@
//! ranges is currently supported. //! ranges is currently supported.
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{VecDeque, vec_deque};
use std::iter::IntoIterator; use std::iter::IntoIterator;
use std::ops::{Deref, Range, RangeTo, RangeFrom, RangeFull, Index, IndexMut}; use std::ops::{Deref, Range, RangeTo, RangeFrom, RangeFull, Index, IndexMut};
@ -33,6 +32,9 @@ pub use self::row::Row;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
mod storage;
use self::storage::Storage;
/// Convert a type to a linear index range. /// Convert a type to a linear index range.
pub trait ToRange { pub trait ToRange {
fn to_range(&self) -> RangeInclusive<index::Linear>; fn to_range(&self) -> RangeInclusive<index::Linear>;
@ -64,7 +66,7 @@ impl<T> Deref for Indexed<T> {
pub struct Grid<T> { pub struct Grid<T> {
/// Lines in the grid. Each row holds a list of cells corresponding to the /// Lines in the grid. Each row holds a list of cells corresponding to the
/// columns in that row. /// columns in that row.
raw: VecDeque<Row<T>>, raw: Storage<Row<T>>,
/// Number of columns /// Number of columns
cols: index::Column, cols: index::Column,
@ -82,6 +84,9 @@ pub struct Grid<T> {
/// Template cell for populating template_row /// Template cell for populating template_row
template: T, template: T,
/// Temporary row storage for scrolling with a region
temp: Vec<Row<T>>,
} }
pub struct GridIterator<'a, T: 'a> { pub struct GridIterator<'a, T: 'a> {
@ -91,10 +96,10 @@ pub struct GridIterator<'a, T: 'a> {
impl<T: Copy + Clone> Grid<T> { impl<T: Copy + Clone> Grid<T> {
pub fn new(lines: index::Line, cols: index::Column, template: T) -> Grid<T> { pub fn new(lines: index::Line, cols: index::Column, template: T) -> Grid<T> {
let mut raw = VecDeque::with_capacity(*lines); let mut raw = Storage::with_capacity(*lines);
let template_row = Row::new(cols, &template); let template_row = Row::new(cols, &template);
for _ in IndexRange(index::Line(0)..lines) { for _ in IndexRange(index::Line(0)..lines) {
raw.push_back(template_row.clone()); raw.push(template_row.clone());
} }
Grid { Grid {
@ -103,6 +108,7 @@ impl<T: Copy + Clone> Grid<T> {
lines, lines,
template_row, template_row,
template, template,
temp: Vec::new(),
} }
} }
@ -127,7 +133,7 @@ impl<T: Copy + Clone> Grid<T> {
fn grow_lines(&mut self, lines: index::Line) { fn grow_lines(&mut self, lines: index::Line) {
for _ in IndexRange(self.num_lines()..lines) { for _ in IndexRange(self.num_lines()..lines) {
self.raw.push_back(self.template_row.clone()); self.raw.push(self.template_row.clone());
} }
self.lines = lines; self.lines = lines;
@ -147,12 +153,25 @@ impl<T: Copy + Clone> Grid<T> {
#[inline] #[inline]
pub fn scroll_down(&mut self, region: &Range<index::Line>, positions: index::Line) { pub fn scroll_down(&mut self, region: &Range<index::Line>, positions: index::Line) {
if region.start == Line(0) && region.end == self.num_lines() { // Whether or not there is a scrolling region active, as long as it
// Full rotation // starts at the top, we can do a full rotation which just involves
for _ in 0..positions.0 { // changing the start index.
let mut item = self.raw.pop_back().unwrap(); //
item.reset(&self.template_row); // To accomodate scroll regions, rows are reordered at the end.
self.raw.push_front(item); if region.start == Line(0) {
// Rotate the entire line buffer. If there's a scrolling region
// active, the bottom lines are restored in the next step.
self.raw.rotate(-(*positions as isize));
// Now, restore any scroll region lines
for i in IndexRange(region.end .. self.num_lines()) {
// First do the swap
self.raw.swap(*i, *i + *positions);
}
// Finally, reset recycled lines
for i in 0..*positions {
self.raw[i].reset(&self.template_row);
} }
} else { } else {
// Subregion rotation // Subregion rotation
@ -160,23 +179,33 @@ impl<T: Copy + Clone> Grid<T> {
self.swap_lines(line, line - positions); self.swap_lines(line, line - positions);
} }
let template = &self.template_row;
for i in IndexRange(Line(0)..positions) { for i in IndexRange(Line(0)..positions) {
self.raw self.raw[*(region.start - i - 1)].reset(&self.template_row);
.get_mut(*(region.start + i))
.map(|row| row.reset(template));
} }
} }
} }
#[inline] #[inline]
pub fn scroll_up(&mut self, region: &Range<index::Line>, positions: index::Line) { pub fn scroll_up(&mut self, region: &Range<index::Line>, positions: index::Line) {
if region.start == Line(0) && region.end == self.num_lines() { if region.start == Line(0) {
// Full rotation // Rotate the entire line buffer. If there's a scrolling region
for _ in 0..positions.0 { // active, the bottom lines are restored in the next step.
let mut item = self.raw.pop_front().unwrap(); self.raw.rotate(*positions as isize);
item.reset(&self.template_row);
self.raw.push_back(item); // Now, restore any lines outside the scroll region
let mut i = 0;
for _ in IndexRange(region.end .. self.num_lines()) {
let idx = *self.num_lines() - i - 1;
// First do the swap
self.raw.swap(idx, idx - *positions);
i += 1;
}
// Finally, reset recycled lines
//
// Recycled lines are just above the end of the scrolling region.
for i in 0..*positions {
self.raw[*region.end - i - 1].reset(&self.template_row);
} }
} else { } else {
// Subregion rotation // Subregion rotation
@ -185,11 +214,8 @@ impl<T: Copy + Clone> Grid<T> {
} }
// Clear reused lines // Clear reused lines
let template = &self.template_row;
for i in IndexRange(Line(0)..positions) { for i in IndexRange(Line(0)..positions) {
self.raw self.raw[*(region.start - i - 1)].reset(&self.template_row);
.get_mut(*(region.end - i - 1))
.map(|row| row.reset(template));
} }
} }
} }
@ -229,7 +255,7 @@ impl<T> Grid<T> {
fn shrink_lines(&mut self, lines: index::Line) { fn shrink_lines(&mut self, lines: index::Line) {
while index::Line(self.raw.len()) != lines { while index::Line(self.raw.len()) != lines {
self.raw.pop_back(); self.raw.pop();
} }
self.lines = lines; self.lines = lines;
@ -322,10 +348,10 @@ impl<'point, T> IndexMut<&'point Point> for Grid<T> {
impl<'a, T> IntoIterator for &'a Grid<T> { impl<'a, T> IntoIterator for &'a Grid<T> {
type Item = &'a Row<T>; type Item = &'a Row<T>;
type IntoIter = vec_deque::Iter<'a, Row<T>>; type IntoIter = self::storage::Iter<'a, Row<T>>;
#[inline] #[inline]
fn into_iter(self) -> vec_deque::Iter<'a, Row<T>> { fn into_iter(self) -> self::storage::Iter<'a, Row<T>> {
self.raw.iter() self.raw.iter()
} }
} }
@ -340,7 +366,7 @@ impl<'a, T> IntoIterator for &'a Grid<T> {
pub struct Region<'a, T: 'a> { pub struct Region<'a, T: 'a> {
start: Line, start: Line,
end: Line, end: Line,
raw: &'a VecDeque<Row<T>>, raw: &'a Storage<Row<T>>,
} }
/// A mutable subset of lines in the grid /// A mutable subset of lines in the grid
@ -349,7 +375,7 @@ pub struct Region<'a, T: 'a> {
pub struct RegionMut<'a, T: 'a> { pub struct RegionMut<'a, T: 'a> {
start: Line, start: Line,
end: Line, end: Line,
raw: &'a mut VecDeque<Row<T>>, raw: &'a mut Storage<Row<T>>,
} }
impl<'a, T> RegionMut<'a, T> { impl<'a, T> RegionMut<'a, T> {
@ -453,13 +479,13 @@ impl<T> IndexRegion<RangeFull, T> for Grid<T> {
pub struct RegionIter<'a, T: 'a> { pub struct RegionIter<'a, T: 'a> {
end: Line, end: Line,
cur: Line, cur: Line,
raw: &'a VecDeque<Row<T>>, raw: &'a Storage<Row<T>>,
} }
pub struct RegionIterMut<'a, T: 'a> { pub struct RegionIterMut<'a, T: 'a> {
end: Line, end: Line,
cur: Line, cur: Line,
raw: &'a mut VecDeque<Row<T>>, raw: &'a mut Storage<Row<T>>,
} }
impl<'a, T> IntoIterator for Region<'a, T> { impl<'a, T> IntoIterator for Region<'a, T> {

129
src/grid/storage.rs Normal file
View file

@ -0,0 +1,129 @@
/// Wrapper around Vec which supports fast indexing and rotation
///
/// The rotation implemented by grid::Storage is a simple integer addition.
/// Compare with standard library rotation which requires rearranging items in
/// memory.
///
/// As a consequence, the indexing operators need to be reimplemented for this
/// type to account for the 0th element not always being at the start of the
/// allocation.
///
/// Because certain Vec operations are no longer valid on this type, no Deref
/// implementation is provided. Anything from Vec that should be exposed must be
/// done so manually.
use std::ops::{Index, IndexMut};
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
pub struct Storage<T> {
inner: Vec<T>,
zero: usize,
}
impl<T> Storage<T> {
#[inline]
pub fn with_capacity(cap: usize) -> Storage<T> {
Storage {
inner: Vec::with_capacity(cap),
zero: 0
}
}
#[inline]
pub fn push(&mut self, item: T) {
self.inner.push(item)
}
#[inline]
pub fn pop(&mut self) -> Option<T> {
self.inner.pop()
}
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
/// Compute actual index in underlying storage given the requested index.
#[inline]
fn compute_index(&self, requested: usize) -> usize {
(requested + self.zero) % self.len()
}
pub fn swap(&mut self, a: usize, b: usize) {
let a = self.compute_index(a);
let b = self.compute_index(b);
self.inner.swap(a, b);
}
pub fn iter(&self) -> Iter<T> {
Iter { storage: self, index: 0 }
}
pub fn iter_mut(&mut self) -> IterMut<T> {
IterMut { storage: self, index: 0 }
}
pub fn rotate(&mut self, count: isize) {
let len = self.len();
assert!(count.abs() as usize <= len);
self.zero += (count + len as isize) as usize % len;
}
}
impl<T> Index<usize> for Storage<T> {
type Output = T;
#[inline]
fn index(&self, index: usize) -> &T {
let index = self.compute_index(index); // borrowck
&self.inner[index]
}
}
impl<T> IndexMut<usize> for Storage<T> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut T {
let index = self.compute_index(index); // borrowck
&mut self.inner[index]
}
}
pub struct Iter<'a, T: 'a> {
storage: &'a Storage<T>,
index: usize,
}
pub struct IterMut<'a, T: 'a> {
storage: &'a mut Storage<T>,
index: usize,
}
impl<'a, T: 'a> Iterator for Iter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.storage.len() {
None
} else {
let res = Some(&self.storage[self.index]);
self.index += 1;
res
}
}
}
impl<'a, T: 'a> Iterator for IterMut<'a, T> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.storage.len() {
None
} else {
let index = self.index;
self.index += 1;
unsafe {
Some(&mut *(&mut self.storage[index] as *mut _))
}
}
}
}