Simplified the screen buffer writing.

Turns out the main reason it was so slow was because stdout isn't
buffered in Rust by default.  That actually makes a lot of sense,
but I'd forgotten.

Wrapping it in a BufWriter solves the performance issues much more
gracefully.
This commit is contained in:
Nathan Vegdahl 2018-01-01 03:10:32 -08:00
parent 1f62eb32a0
commit 2591aef6d4

View File

@ -1,7 +1,7 @@
use std; use std;
use std::cell::RefCell; use std::cell::RefCell;
use std::io; use std::io;
use std::io::Write; use std::io::{BufWriter, Write};
use super::smallstring::SmallString; use super::smallstring::SmallString;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
@ -12,7 +12,7 @@ use termion::color;
use termion::raw::{IntoRawMode, RawTerminal}; use termion::raw::{IntoRawMode, RawTerminal};
pub(crate) struct Screen { pub(crate) struct Screen {
out: RefCell<AlternateScreen<RawTerminal<io::Stdout>>>, out: RefCell<AlternateScreen<RawTerminal<BufWriter<io::Stdout>>>>,
buf: RefCell<Vec<Option<(Style, SmallString)>>>, buf: RefCell<Vec<Option<(Style, SmallString)>>>,
w: usize, w: usize,
h: usize, h: usize,
@ -25,7 +25,11 @@ impl Screen {
.take(w as usize * h as usize) .take(w as usize * h as usize)
.collect(); .collect();
Screen { Screen {
out: RefCell::new(AlternateScreen::from(io::stdout().into_raw_mode().unwrap())), out: RefCell::new(AlternateScreen::from(
BufWriter::with_capacity(1 << 14, io::stdout())
.into_raw_mode()
.unwrap(),
)),
buf: RefCell::new(buf), buf: RefCell::new(buf),
w: w as usize, w: w as usize,
h: h as usize, h: h as usize,
@ -56,39 +60,31 @@ impl Screen {
} }
pub(crate) fn present(&self) { pub(crate) fn present(&self) {
let mut out = self.out.borrow_mut();
let buf = self.buf.borrow(); let buf = self.buf.borrow();
// Double the minimum needed space, because of formatting characters and such. let mut last_style = Style(Color::Black, Color::Black);
let mut tmp_string = String::with_capacity(self.w * self.h * 2); write!(out, "{}", last_style).unwrap();
tmp_string.push_str(&format!("{}", termion::cursor::Goto(1, 1)));
// Write everything to the tmp_string first. // Write everything to the tmp_string first.
for y in 0..self.h { for y in 0..self.h {
let mut x = 0; let mut x = 0;
let mut last_style = Style(Color::Black, Color::Black); write!(out, "{}", termion::cursor::Goto(1, y as u16 + 2)).unwrap();
tmp_string.push_str(&format!("{}", last_style));
while x < self.w { while x < self.w {
if let Some((style, ref text)) = buf[y * self.w + x] { if let Some((style, ref text)) = buf[y * self.w + x] {
if style != last_style { if style != last_style {
tmp_string.push_str(&format!("{}", style)); write!(out, "{}", style).unwrap();
last_style = style; last_style = style;
} }
tmp_string.push_str(text); write!(out, "{}", text).unwrap();
x += 1; x += 1;
} else { } else {
x += 1; x += 1;
} }
} }
tmp_string.push_str(&format!("{}", termion::cursor::Goto(1, y as u16 + 2)));
} }
// Write tmp_string to the screen all at once. // Make sure everything is written out
write!(
self.out.borrow_mut(),
"{}{}",
termion::cursor::Goto(0, 0),
tmp_string,
).unwrap();
self.out.borrow_mut().flush().unwrap(); self.out.borrow_mut().flush().unwrap();
} }