From 4fcea59929f5e87c68e8ae83ce43124baadc675b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 11 May 2020 13:11:01 +0200 Subject: [PATCH] Optimize meshing --- emigui/src/containers/collapsing_header.rs | 8 +-- emigui/src/containers/resize.rs | 2 +- emigui/src/math.rs | 9 ++++ emigui/src/mesher.rs | 63 ++++++++++++++++------ emigui/src/types.rs | 13 +++-- emigui/src/ui.rs | 22 ++++---- emigui/src/widgets.rs | 16 +++--- emigui/src/widgets/text_edit.rs | 2 +- 8 files changed, 89 insertions(+), 46 deletions(-) diff --git a/emigui/src/containers/collapsing_header.rs b/emigui/src/containers/collapsing_header.rs index ba5c5003..edf89d25 100644 --- a/emigui/src/containers/collapsing_header.rs +++ b/emigui/src/containers/collapsing_header.rs @@ -157,8 +157,8 @@ fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) { )); // Draw a minus: - ui.add_paint_cmd(PaintCmd::Line { - points: vec![ + ui.add_paint_cmd(PaintCmd::LineSegment { + points: [ pos2(small_icon_rect.left(), small_icon_rect.center().y), pos2(small_icon_rect.right(), small_icon_rect.center().y), ], @@ -168,8 +168,8 @@ fn paint_icon(ui: &mut Ui, state: &State, interact: &InteractInfo) { if !state.open { // Draw it as a plus: - ui.add_paint_cmd(PaintCmd::Line { - points: vec![ + ui.add_paint_cmd(PaintCmd::LineSegment { + points: [ pos2(small_icon_rect.center().x, small_icon_rect.top()), pos2(small_icon_rect.center().x, small_icon_rect.bottom()), ], diff --git a/emigui/src/containers/resize.rs b/emigui/src/containers/resize.rs index 9ec3eb5d..b3121955 100644 --- a/emigui/src/containers/resize.rs +++ b/emigui/src/containers/resize.rs @@ -256,7 +256,7 @@ fn paint_resize_corner(ui: &mut Ui, interact: &InteractInfo) { while w < 12.0 { ui.add_paint_cmd(PaintCmd::line_segment( - (pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)), + [pos2(corner.x - w, corner.y), pos2(corner.x, corner.y - w)], color, width, )); diff --git a/emigui/src/math.rs b/emigui/src/math.rs index cda60dc8..2f1c2a85 100644 --- a/emigui/src/math.rs +++ b/emigui/src/math.rs @@ -8,6 +8,7 @@ pub struct Vec2 { pub y: f32, } +#[inline(always)] pub fn vec2(x: f32, y: f32) -> Vec2 { Vec2 { x, y } } @@ -39,6 +40,7 @@ impl Vec2 { } } + #[inline(always)] pub fn rot90(self) -> Self { vec2(self.y, -self.x) } @@ -212,6 +214,13 @@ pub fn pos2(x: f32, y: f32) -> Pos2 { } impl Pos2 { + pub fn to_vec2(self) -> Vec2 { + Vec2 { + x: self.x, + y: self.y, + } + } + pub fn dist(self: Self, other: Self) -> f32 { (self - other).length() } diff --git a/emigui/src/mesher.rs b/emigui/src/mesher.rs index 14ade799..3175c028 100644 --- a/emigui/src/mesher.rs +++ b/emigui/src/mesher.rs @@ -147,6 +147,7 @@ impl Path { self.0.clear(); } + #[inline(always)] pub fn add_point(&mut self, pos: Pos2, normal: Vec2) { self.0.push(PathPoint { pos, normal }); } @@ -160,22 +161,33 @@ impl Path { } } + pub fn add_line_segment(&mut self, points: [Pos2; 2]) { + let normal = (points[1] - points[0]).normalized().rot90(); + self.add_point(points[0], normal); + self.add_point(points[1], normal); + } + pub fn add_line(&mut self, points: &[Pos2]) { let n = points.len(); assert!(n >= 2); - self.add_point(points[0], (points[1] - points[0]).normalized().rot90()); - for i in 1..n - 1 { - let n0 = (points[i] - points[i - 1]).normalized().rot90(); - let n1 = (points[i + 1] - points[i]).normalized().rot90(); - let v = (n0 + n1) / 2.0; - let normal = v / v.length_sq(); - self.add_point(points[i], normal); // TODO: handle VERY sharp turns better + if n == 2 { + // Common case optimization: + self.add_line_segment([points[0], points[1]]); + } else { + self.add_point(points[0], (points[1] - points[0]).normalized().rot90()); + for i in 1..n - 1 { + let n0 = (points[i] - points[i - 1]).normalized().rot90(); + let n1 = (points[i + 1] - points[i]).normalized().rot90(); + let v = (n0 + n1) / 2.0; + let normal = v / v.length_sq(); + self.add_point(points[i], normal); // TODO: handle VERY sharp turns better + } + self.add_point( + points[n - 1], + (points[n - 1] - points[n - 2]).normalized().rot90(), + ); } - self.add_point( - points[n - 1], - (points[n - 1] - points[n - 2]).normalized().rot90(), - ); } pub fn add_rectangle(&mut self, rect: Rect) { @@ -387,7 +399,16 @@ pub fn paint_path( // ---------------------------------------------------------------------------- -pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, out_mesh: &mut Mesh) { +/// path: only used to reuse memory +pub fn mesh_command( + path: &mut Path, + options: MesherOptions, + fonts: &Fonts, + command: PaintCmd, + out_mesh: &mut Mesh, +) { + path.clear(); + match command { PaintCmd::Circle { center, @@ -395,7 +416,6 @@ pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, ou outline, radius, } => { - let mut path = Path::default(); path.add_circle(center, radius); if let Some(color) = fill_color { fill_closed_path(out_mesh, options, &path.0, color); @@ -414,14 +434,21 @@ pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, ou PaintCmd::Mesh(mesh) => { out_mesh.append(&mesh); } - PaintCmd::Line { + PaintCmd::LineSegment { + points, + color, + width, + } => { + path.add_line_segment(points); + paint_path(out_mesh, options, Open, &path.0, color, width); + } + PaintCmd::LinePath { points, color, width, } => { let n = points.len(); if n >= 2 { - let mut path = Path::default(); path.add_line(&points); paint_path(out_mesh, options, Open, &path.0, color, width); } @@ -462,7 +489,6 @@ pub fn mesh_command(options: MesherOptions, fonts: &Fonts, command: PaintCmd, ou rect.min = rect.min.max(pos2(-1e7, -1e7)); rect.max = rect.max.min(pos2(1e7, 1e7)); - let mut path = Path::default(); path.add_rounded_rectangle(rect, corner_radius); if let Some(fill_color) = fill_color { fill_closed_path(out_mesh, options, &path.0, fill_color); @@ -512,6 +538,8 @@ pub fn mesh_paint_commands( fonts: &Fonts, commands: Vec<(Rect, PaintCmd)>, ) -> Vec<(Rect, Mesh)> { + let mut reused_path = Path::default(); + let mut batches = PaintBatches::default(); for (clip_rect, cmd) in commands { // TODO: cull(clip_rect, cmd) @@ -522,6 +550,7 @@ pub fn mesh_paint_commands( if options.debug_paint_clip_rects && !clip_rect.is_empty() { let out_mesh = &mut batches.last_mut().unwrap().1; mesh_command( + &mut reused_path, options, fonts, PaintCmd::Rect { @@ -536,7 +565,7 @@ pub fn mesh_paint_commands( } let out_mesh = &mut batches.last_mut().unwrap().1; - mesh_command(options, fonts, cmd, out_mesh); + mesh_command(&mut reused_path, options, fonts, cmd, out_mesh); } batches diff --git a/emigui/src/types.rs b/emigui/src/types.rs index 1978e069..34cf6ab9 100644 --- a/emigui/src/types.rs +++ b/emigui/src/types.rs @@ -78,7 +78,12 @@ pub enum PaintCmd { outline: Option, radius: f32, }, - Line { + LineSegment { + points: [Pos2; 2], + color: Color, + width: f32, + }, + LinePath { points: Vec, color: Color, width: f32, @@ -111,9 +116,9 @@ pub enum PaintCmd { } impl PaintCmd { - pub fn line_segment(seg: (Pos2, Pos2), color: Color, width: f32) -> Self { - Self::Line { - points: vec![seg.0, seg.1], + pub fn line_segment(points: [Pos2; 2], color: Color, width: f32) -> Self { + Self::LineSegment { + points, color, width, } diff --git a/emigui/src/ui.rs b/emigui/src/ui.rs index 6bb6b95a..c1bf6742 100644 --- a/emigui/src/ui.rs +++ b/emigui/src/ui.rs @@ -369,17 +369,17 @@ impl Ui { if too_wide { self.add_paint_cmd(PaintCmd::line_segment( - (rect.left_top(), rect.left_bottom()), + [rect.left_top(), rect.left_bottom()], color, width, )); self.add_paint_cmd(PaintCmd::line_segment( - (rect.left_center(), rect.right_center()), + [rect.left_center(), rect.right_center()], color, width, )); self.add_paint_cmd(PaintCmd::line_segment( - (rect.right_top(), rect.right_bottom()), + [rect.right_top(), rect.right_bottom()], color, width, )); @@ -387,17 +387,17 @@ impl Ui { if too_high { self.add_paint_cmd(PaintCmd::line_segment( - (rect.left_top(), rect.right_top()), + [rect.left_top(), rect.right_top()], color, width, )); self.add_paint_cmd(PaintCmd::line_segment( - (rect.center_top(), rect.center_bottom()), + [rect.center_top(), rect.center_bottom()], color, width, )); self.add_paint_cmd(PaintCmd::line_segment( - (rect.left_bottom(), rect.right_bottom()), + [rect.left_bottom(), rect.right_bottom()], color, width, )); @@ -594,11 +594,11 @@ impl Ui { let line_start = child_rect.min - indent * 0.5; let line_start = line_start.round(); // TODO: round to pixel instead let line_end = pos2(line_start.x, line_start.y + size.y - 8.0); - self.add_paint_cmd(PaintCmd::Line { - points: vec![line_start, line_end], - color: gray(150, 255), - width: self.style.line_width, - }); + self.add_paint_cmd(PaintCmd::line_segment( + [line_start, line_end], + gray(150, 255), + self.style.line_width, + )); self.reserve_space(indent + size, None) } diff --git a/emigui/src/widgets.rs b/emigui/src/widgets.rs index 9c8197cc..1e036c4f 100644 --- a/emigui/src/widgets.rs +++ b/emigui/src/widgets.rs @@ -160,11 +160,11 @@ impl Widget for Hyperlink { let y = ui.round_to_pixel(y); let min_x = pos.x + fragment.min_x(); let max_x = pos.x + fragment.max_x(); - ui.add_paint_cmd(PaintCmd::Line { - points: vec![pos2(min_x, y), pos2(max_x, y)], + ui.add_paint_cmd(PaintCmd::line_segment( + [pos2(min_x, y), pos2(max_x, y)], color, - width: ui.style().line_width, - }); + ui.style().line_width, + )); } } @@ -284,7 +284,7 @@ impl<'a> Widget for Checkbox<'a> { let stroke_color = ui.style().interact(&interact).stroke_color; if *self.checked { - ui.add_paint_cmd(PaintCmd::Line { + ui.add_paint_cmd(PaintCmd::LinePath { points: vec![ pos2(small_icon_rect.left(), small_icon_rect.center().y), pos2(small_icon_rect.center().x, small_icon_rect.bottom()), @@ -422,7 +422,7 @@ impl Widget for Separator { Direction::Horizontal => { let interact = ui.reserve_space(vec2(self.min_spacing, available_space.y), None); ( - vec![ + [ pos2(interact.rect.center().x, interact.rect.top() - extra), pos2(interact.rect.center().x, interact.rect.bottom() + extra), ], @@ -432,7 +432,7 @@ impl Widget for Separator { Direction::Vertical => { let interact = ui.reserve_space(vec2(available_space.x, self.min_spacing), None); ( - vec![ + [ pos2(interact.rect.left() - extra, interact.rect.center().y), pos2(interact.rect.right() + extra, interact.rect.center().y), ], @@ -440,7 +440,7 @@ impl Widget for Separator { ) } }; - ui.add_paint_cmd(PaintCmd::Line { + ui.add_paint_cmd(PaintCmd::LineSegment { points, color: self.color, width: self.line_width, diff --git a/emigui/src/widgets/text_edit.rs b/emigui/src/widgets/text_edit.rs index 8ce2002e..00889b34 100644 --- a/emigui/src/widgets/text_edit.rs +++ b/emigui/src/widgets/text_edit.rs @@ -96,7 +96,7 @@ impl<'t> Widget for TextEdit<'t> { interact.rect.min }; ui.add_paint_cmd(PaintCmd::line_segment( - (cursor_pos, cursor_pos + vec2(0.0, line_spacing)), + [cursor_pos, cursor_pos + vec2(0.0, line_spacing)], color::WHITE, ui.style().text_cursor_width, ));