[culling] coarse culling of text lines, circles and boxes
This commit is contained in:
parent
9874921d06
commit
b01690c7b8
4 changed files with 68 additions and 18 deletions
|
@ -6,6 +6,7 @@
|
||||||
* Unicode characters in labels (limited by [what the default font supports](https://fonts.google.com/specimen/Comfortaa#glyphs))
|
* Unicode characters in labels (limited by [what the default font supports](https://fonts.google.com/specimen/Comfortaa#glyphs))
|
||||||
* Simple drop-down combo box menu
|
* Simple drop-down combo box menu
|
||||||
* Logarithmic sliders
|
* Logarithmic sliders
|
||||||
|
* Optimization: coarse culling in the tesselator
|
||||||
|
|
||||||
## 0.1.4 - 2020-09-08
|
## 0.1.4 - 2020-09-08
|
||||||
|
|
||||||
|
|
|
@ -618,13 +618,18 @@ impl Context {
|
||||||
impl paint::PaintOptions {
|
impl paint::PaintOptions {
|
||||||
pub fn ui(&mut self, ui: &mut Ui) {
|
pub fn ui(&mut self, ui: &mut Ui) {
|
||||||
let Self {
|
let Self {
|
||||||
anti_alias,
|
|
||||||
aa_size: _,
|
aa_size: _,
|
||||||
|
anti_alias,
|
||||||
|
coarse_tessellation_culling,
|
||||||
debug_paint_clip_rects,
|
debug_paint_clip_rects,
|
||||||
debug_ignore_clip_rects,
|
debug_ignore_clip_rects,
|
||||||
} = self;
|
} = self;
|
||||||
use crate::widgets::*;
|
use crate::widgets::*;
|
||||||
ui.add(Checkbox::new(anti_alias, "Antialias"));
|
ui.add(Checkbox::new(anti_alias, "Antialias"));
|
||||||
|
ui.add(Checkbox::new(
|
||||||
|
coarse_tessellation_culling,
|
||||||
|
"Do coarse culling in the tessellator",
|
||||||
|
));
|
||||||
ui.add(Checkbox::new(
|
ui.add(Checkbox::new(
|
||||||
debug_paint_clip_rects,
|
debug_paint_clip_rects,
|
||||||
"Paint clip rectangles (debug)",
|
"Paint clip rectangles (debug)",
|
||||||
|
|
|
@ -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
|
/// keep min
|
||||||
pub fn set_width(&mut self, w: f32) {
|
pub fn set_width(&mut self, w: f32) {
|
||||||
self.max.x = self.min.x + w;
|
self.max.x = self.min.x + w;
|
||||||
|
|
|
@ -432,10 +432,12 @@ use self::PathType::{Closed, Open};
|
||||||
/// Tesselation quality options
|
/// Tesselation quality options
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct PaintOptions {
|
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
|
/// Size of a pixel in points, e.g. 0.5
|
||||||
pub aa_size: f32,
|
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?
|
/// Output the clip rectangles to be painted?
|
||||||
pub debug_paint_clip_rects: bool,
|
pub debug_paint_clip_rects: bool,
|
||||||
/// If true, no clipping will be done
|
/// If true, no clipping will be done
|
||||||
|
@ -445,10 +447,11 @@ pub struct PaintOptions {
|
||||||
impl Default for PaintOptions {
|
impl Default for PaintOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
anti_alias: true,
|
|
||||||
aa_size: 1.0,
|
aa_size: 1.0,
|
||||||
|
anti_alias: true,
|
||||||
debug_paint_clip_rects: false,
|
debug_paint_clip_rects: false,
|
||||||
debug_ignore_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`
|
/// * `scratchpad_path`: if you plan to run `tessellate_paint_command`
|
||||||
/// many times, pass it a reference to the same `Path` to avoid excessive allocations.
|
/// many times, pass it a reference to the same `Path` to avoid excessive allocations.
|
||||||
fn tessellate_paint_command(
|
fn tessellate_paint_command(
|
||||||
|
clip_rect: Rect,
|
||||||
command: PaintCmd,
|
command: PaintCmd,
|
||||||
options: PaintOptions,
|
options: PaintOptions,
|
||||||
fonts: &Fonts,
|
fonts: &Fonts,
|
||||||
|
@ -667,11 +671,19 @@ fn tessellate_paint_command(
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
} => {
|
} => {
|
||||||
if radius > 0.0 {
|
if radius <= 0.0 {
|
||||||
path.add_circle(center, radius);
|
return;
|
||||||
fill_closed_path(&path.0, fill, options, out);
|
|
||||||
stroke_path(&path.0, Closed, stroke, options, out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) => {
|
PaintCmd::Triangles(triangles) => {
|
||||||
out.append(&triangles);
|
out.append(&triangles);
|
||||||
|
@ -710,17 +722,25 @@ fn tessellate_paint_command(
|
||||||
fill,
|
fill,
|
||||||
stroke,
|
stroke,
|
||||||
} => {
|
} => {
|
||||||
if !rect.is_empty() {
|
if rect.is_empty() {
|
||||||
// It is common to (sometimes accidentally) create an infinitely sized rectangle.
|
return;
|
||||||
// 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 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 {
|
PaintCmd::Text {
|
||||||
pos,
|
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 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 font = &fonts[text_style];
|
||||||
let mut chars = galley.text.chars();
|
let mut chars = galley.text.chars();
|
||||||
for line in &galley.lines {
|
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) {
|
for x_offset in line.x_offsets.iter().take(line.x_offsets.len() - 1) {
|
||||||
let c = chars.next().unwrap();
|
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) {
|
if let Some(glyph) = font.uv_rect(c) {
|
||||||
let mut left_top =
|
let mut left_top =
|
||||||
pos + glyph.offset + vec2(*x_offset, line.y_min) + text_offset;
|
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;
|
let out = &mut jobs.last_mut().unwrap().1;
|
||||||
tessellate_paint_command(
|
tessellate_paint_command(
|
||||||
|
clip_rect,
|
||||||
cmd,
|
cmd,
|
||||||
options,
|
options,
|
||||||
fonts,
|
fonts,
|
||||||
|
@ -817,6 +852,7 @@ pub fn tessellate_paint_commands(
|
||||||
if options.debug_paint_clip_rects {
|
if options.debug_paint_clip_rects {
|
||||||
for (clip_rect, triangles) in &mut jobs {
|
for (clip_rect, triangles) in &mut jobs {
|
||||||
tessellate_paint_command(
|
tessellate_paint_command(
|
||||||
|
Rect::everything(),
|
||||||
PaintCmd::Rect {
|
PaintCmd::Rect {
|
||||||
rect: *clip_rect,
|
rect: *clip_rect,
|
||||||
corner_radius: 0.0,
|
corner_radius: 0.0,
|
||||||
|
|
Loading…
Reference in a new issue