Browse Source

优化渲染模块,采用局部渲染提高渲染速度 (#26)

GnoCiYeH 6 months ago
parent
commit
2e3ca1943a

+ 1 - 1
src/application/mode/insert.rs

@@ -12,7 +12,7 @@ impl ModeRenderer for InsertRenderer {
     fn render(
         workspace: &mut crate::workspace::Workspace,
         monitor: &mut crate::view::monitor::Monitor,
-        mode: &mut super::ModeData,
+        _mode: &mut super::ModeData,
     ) -> super::Result<()> {
         let mut presenter = monitor.build_presenter()?;
 

+ 4 - 1
src/view/monitor/mod.rs

@@ -2,7 +2,7 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc, sync::Arc};
 
 use super::{
     presenter::Presenter,
-    render::render_state::RenderState,
+    render::{render_buffer::CachedRenderBuffer, render_state::RenderState},
     terminal::{cross_terminal::CrossTerminal, Terminal},
     theme_loadler::ThemeLoader,
 };
@@ -23,11 +23,13 @@ pub struct Monitor {
     scroll_controllers: HashMap<usize, ScrollController>,
     render_caches: HashMap<usize, Rc<RefCell<HashMap<usize, RenderState>>>>,
     pub last_key: Option<KeyEvent>,
+    pub cached_render_buffer: Rc<RefCell<CachedRenderBuffer>>,
 }
 
 impl Monitor {
     pub fn new(perference: Rc<RefCell<dyn Perferences>>) -> Result<Monitor> {
         let terminal = CrossTerminal::new()?;
+        let cached_render_buffer = CachedRenderBuffer::new(terminal.width()?, terminal.height()?);
         let theme_set = ThemeLoader::new(perference.borrow().theme_path()?).load()?;
         Ok(Monitor {
             terminal: Arc::new(Box::new(terminal)),
@@ -36,6 +38,7 @@ impl Monitor {
             scroll_controllers: HashMap::new(),
             render_caches: HashMap::new(),
             last_key: None,
+            cached_render_buffer: Rc::new(RefCell::new(cached_render_buffer)),
         })
     }
 

+ 5 - 1
src/view/presenter.rs

@@ -36,7 +36,11 @@ impl<'a> Presenter<'a> {
                 .get_theme(&theme_name)
                 .ok_or_else(|| format!("Couldn't find \"{}\" theme", theme_name))?;
         }
-        let present_buffer = RenderBuffer::new(monitor.width()?, monitor.height()?);
+        let present_buffer = RenderBuffer::new(
+            monitor.width()?,
+            monitor.height()?,
+            monitor.cached_render_buffer.clone(),
+        );
         Ok(Presenter {
             view: monitor,
             theme,

+ 58 - 5
src/view/render/render_buffer.rs

@@ -24,18 +24,66 @@ impl<'c> Default for Cell<'c> {
     }
 }
 
+#[derive(Debug, Default, Clone)]
+pub struct CachedCell {
+    pub content: String,
+    pub colors: Colors,
+    pub style: CharStyle,
+}
+
+#[derive(Debug)]
+pub struct CachedRenderBuffer {
+    pub cells: Vec<CachedCell>,
+}
+
+impl CachedRenderBuffer {
+    pub fn new(width: usize, height: usize) -> CachedRenderBuffer {
+        CachedRenderBuffer {
+            cells: vec![CachedCell::default(); width * height],
+        }
+    }
+
+    // 返回对应index是否与cell相等
+    pub fn compare_and_update(&mut self, cell: &Cell, index: usize) -> bool {
+        if index < self.cells.len() {
+            let cache = &mut self.cells[index];
+            let cell_content = String::from_iter(cell.content.chars());
+            let equal = cache.colors == cell.colors
+                && cache.style == cell.style
+                && cache.content == cell_content;
+
+            if !equal {
+                cache.colors = cell.colors;
+                cache.style = cell.style;
+                cache.content = cell_content;
+            }
+
+            return equal;
+        } else {
+            return false;
+        }
+    }
+}
+
+#[derive(Debug)]
 pub struct RenderBuffer<'a> {
     width: usize,
     height: usize,
     cells: Vec<Cell<'a>>,
+    cached: Rc<RefCell<CachedRenderBuffer>>,
 }
 
 impl<'a> RenderBuffer<'a> {
-    pub fn new(width: usize, height: usize) -> RenderBuffer<'a> {
+    pub fn new(
+        width: usize,
+        height: usize,
+        cached: Rc<RefCell<CachedRenderBuffer>>,
+    ) -> RenderBuffer<'a> {
         RenderBuffer {
             width,
             height,
             cells: vec![Cell::default(); width * height],
+            cached,
         }
     }
 
@@ -60,6 +108,7 @@ pub struct RenderBufferIter<'a> {
     index: usize,
     width: usize,
     cells: &'a Vec<Cell<'a>>,
+    cached: Rc<RefCell<CachedRenderBuffer>>,
 }
 
 impl<'a> RenderBufferIter<'a> {
@@ -68,6 +117,7 @@ impl<'a> RenderBufferIter<'a> {
             index: 0,
             width: render_buffer.width,
             cells: &render_buffer.cells,
+            cached: render_buffer.cached.clone(),
         }
     }
 }
@@ -76,17 +126,20 @@ impl<'a> Iterator for RenderBufferIter<'a> {
     type Item = (Position, &'a Cell<'a>);
 
     fn next(&mut self) -> Option<Self::Item> {
-        if self.index < self.cells.len() {
+        while self.index < self.cells.len() {
             let position = Position {
                 line: self.index / self.width,
                 offset: self.index % self.width,
             };
+
+            let index = self.index;
             let cell = &self.cells[self.index];
             self.index += cell.content.graphemes(true).count().max(1);
 
-            Some((position, cell))
-        } else {
-            None
+            if !self.cached.borrow_mut().compare_and_update(cell, index) {
+                return Some((position, cell));
+            }
         }
+        None
     }
 }

+ 11 - 8
src/view/render/renderer.rs

@@ -5,7 +5,7 @@ use std::rc::Rc;
 use std::str::FromStr;
 
 use crate::errors::*;
-use crate::modules::perferences::{self, Perferences};
+use crate::modules::perferences::Perferences;
 use crate::util::position::Position;
 use crate::util::range::Range;
 use crate::view::colors::colors::Colors;
@@ -396,10 +396,7 @@ mod tests {
         rc::Rc,
     };
 
-    use syntect::{
-        highlighting::{Theme, ThemeSet},
-        parsing::SyntaxSet,
-    };
+    use syntect::{highlighting::ThemeSet, parsing::SyntaxSet};
 
     use crate::{
         buffer::Buffer,
@@ -407,7 +404,7 @@ mod tests {
         util::line_iterator::LineIterator,
         view::{
             colors::map::ColorMap,
-            render::render_buffer::RenderBuffer,
+            render::render_buffer::{CachedRenderBuffer, RenderBuffer},
             terminal::{cross_terminal::CrossTerminal, Terminal},
         },
     };
@@ -418,8 +415,14 @@ mod tests {
     fn test_display() {
         let terminal = CrossTerminal::new().unwrap();
         let mut buffer = Buffer::from_file(Path::new("src/main.rs")).unwrap();
-        let mut render_buffer =
-            RenderBuffer::new(terminal.width().unwrap(), terminal.height().unwrap());
+        let mut render_buffer = RenderBuffer::new(
+            terminal.width().unwrap(),
+            terminal.height().unwrap(),
+            Rc::new(RefCell::new(CachedRenderBuffer::new(
+                terminal.width().unwrap(),
+                terminal.height().unwrap(),
+            ))),
+        );
         let perferences = DummyPerferences;
         let cached_render_state = Rc::new(RefCell::new(HashMap::new()));
 

+ 9 - 2
src/view/terminal/cross_terminal.rs

@@ -42,7 +42,11 @@ impl CrossTerminal {
         ))?;
 
         match char_style {
-            crate::view::style::CharStyle::Default => {}
+            crate::view::style::CharStyle::Default => {
+                self.buffer().queue(crossterm::style::SetAttribute(
+                    crossterm::style::Attribute::Reset,
+                ))?;
+            }
             crate::view::style::CharStyle::Bold => {
                 self.buffer().queue(crossterm::style::SetAttribute(
                     crossterm::style::Attribute::Bold,
@@ -61,7 +65,9 @@ impl CrossTerminal {
         }
 
         match colors {
-            crate::view::colors::colors::Colors::Default => {}
+            crate::view::colors::colors::Colors::Default => {
+                self.buffer().queue(crossterm::style::ResetColor)?;
+            }
             crate::view::colors::colors::Colors::CustomForeground(color) => {
                 self.buffer()
                     .queue(crossterm::style::SetForegroundColor(color))?;
@@ -102,6 +108,7 @@ impl Terminal for CrossTerminal {
 
     fn present(&self) -> Result<()> {
         stdout().write_all(&self.buffer())?;
+        stdout().flush()?;
         self.buffer().clear();
         Ok(())
     }