Skip to content

Commit

Permalink
Parallell tessellation (emilk#3934)
Browse files Browse the repository at this point in the history
* Part of https://github.com/emilk/egui/issues/1485

This adds a `rayon` feature to `epaint` and `egui` to parallelize
tessellation of large shapes, such as high-resolution plot lines.
  • Loading branch information
emilk authored and hacknus committed Oct 30, 2024
1 parent 5f9e5b4 commit 038c400
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 102 deletions.
46 changes: 42 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion crates/egui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,12 @@ persistence = ["serde", "epaint/serde", "ron"]
## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = ["dep:puffin"]
puffin = ["dep:puffin", "epaint/puffin"]

## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).
##
## This can help performance for graphics-intense applications.
rayon = ["epaint/rayon"]

## Allow serialization using [`serde`](https://docs.rs/serde).
serde = ["dep:serde", "epaint/serde", "accesskit?/serde"]
Expand Down
4 changes: 2 additions & 2 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1977,13 +1977,13 @@ impl Context {
let paint_stats = PaintStats::from_shapes(&shapes);
let clipped_primitives = {
crate::profile_scope!("tessellator::tessellate_shapes");
tessellator::tessellate_shapes(
tessellator::Tessellator::new(
pixels_per_point,
tessellation_options,
font_tex_size,
prepared_discs,
shapes,
)
.tessellate_shapes(shapes)
};
ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives);
clipped_primitives
Expand Down
8 changes: 8 additions & 0 deletions crates/egui/src/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ impl Widget for &mut epaint::TessellationOptions {
debug_ignore_clip_rects,
bezier_tolerance,
epsilon: _,
parallel_tessellation,
validate_meshes,
} = self;

ui.checkbox(feathering, "Feathering (antialias)")
Expand Down Expand Up @@ -176,6 +178,12 @@ impl Widget for &mut epaint::TessellationOptions {
ui.checkbox(debug_paint_clip_rects, "Paint clip rectangles");
ui.checkbox(debug_paint_text_rects, "Paint text bounds");
});

ui.add_enabled(epaint::HAS_RAYON, crate::Checkbox::new(parallel_tessellation, "Parallelize tessellation")
).on_hover_text("Only available if epaint was compiled with the rayon feature")
.on_disabled_hover_text("epaint was not compiled with the rayon feature");

ui.checkbox(validate_meshes, "Validate meshes").on_hover_text("Check that incoming meshes are valid, i.e. that all indices are in range, etc.");
})
.response
}
Expand Down
12 changes: 12 additions & 0 deletions crates/epaint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ log = ["dep:log"]
## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra).
mint = ["emath/mint"]

## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate.
##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers.
puffin = ["dep:puffin"]

## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon).
##
## This can help performance for graphics-intense applications.
rayon = ["dep:rayon"]

## Allow serialization using [`serde`](https://docs.rs/serde).
serde = ["dep:serde", "ahash/serde", "emath/serde", "ecolor/serde"]

Expand All @@ -88,6 +98,8 @@ bytemuck = { version = "1.7.2", optional = true, features = ["derive"] }
document-features = { version = "0.2", optional = true }

log = { version = "0.4", optional = true, features = ["std"] }
puffin = { workspace = true, optional = true }
rayon = { version = "1.7", optional = true }

## Allow serialization using [`serde`](https://docs.rs/serde) .
serde = { version = "1", optional = true, features = ["derive", "rc"] }
Expand Down
6 changes: 3 additions & 3 deletions crates/epaint/benches/benchmark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ fn tessellate_circles(c: &mut Criterion) {
let prepared_discs = atlas.prepared_discs();

b.iter(|| {
let clipped_primitive = tessellate_shapes(
let mut tessellator = Tessellator::new(
pixels_per_point,
options,
font_tex_size,
prepared_discs.clone(),
clipped_shapes.clone(),
);
black_box(clipped_primitive);
let clipped_primitives = tessellator.tessellate_shapes(clipped_shapes.clone());
black_box(clipped_primitives);
});
});
}
Expand Down
40 changes: 39 additions & 1 deletion crates/epaint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,16 @@ pub use {
},
stats::PaintStats,
stroke::Stroke,
tessellator::{tessellate_shapes, TessellationOptions, Tessellator},
tessellator::{TessellationOptions, Tessellator},
text::{FontFamily, FontId, Fonts, Galley},
texture_atlas::TextureAtlas,
texture_handle::TextureHandle,
textures::TextureManager,
};

#[allow(deprecated)]
pub use tessellator::tessellate_shapes;

pub use ecolor::{Color32, Hsva, HsvaGamma, Rgba};
pub use emath::{pos2, vec2, Pos2, Rect, Vec2};

Expand Down Expand Up @@ -172,3 +175,38 @@ pub(crate) fn f64_hash<H: std::hash::Hasher>(state: &mut H, f: f64) {
f.to_bits().hash(state);
}
}

// ---------------------------------------------------------------------------

/// Was epaint compiled with the `rayon` feature?
pub const HAS_RAYON: bool = cfg!(feature = "rayon");

// ---------------------------------------------------------------------------

mod profiling_scopes {
#![allow(unused_macros)]
#![allow(unused_imports)]

/// Profiling macro for feature "puffin"
macro_rules! profile_function {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_function!($($arg)*);
};
}
pub(crate) use profile_function;

/// Profiling macro for feature "puffin"
macro_rules! profile_scope {
($($arg: tt)*) => {
#[cfg(feature = "puffin")]
#[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there.
puffin::profile_scope!($($arg)*);
};
}
pub(crate) use profile_scope;
}

#[allow(unused_imports)]
pub(crate) use profiling_scopes::*;
3 changes: 3 additions & 0 deletions crates/epaint/src/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ impl Mesh {

/// Are all indices within the bounds of the contained vertices?
pub fn is_valid(&self) -> bool {
crate::profile_function!();

if let Ok(n) = u32::try_from(self.vertices.len()) {
self.indices.iter().all(|&i| i < n)
} else {
Expand All @@ -106,6 +108,7 @@ impl Mesh {

/// Append all the indices and vertices of `other` to `self`.
pub fn append(&mut self, other: Self) {
crate::profile_function!();
crate::epaint_assert!(other.is_valid());

if self.is_empty() {
Expand Down
Loading

0 comments on commit 038c400

Please sign in to comment.