profiling motivated perf improvements in build draw commands

macos-click-through
Keith Simmons 5 years ago
parent 792568774b
commit 31a1bd5cdc

@ -1,4 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use skulpin::skia_safe::Color4f; use skulpin::skia_safe::Color4f;
use super::style::{Style, Colors}; use super::style::{Style, Colors};
@ -39,7 +41,7 @@ pub struct Cursor {
pub blinkwait: Option<u64>, pub blinkwait: Option<u64>,
pub blinkon: Option<u64>, pub blinkon: Option<u64>,
pub blinkoff: Option<u64>, pub blinkoff: Option<u64>,
pub style: Option<Style>, pub style: Option<Arc<Style>>,
pub enabled: bool, pub enabled: bool,
pub mode_list: Vec<CursorMode> pub mode_list: Vec<CursorMode>
} }
@ -75,7 +77,7 @@ impl Cursor {
} }
} }
pub fn change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Style>) { pub fn change_mode(&mut self, mode_index: u64, styles: &HashMap<u64, Arc<Style>>) {
if let Some(CursorMode { shape, style_id, cell_percentage, blinkwait, blinkon, blinkoff }) = self.mode_list.get(mode_index as usize) { if let Some(CursorMode { shape, style_id, cell_percentage, blinkwait, blinkon, blinkoff }) = self.mode_list.get(mode_index as usize) {
if let Some(shape) = shape { if let Some(shape) = shape {
self.shape = shape.clone(); self.shape = shape.clone();

@ -17,13 +17,14 @@ lazy_static! {
pub static ref EDITOR: Arc<Mutex<Editor>> = Arc::new(Mutex::new(Editor::new())); pub static ref EDITOR: Arc<Mutex<Editor>> = Arc::new(Mutex::new(Editor::new()));
} }
pub type GridCell = Option<(String, Option<Style>)>; pub type GridCell = Option<(String, Option<Arc<Style>>)>;
#[derive(new, Debug, Clone)] #[derive(new, Debug, Clone)]
pub struct DrawCommand { pub struct DrawCommand {
pub text: String, pub text: String,
pub cell_width: u64,
pub grid_position: (u64, u64), pub grid_position: (u64, u64),
pub style: Option<Style>, pub style: Option<Arc<Style>>,
#[new(value = "1")] #[new(value = "1")]
pub scale: u16 pub scale: u16
} }
@ -38,9 +39,9 @@ pub struct Editor {
pub font_name: Option<String>, pub font_name: Option<String>,
pub font_size: Option<f32>, pub font_size: Option<f32>,
pub cursor: Cursor, pub cursor: Cursor,
pub default_colors: Colors, pub default_style: Arc<Style>,
pub defined_styles: HashMap<u64, Style>, pub defined_styles: HashMap<u64, Arc<Style>>,
pub previous_style: Option<Style> pub previous_style: Option<Arc<Style>>
} }
impl Editor { impl Editor {
@ -51,11 +52,11 @@ impl Editor {
should_clear: true, should_clear: true,
title: "Neovide".to_string(), title: "Neovide".to_string(),
cursor: Cursor::new(),
size: INITIAL_DIMENSIONS, size: INITIAL_DIMENSIONS,
font_name: None, font_name: None,
font_size: None, font_size: None,
default_colors: Colors::new(Some(colors::WHITE), Some(colors::BLACK), Some(colors::GREY)), cursor: Cursor::new(),
default_style: Arc::new(Style::new(Colors::new(Some(colors::WHITE), Some(colors::BLACK), Some(colors::GREY)))),
defined_styles: HashMap::new(), defined_styles: HashMap::new(),
previous_style: None previous_style: None
}; };
@ -74,8 +75,8 @@ impl Editor {
RedrawEvent::BusyStop => self.cursor.enabled = true, RedrawEvent::BusyStop => self.cursor.enabled = true,
RedrawEvent::Flush => REDRAW_SCHEDULER.queue_next_frame(), RedrawEvent::Flush => REDRAW_SCHEDULER.queue_next_frame(),
RedrawEvent::Resize { width, height, .. } => self.resize((width, height)), RedrawEvent::Resize { width, height, .. } => self.resize((width, height)),
RedrawEvent::DefaultColorsSet { colors } => self.default_colors = colors, RedrawEvent::DefaultColorsSet { colors } => self.default_style = Arc::new(Style::new(colors)),
RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, style); }, RedrawEvent::HighlightAttributesDefine { id, style } => { self.defined_styles.insert(id, Arc::new(style)); },
RedrawEvent::GridLine { row, column_start, cells, .. } => self.draw_grid_line(row, column_start, cells), RedrawEvent::GridLine { row, column_start, cells, .. } => self.draw_grid_line(row, column_start, cells),
RedrawEvent::Clear { .. } => self.clear(), RedrawEvent::Clear { .. } => self.clear(),
RedrawEvent::CursorGoto { row, column, .. } => self.cursor.position = (row, column), RedrawEvent::CursorGoto { row, column, .. } => self.cursor.position = (row, column),
@ -95,24 +96,27 @@ impl Editor {
} }
} }
fn command_matches(command: &Option<DrawCommand>, style: &Option<Style>) -> bool { fn command_matches(command: &Option<DrawCommand>, style: &Option<Arc<Style>>) -> bool {
match command { match command {
Some(command) => &command.style == style, Some(command) => &command.style == style,
None => true None => true
} }
} }
fn add_character(command: &mut Option<DrawCommand>, character: &str, row_index: u64, col_index: u64, style: Option<Style>) { fn add_character(command: &mut Option<DrawCommand>, character: &str, row_index: u64, col_index: u64, style: Option<Arc<Style>>) {
match command { match command {
Some(command) => command.text.push_str(character), Some(command) => {
command.text.push_str(character);
command.cell_width += 1;
},
None => { None => {
command.replace(DrawCommand::new(character.to_string(), (col_index, row_index), style)); command.replace(DrawCommand::new(character.to_string(), 1, (col_index, row_index), style));
} }
} }
} }
for (col_index, cell) in row.iter().enumerate() { for (col_index, cell) in row.iter().enumerate() {
let (character, style) = cell.clone().unwrap_or_else(|| (' '.to_string(), Some(Style::new(self.default_colors.clone())))); if let Some((character, style)) = cell {
if character.is_empty() { if character.is_empty() {
add_character(&mut command, &" ", row_index as u64, col_index as u64, style.clone()); add_character(&mut command, &" ", row_index as u64, col_index as u64, style.clone());
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
@ -124,6 +128,13 @@ impl Editor {
} }
add_character(&mut command, &character, row_index as u64, col_index as u64, style.clone()); add_character(&mut command, &character, row_index as u64, col_index as u64, style.clone());
} }
} else {
if !command_matches(&command, &None) {
add_command(&mut draw_commands, command);
command = None;
}
add_character(&mut command, " ", row_index as u64, col_index as u64, None);
}
} }
add_command(&mut draw_commands, command); add_command(&mut draw_commands, command);
} }
@ -133,8 +144,10 @@ impl Editor {
let (x, y) = command.grid_position; let (x, y) = command.grid_position;
let dirty_row = &self.dirty[y as usize]; let dirty_row = &self.dirty[y as usize];
for char_index in 0..command.text.graphemes(true).count() { let min = (x as i64 - 1).max(0) as u64;
if dirty_row[x as usize + char_index] { let max = (x + command.cell_width + 1).min(dirty_row.len() as u64);
for char_index in min..max {
if dirty_row[char_index as usize] {
return true; return true;
} }
} }

@ -1,3 +1,5 @@
use std::sync::Arc;
use skulpin::CoordinateSystemHelper; use skulpin::CoordinateSystemHelper;
use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors}; use skulpin::skia_safe::{Canvas, Paint, Surface, Budgeted, Rect, colors};
use skulpin::skia_safe::gpu::SurfaceOrigin; use skulpin::skia_safe::gpu::SurfaceOrigin;
@ -42,44 +44,44 @@ impl Renderer {
self.font_height = font_height; self.font_height = font_height;
} }
fn compute_text_region(&self, text: &str, grid_pos: (u64, u64), size: u16) -> Rect { fn compute_text_region(&self, text: &str, grid_pos: (u64, u64), cell_width: u64, size: u16) -> Rect {
let (grid_x, grid_y) = grid_pos; let (grid_x, grid_y) = grid_pos;
let x = grid_x as f32 * self.font_width; let x = grid_x as f32 * self.font_width;
let y = grid_y as f32 * self.font_height; let y = grid_y as f32 * self.font_height;
let width = text.graphemes(true).count() as f32 * self.font_width * size as f32; let width = cell_width as f32 * self.font_width * size as f32;
let height = self.font_height * size as f32; let height = self.font_height * size as f32;
Rect::new(x, y, x + width, y + height) Rect::new(x, y, x + width, y + height)
} }
fn draw_background(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), size: u16, style: &Option<Style>, default_colors: &Colors) { fn draw_background(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), cell_width:u64, size: u16, style: &Option<Arc<Style>>, default_style: &Arc<Style>) {
let region = self.compute_text_region(text, grid_pos, size); let region = self.compute_text_region(text, grid_pos, cell_width, size);
let style = style.clone().unwrap_or_else(|| Style::new(default_colors.clone())); let style = style.as_ref().unwrap_or(default_style);
self.paint.set_color(style.background(default_colors).to_color()); self.paint.set_color(style.background(&default_style.colors).to_color());
canvas.draw_rect(region, &self.paint); canvas.draw_rect(region, &self.paint);
} }
fn draw_foreground(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), size: u16, style: &Option<Style>, default_colors: &Colors) { fn draw_foreground(&mut self, canvas: &mut Canvas, text: &str, grid_pos: (u64, u64), cell_width: u64, size: u16, style: &Option<Arc<Style>>, default_style: &Arc<Style>) {
let (grid_x, grid_y) = grid_pos; let (grid_x, grid_y) = grid_pos;
let x = grid_x as f32 * self.font_width; let x = grid_x as f32 * self.font_width;
let y = grid_y as f32 * self.font_height; let y = grid_y as f32 * self.font_height;
let width = text.graphemes(true).count() as f32 * self.font_width; let width = cell_width as f32 * self.font_width;
let style = style.clone().unwrap_or_else(|| Style::new(default_colors.clone())); let style = style.as_ref().unwrap_or(default_style);
canvas.save(); canvas.save();
let region = self.compute_text_region(text, grid_pos, size); let region = self.compute_text_region(text, grid_pos, cell_width, size);
canvas.clip_rect(region, None, Some(false)); canvas.clip_rect(region, None, Some(false));
if style.underline || style.undercurl { if style.underline || style.undercurl {
let line_position = self.shaper.underline_position(); let line_position = self.shaper.underline_position();
self.paint.set_color(style.special(&default_colors).to_color()); self.paint.set_color(style.special(&default_style.colors).to_color());
canvas.draw_line((x, y - line_position + self.font_height), (x + width, y - line_position + self.font_height), &self.paint); canvas.draw_line((x, y - line_position + self.font_height), (x + width, y - line_position + self.font_height), &self.paint);
} }
self.paint.set_color(style.foreground(&default_colors).to_color()); self.paint.set_color(style.foreground(&default_style.colors).to_color());
let text = text.trim_end(); let text = text.trim_end();
if !text.is_empty() { if !text.is_empty() {
for blob in self.shaper.shape_cached(text, style.bold, style.italic).iter() { for blob in self.shaper.shape_cached(text, style.bold, style.italic).iter() {
@ -89,7 +91,7 @@ impl Renderer {
if style.strikethrough { if style.strikethrough {
let line_position = region.center_y(); let line_position = region.center_y();
self.paint.set_color(style.special(&default_colors).to_color()); self.paint.set_color(style.special(&default_style.colors).to_color());
canvas.draw_line((x, line_position), (x + width, line_position), &self.paint); canvas.draw_line((x, line_position), (x + width, line_position), &self.paint);
} }
@ -97,11 +99,11 @@ impl Renderer {
} }
pub fn draw(&mut self, gpu_canvas: &mut Canvas, coordinate_system_helper: &CoordinateSystemHelper) -> bool { pub fn draw(&mut self, gpu_canvas: &mut Canvas, coordinate_system_helper: &CoordinateSystemHelper) -> bool {
let ((draw_commands, should_clear), default_colors, cursor, font_name, font_size) = { let ((draw_commands, should_clear), default_style, cursor, font_name, font_size) = {
let mut editor = EDITOR.lock().unwrap(); let mut editor = EDITOR.lock().unwrap();
( (
editor.build_draw_commands(), editor.build_draw_commands(),
editor.default_colors.clone(), editor.default_style.clone(),
editor.cursor.clone(), editor.cursor.clone(),
editor.font_name.clone(), editor.font_name.clone(),
editor.font_size editor.font_size
@ -126,7 +128,7 @@ impl Renderer {
let surface_origin = SurfaceOrigin::TopLeft; let surface_origin = SurfaceOrigin::TopLeft;
let mut surface = Surface::new_render_target(&mut context, budgeted, &image_info, None, surface_origin, None, None).expect("Could not create surface"); let mut surface = Surface::new_render_target(&mut context, budgeted, &image_info, None, surface_origin, None, None).expect("Could not create surface");
let canvas = surface.canvas(); let canvas = surface.canvas();
canvas.clear(default_colors.background.clone().unwrap().to_color()); canvas.clear(default_style.colors.background.clone().unwrap().to_color());
surface surface
}); });
@ -134,10 +136,10 @@ impl Renderer {
coordinate_system_helper.use_logical_coordinates(&mut canvas); coordinate_system_helper.use_logical_coordinates(&mut canvas);
for command in draw_commands.iter() { for command in draw_commands.iter() {
self.draw_background(&mut canvas, &command.text, command.grid_position.clone(), command.scale, &command.style, &default_colors); self.draw_background(&mut canvas, &command.text, command.grid_position.clone(), command.cell_width, command.scale, &command.style, &default_style);
} }
for command in draw_commands.iter() { for command in draw_commands.iter() {
self.draw_foreground(&mut canvas, &command.text, command.grid_position.clone(), command.scale, &command.style, &default_colors); self.draw_foreground(&mut canvas, &command.text, command.grid_position.clone(), command.cell_width, command.scale, &command.style, &default_style);
} }
let image = surface.image_snapshot(); let image = surface.image_snapshot();
@ -148,7 +150,7 @@ impl Renderer {
self.surface = Some(surface); self.surface = Some(surface);
self.cursor_renderer.draw( self.cursor_renderer.draw(
cursor, &default_colors, cursor, &default_style.colors,
self.font_width, self.font_height, self.font_width, self.font_height,
&mut self.paint, &mut self.shaper, &mut self.paint, &mut self.shaper,
gpu_canvas); gpu_canvas);

Loading…
Cancel
Save