Cache layout and performance fixes

This commit is contained in:
Florian Dehau 2016-10-20 16:26:34 +02:00
parent 07ff2b08eb
commit d7131ead11
5 changed files with 61 additions and 31 deletions

View file

@ -82,7 +82,7 @@ struct App {
progress: u16,
data: Vec<u64>,
data2: Vec<(f64, f64)>,
window: [f64; 2],
data3: Vec<(f64, f64)>,
colors: [Color; 2],
color_index: usize,
}
@ -109,6 +109,7 @@ fn main() {
let mut rand_signal = RandomSignal::new(Range::new(0, 100));
let mut sin_signal = SinSignal::new(4.0, 20.0);
let mut sin_signal2 = SinSignal::new(2.0, 10.0);
let mut app = App {
name: String::from("Test app"),
@ -119,7 +120,7 @@ fn main() {
progress: 0,
data: rand_signal.clone().take(100).collect(),
data2: sin_signal.clone().take(100).collect(),
window: [0.0, 100.0],
data3: sin_signal2.clone().take(100).collect(),
colors: [Color::Magenta, Color::Red],
color_index: 0,
};
@ -141,7 +142,7 @@ fn main() {
let tx = tx.clone();
loop {
tx.send(Event::Tick).unwrap();
thread::sleep(time::Duration::from_millis(500));
thread::sleep(time::Duration::from_millis(1000));
}
});
@ -184,8 +185,8 @@ fn main() {
app.data.pop();
app.data2.remove(0);
app.data2.push(sin_signal.next().unwrap());
app.window[0] += 1.0;
app.window[1] += 1.0;
app.data3.remove(0);
app.data3.push(sin_signal2.next().unwrap());
app.selected += 1;
if app.selected >= app.items.len() {
app.selected = 0;
@ -206,14 +207,14 @@ fn draw(t: &mut Terminal, app: &App) {
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.chunks(&[Size::Fixed(7), Size::Min(5), Size::Fixed(3)])
.render(&Terminal::size().unwrap(), |chunks| {
.render(t, &Terminal::size().unwrap(), |t, chunks| {
Block::default().borders(border::ALL).title("Graphs").render(&chunks[0], t);
Group::default()
.direction(Direction::Vertical)
.alignment(Alignment::Left)
.margin(1)
.chunks(&[Size::Fixed(2), Size::Fixed(3)])
.render(&chunks[0], |chunks| {
.render(t, &chunks[0], |t, chunks| {
Gauge::default()
.block(Block::default().title("Gauge:"))
.bg(Color::Yellow)
@ -234,7 +235,7 @@ fn draw(t: &mut Terminal, app: &App) {
.direction(Direction::Horizontal)
.alignment(Alignment::Left)
.chunks(&sizes)
.render(&chunks[1], |chunks| {
.render(t, &chunks[1], |t, chunks| {
List::default()
.block(Block::default().borders(border::ALL).title("List"))
.render(&chunks[0], t);
@ -243,9 +244,10 @@ fn draw(t: &mut Terminal, app: &App) {
.block(Block::default()
.borders(border::ALL)
.title("Chart"))
.x_axis(Axis::default().title("X").bounds(app.window))
.x_axis(Axis::default().title("X").bounds([0.0, 100.0]))
.y_axis(Axis::default().title("Y").bounds([0.0, 40.0]))
.datasets(&[Dataset::default().color(Color::Cyan).data(&app.data2)])
.datasets(&[Dataset::default().color(Color::Cyan).data(&app.data2),
Dataset::default().color(Color::Yellow).data(&app.data3)])
.render(&chunks[1], t);
}
});

View file

@ -6,7 +6,10 @@ use cassowary::WeightedRelation::*;
use cassowary::strength::{WEAK, REQUIRED};
use buffer::Buffer;
use terminal::Terminal;
use util::hash;
#[derive(Hash)]
pub enum Alignment {
Top,
Left,
@ -15,12 +18,13 @@ pub enum Alignment {
Right,
}
#[derive(Hash)]
pub enum Direction {
Horizontal,
Vertical,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct Rect {
pub x: u16,
pub y: u16,
@ -235,6 +239,7 @@ impl Element {
}
}
#[derive(Hash)]
pub struct Group {
direction: Direction,
alignment: Alignment,
@ -273,14 +278,26 @@ impl Group {
self.chunks = Vec::from(chunks);
self
}
pub fn render<F>(&self, area: &Rect, mut f: F)
where F: FnMut(&[Rect])
pub fn render<F>(&self, t: &mut Terminal, area: &Rect, mut f: F)
where F: FnMut(&mut Terminal, &[Rect])
{
let chunks = split(area,
&self.direction,
&self.alignment,
self.margin,
&self.chunks);
f(&chunks);
let hash = hash(self, area);
let (cache_update, chunks) = match t.get_layout(hash) {
Some(chs) => (false, chs.to_vec()),
None => {
(true,
split(area,
&self.direction,
&self.alignment,
self.margin,
&self.chunks))
}
};
f(t, &chunks);
if cache_update {
t.set_layout(hash, chunks);
}
}
}

View file

@ -8,16 +8,19 @@ use termion::raw::{IntoRawMode, RawTerminal};
use buffer::Buffer;
use widgets::Widget;
use layout::Rect;
use util::hash;
pub struct Terminal {
stdout: RawTerminal<io::Stdout>,
layout_cache: HashMap<u64, Vec<Rect>>,
}
impl Terminal {
pub fn new() -> Result<Terminal, io::Error> {
let stdout = try!(io::stdout().into_raw_mode());
Ok(Terminal { stdout: stdout })
Ok(Terminal {
stdout: stdout,
layout_cache: HashMap::new(),
})
}
pub fn size() -> Result<Rect, io::Error> {
@ -30,20 +33,29 @@ impl Terminal {
})
}
// FIXME: Clean cache to prevent memory leak
pub fn get_layout(&self, hash: u64) -> Option<&Vec<Rect>> {
self.layout_cache.get(&hash)
}
pub fn set_layout(&mut self, hash: u64, chunks: Vec<Rect>) {
self.layout_cache.insert(hash, chunks);
}
pub fn render_buffer(&mut self, buffer: Buffer) {
let mut string = String::with_capacity(buffer.area().area() as usize);
for (i, cell) in buffer.content().iter().enumerate() {
let (lx, ly) = buffer.pos_of(i).unwrap();
let (x, y) = (lx + buffer.area().x, ly + buffer.area().y);
if cell.symbol != "" {
write!(self.stdout,
"{}{}{}{}",
termion::cursor::Goto(x + 1, y + 1),
cell.fg.fg(),
cell.bg.bg(),
cell.symbol)
.unwrap();
string.push_str(&format!("{}{}{}{}",
termion::cursor::Goto(x + 1, y + 1),
cell.fg.fg(),
cell.bg.bg(),
cell.symbol))
}
}
write!(self.stdout, "{}", string);
self.stdout.flush().unwrap();
}
pub fn clear(&mut self) {

View file

@ -3,9 +3,10 @@ use std::hash::{Hash, Hasher, BuildHasher};
use layout::Rect;
pub fn hash<T: Hash>(t: &T) -> u64 {
pub fn hash<T: Hash>(t: &T, area: &Rect) -> u64 {
let state = RandomState::new();
let mut hasher = state.build_hasher();
t.hash(&mut hasher);
area.hash(&mut hasher);
hasher.finish()
}

View file

@ -138,7 +138,6 @@ impl<'a> Widget<'a> for Chart<'a> {
let margin_x = chart_area.x - area.x;
let margin_y = chart_area.y - area.y;
// info!("{:?}", self.datasets[0].data[0]);
for dataset in self.datasets {
for &(x, y) in dataset.data.iter() {
@ -150,7 +149,6 @@ impl<'a> Widget<'a> for Chart<'a> {
(self.y_axis.bounds[1] - self.y_axis.bounds[0]);
let dx = (self.x_axis.bounds[1] - x) * (chart_area.width - 1) as f64 /
(self.x_axis.bounds[1] - self.x_axis.bounds[0]);
info!("{} {}", dx, dy);
buf.update_cell(dx as u16 + margin_x, dy as u16 + margin_y, |c| {
c.symbol = symbols::DOT;
c.fg = dataset.color;