Browse Source

[culling] coarse culling of text lines, circles and boxes

pull/28/head
Emil Ernerfeldt 4 years ago
parent
commit
b01690c7b8
  1. 1
      CHANGELOG.md
  2. 7
      egui/src/context.rs
  3. 8
      egui/src/math/rect.rs
  4. 70
      egui/src/paint/tessellator.rs

1
CHANGELOG.md

@ -6,6 +6,7 @@
* Unicode characters in labels (limited by [what the default font supports](https://fonts.google.com/specimen/Comfortaa#glyphs))
* Simple drop-down combo box menu
* Logarithmic sliders
* Optimization: coarse culling in the tesselator
## 0.1.4 - 2020-09-08

7
egui/src/context.rs

@ -618,13 +618,18 @@ impl Context {
impl paint::PaintOptions {
pub fn ui(&mut self, ui: &mut Ui) {
let Self {
anti_alias,
aa_size: _,
anti_alias,
coarse_tessellation_culling,
debug_paint_clip_rects,
debug_ignore_clip_rects,
} = self;
use crate::widgets::*;
ui.add(Checkbox::new(anti_alias, "Antialias"));
ui.add(Checkbox::new(
coarse_tessellation_culling,
"Do coarse culling in the tessellator",
));
ui.add(Checkbox::new(
debug_paint_clip_rects,
"Paint clip rectangles (debug)",

8
egui/src/math/rect.rs

@ -93,6 +93,14 @@ impl Rect {
}
}
#[must_use]
pub fn intersects(self, other: Rect) -> bool {
self.min.x <= other.max.x
&& other.min.x <= self.max.x
&& self.min.y <= other.max.y
&& other.min.y <= self.max.y
}
/// keep min
pub fn set_width(&mut self, w: f32) {
self.max.x = self.min.x + w;

70
egui/src/paint/tessellator.rs

@ -432,10 +432,12 @@ use self::PathType::{Closed, Open};
/// Tesselation quality options
#[derive(Clone, Copy)]
pub struct PaintOptions {
/// Anti-aliasing makes shapes appear smoother, but requires more triangles and is therefore slower.
pub anti_alias: bool,
/// Size of a pixel in points, e.g. 0.5
pub aa_size: f32,
/// Anti-aliasing makes shapes appear smoother, but requires more triangles and is therefore slower.
pub anti_alias: bool,
/// If `true` (default) cull certain primitives before tessellating them
pub coarse_tessellation_culling: bool,
/// Output the clip rectangles to be painted?
pub debug_paint_clip_rects: bool,
/// If true, no clipping will be done
@ -445,10 +447,11 @@ pub struct PaintOptions {
impl Default for PaintOptions {
fn default() -> Self {
Self {
anti_alias: true,
aa_size: 1.0,
anti_alias: true,
debug_paint_clip_rects: false,
debug_ignore_clip_rects: false,
coarse_tessellation_culling: true,
}
}
}
@ -649,6 +652,7 @@ fn mul_color(color: Srgba, factor: f32) -> Srgba {
/// * `scratchpad_path`: if you plan to run `tessellate_paint_command`
/// many times, pass it a reference to the same `Path` to avoid excessive allocations.
fn tessellate_paint_command(
clip_rect: Rect,
command: PaintCmd,
options: PaintOptions,
fonts: &Fonts,
@ -667,11 +671,19 @@ fn tessellate_paint_command(
fill,
stroke,
} => {
if radius > 0.0 {
path.add_circle(center, radius);
fill_closed_path(&path.0, fill, options, out);
stroke_path(&path.0, Closed, stroke, options, out);
if radius <= 0.0 {
return;
}
if options.coarse_tessellation_culling
&& !clip_rect.expand(radius + stroke.width).contains(center)
{
return;
}
path.add_circle(center, radius);
fill_closed_path(&path.0, fill, options, out);
stroke_path(&path.0, Closed, stroke, options, out);
}
PaintCmd::Triangles(triangles) => {
out.append(&triangles);
@ -710,17 +722,25 @@ fn tessellate_paint_command(
fill,
stroke,
} => {
if !rect.is_empty() {
// It is common to (sometimes accidentally) create an infinitely sized rectangle.
// Make sure we can handle that:
rect.min = rect.min.at_least(pos2(-1e7, -1e7));
rect.max = rect.max.at_most(pos2(1e7, 1e7));
path::rounded_rectangle(scratchpad_points, rect, corner_radius);
path.add_line_loop(scratchpad_points);
fill_closed_path(&path.0, fill, options, out);
stroke_path(&path.0, Closed, stroke, options, out);
if rect.is_empty() {
return;
}
if options.coarse_tessellation_culling
&& !rect.expand(stroke.width).intersects(clip_rect)
{
return;
}
// It is common to (sometimes accidentally) create an infinitely sized rectangle.
// Make sure we can handle that:
rect.min = rect.min.at_least(pos2(-1e7, -1e7));
rect.max = rect.max.at_most(pos2(1e7, 1e7));
path::rounded_rectangle(scratchpad_points, rect, corner_radius);
path.add_line_loop(scratchpad_points);
fill_closed_path(&path.0, fill, options, out);
stroke_path(&path.0, Closed, stroke, options, out);
}
PaintCmd::Text {
pos,
@ -742,11 +762,25 @@ fn tessellate_paint_command(
let text_offset = vec2(0.0, 1.0); // Eye-balled for buttons. TODO: why is this needed?
let clip_rect = clip_rect.expand(2.0); // Some fudge to handle letter slightly larger than expected.
let font = &fonts[text_style];
let mut chars = galley.text.chars();
for line in &galley.lines {
let line_min_y = pos.y + line.y_min + text_offset.x;
let line_max_y = line_min_y + font.height();
let is_line_visible =
line_max_y >= clip_rect.min.y && line_min_y <= clip_rect.max.y;
for x_offset in line.x_offsets.iter().take(line.x_offsets.len() - 1) {
let c = chars.next().unwrap();
if options.coarse_tessellation_culling && !is_line_visible {
// culling individual lines of text is important, since a single `PaintCmd::Text`
// can span hundreds of lines.
continue;
}
if let Some(glyph) = font.uv_rect(c) {
let mut left_top =
pos + glyph.offset + vec2(*x_offset, line.y_min) + text_offset;
@ -805,6 +839,7 @@ pub fn tessellate_paint_commands(
let out = &mut jobs.last_mut().unwrap().1;
tessellate_paint_command(
clip_rect,
cmd,
options,
fonts,
@ -817,6 +852,7 @@ pub fn tessellate_paint_commands(
if options.debug_paint_clip_rects {
for (clip_rect, triangles) in &mut jobs {
tessellate_paint_command(
Rect::everything(),
PaintCmd::Rect {
rect: *clip_rect,
corner_radius: 0.0,

Loading…
Cancel
Save