egui/egui_demo_lib/benches/benchmark.rs
Emil Ernerfeldt 7b18fab7a4
Optimize tessellation of filled circles (#1616)
When painting a scatter plot we sometimes want to paint hundreds of thousands of points (filled circles) on screen every frame.

In this PR the font texture atlas is pre-populated with some filled circled of various radii. These are then used when painting (small) filled circled, which means A LOT less triangles and vertices are generated for them.

In a new benchmark we can see a 10x speedup in circle tessellation, but the the real benefit comes in the painting of these circles: since we generate a lot less vertices, the backend painter has less to do.

In a real-life scenario with a lot of things being painted (including around 100k points) I saw tessellation go from 35ms -> 7ms and painting go from 45ms -> 1ms. This means the total frame time went from 80ms to 8ms, or a 10x speedup.
2022-05-10 19:31:19 +02:00

146 lines
4.7 KiB
Rust

use criterion::{criterion_group, criterion_main, Criterion};
use egui::epaint::TextShape;
use egui_demo_lib::LOREM_IPSUM_LONG;
pub fn criterion_benchmark(c: &mut Criterion) {
use egui::RawInput;
{
let ctx = egui::Context::default();
let mut demo_windows = egui_demo_lib::DemoWindows::default();
// The most end-to-end benchmark.
c.bench_function("demo_with_tessellate__realistic", |b| {
b.iter(|| {
let full_output = ctx.run(RawInput::default(), |ctx| {
demo_windows.ui(ctx);
});
ctx.tessellate(full_output.shapes)
});
});
c.bench_function("demo_no_tessellate", |b| {
b.iter(|| {
ctx.run(RawInput::default(), |ctx| {
demo_windows.ui(ctx);
})
});
});
let full_output = ctx.run(RawInput::default(), |ctx| {
demo_windows.ui(ctx);
});
c.bench_function("demo_only_tessellate", |b| {
b.iter(|| ctx.tessellate(full_output.shapes.clone()));
});
}
if false {
let ctx = egui::Context::default();
ctx.memory().set_everything_is_visible(true); // give us everything
let mut demo_windows = egui_demo_lib::DemoWindows::default();
c.bench_function("demo_full_no_tessellate", |b| {
b.iter(|| {
ctx.run(RawInput::default(), |ctx| {
demo_windows.ui(ctx);
})
});
});
}
{
let ctx = egui::Context::default();
let _ = ctx.run(RawInput::default(), |ctx| {
egui::CentralPanel::default().show(ctx, |ui| {
c.bench_function("label &str", |b| {
b.iter(|| {
ui.label("the quick brown fox jumps over the lazy dog");
});
});
c.bench_function("label format!", |b| {
b.iter(|| {
ui.label("the quick brown fox jumps over the lazy dog".to_owned());
});
});
});
});
}
{
let ctx = egui::Context::default();
ctx.begin_frame(RawInput::default());
egui::CentralPanel::default().show(&ctx, |ui| {
c.bench_function("Painter::rect", |b| {
let painter = ui.painter();
let rect = ui.max_rect();
b.iter(|| {
painter.rect(rect, 2.0, egui::Color32::RED, (1.0, egui::Color32::WHITE));
});
});
});
// Don't call `end_frame` to not have to drain the huge paint list
}
{
let pixels_per_point = 1.0;
let max_texture_side = 8 * 1024;
let wrap_width = 512.0;
let font_id = egui::FontId::default();
let color = egui::Color32::WHITE;
let fonts = egui::epaint::text::Fonts::new(
pixels_per_point,
max_texture_side,
egui::FontDefinitions::default(),
);
{
let mut locked_fonts = fonts.lock();
c.bench_function("text_layout_uncached", |b| {
b.iter(|| {
use egui::epaint::text::{layout, LayoutJob};
let job = LayoutJob::simple(
LOREM_IPSUM_LONG.to_owned(),
font_id.clone(),
color,
wrap_width,
);
layout(&mut locked_fonts.fonts, job.into())
});
});
}
c.bench_function("text_layout_cached", |b| {
b.iter(|| {
fonts.layout(
LOREM_IPSUM_LONG.to_owned(),
font_id.clone(),
color,
wrap_width,
)
});
});
let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), font_id, color, wrap_width);
let font_image_size = fonts.font_image_size();
let prepared_discs = fonts.texture_atlas().lock().prepared_discs();
let mut tessellator = egui::epaint::Tessellator::new(
1.0,
Default::default(),
font_image_size,
prepared_discs,
);
let mut mesh = egui::epaint::Mesh::default();
let text_shape = TextShape::new(egui::Pos2::ZERO, galley);
c.bench_function("tessellate_text", |b| {
b.iter(|| {
tessellator.tessellate_text(&text_shape, &mut mesh);
mesh.clear();
});
});
}
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);