mirror of
https://github.com/emilk/egui.git
synced 2026-06-27 15:13:12 -04:00
<!-- Please read the "Making a PR" section of [`CONTRIBUTING.md`](https://github.com/emilk/egui/blob/main/CONTRIBUTING.md) before opening a Pull Request! * Keep your PR:s small and focused. * The PR title is what ends up in the changelog, so make it descriptive! * If applicable, add a screenshot or gif. * If it is a non-trivial addition, consider adding a demo for it to `egui_demo_lib`, or a new example. * Do NOT open PR:s from your `master` branch, as that makes it hard for maintainers to test and add commits to your PR. * Remember to run `cargo fmt` and `cargo clippy`. * Open the PR as a draft until you have self-reviewed it and run `./scripts/check.sh`. * When you have addressed a PR comment, mark it as resolved. Please be patient! I will review your PR, but my time is limited! --> * Closes N/A * [x] I have followed the instructions in the PR template I'll probably come back to this and clean it up a bit. This PR reimplements ab_glyph's functionality on top of Skrifa, a somewhat lower-level font API that's being used in Chrome now. Skrifa doesn't perform rasterization itself, so I'm using [vello_cpu](https://github.com/linebender/vello) from the Linebender project for rasterization. It's still in its early days, but I believe it's already quite fast. It also supports color and gradient fills, so color emoji support will be easier. Skrifa also supports font hinting, which should make text look a bit nicer / less blurry. Here's the current ab_glyph rendering: <img width="1592" height="1068" alt="image" src="https://github.com/user-attachments/assets/2385b66e-23f8-4c6e-b8c2-ea90e0eea4e4" /> Here's Skrifa *without* hinting--it looks almost identical, but there are some subpixel differences, probably due to rasterizer behavior: <img width="1592" height="1068" alt="image" src="https://github.com/user-attachments/assets/a815f3e9-65ac-4940-bc00-571177bef53d" /> Here's Skrifa *with* hinting: <img width="1592" height="1068" alt="image" src="https://github.com/user-attachments/assets/d6cc0669-3537-4377-bba9-ed5ef09664db" /> Hinting does make the horizontal strokes look a bit bolder, which makes me wonder once again about increasing the font weight from "light" to "regular". --------- Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
294 lines
8.5 KiB
Rust
294 lines
8.5 KiB
Rust
use criterion::{Criterion, criterion_group, criterion_main};
|
|
|
|
use epaint::{
|
|
ClippedShape, Color32, Mesh, PathStroke, Pos2, Rect, Shape, Stroke, TessellationOptions,
|
|
Tessellator, TextureAtlas, Vec2, pos2, tessellator::Path,
|
|
};
|
|
|
|
use std::hint::black_box;
|
|
|
|
#[global_allocator]
|
|
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; // Much faster allocator
|
|
|
|
fn single_dashed_lines(c: &mut Criterion) {
|
|
c.bench_function("single_dashed_lines", move |b| {
|
|
b.iter(|| {
|
|
let mut v = Vec::new();
|
|
|
|
let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];
|
|
|
|
for _ in 0..100 {
|
|
v.extend(Shape::dashed_line(
|
|
&line,
|
|
Stroke::new(1.5, Color32::RED),
|
|
10.0,
|
|
2.5,
|
|
));
|
|
}
|
|
|
|
black_box(v);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn many_dashed_lines(c: &mut Criterion) {
|
|
c.bench_function("many_dashed_lines", move |b| {
|
|
b.iter(|| {
|
|
let mut v = Vec::new();
|
|
|
|
let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];
|
|
|
|
for _ in 0..100 {
|
|
Shape::dashed_line_many(&line, Stroke::new(1.5, Color32::RED), 10.0, 2.5, &mut v);
|
|
}
|
|
|
|
black_box(v);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn tessellate_circles(c: &mut Criterion) {
|
|
c.bench_function("tessellate_circles_100k", move |b| {
|
|
let radii: [f32; 10] = [1.0, 2.0, 3.6, 4.0, 5.7, 8.0, 10.0, 13.0, 15.0, 17.0];
|
|
let mut clipped_shapes = vec![];
|
|
for r in radii {
|
|
for _ in 0..10_000 {
|
|
let clip_rect = Rect::from_min_size(Pos2::ZERO, Vec2::splat(1024.0));
|
|
let shape = Shape::circle_filled(Pos2::new(10.0, 10.0), r, Color32::WHITE);
|
|
clipped_shapes.push(ClippedShape { clip_rect, shape });
|
|
}
|
|
}
|
|
assert_eq!(
|
|
clipped_shapes.len(),
|
|
100_000,
|
|
"length of clipped shapes should be 100k, but was {}",
|
|
clipped_shapes.len()
|
|
);
|
|
|
|
let pixels_per_point = 2.0;
|
|
let options = TessellationOptions::default();
|
|
|
|
let atlas = TextureAtlas::new([4096, 256], Default::default());
|
|
let font_tex_size = atlas.size();
|
|
let prepared_discs = atlas.prepared_discs();
|
|
|
|
b.iter(|| {
|
|
let mut tessellator = Tessellator::new(
|
|
pixels_per_point,
|
|
options,
|
|
font_tex_size,
|
|
prepared_discs.clone(),
|
|
);
|
|
let clipped_primitives = tessellator.tessellate_shapes(clipped_shapes.clone());
|
|
black_box(clipped_primitives);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thick_line_solid(c: &mut Criterion) {
|
|
c.bench_function("thick_solid_line", move |b| {
|
|
let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(1.5, &Stroke::new(2.0, Color32::RED).into(), &mut mesh);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thick_large_line_solid(c: &mut Criterion) {
|
|
c.bench_function("thick_large_solid_line", move |b| {
|
|
let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(1.5, &Stroke::new(2.0, Color32::RED).into(), &mut mesh);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thin_line_solid(c: &mut Criterion) {
|
|
c.bench_function("thin_solid_line", move |b| {
|
|
let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(1.5, &Stroke::new(0.5, Color32::RED).into(), &mut mesh);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thin_large_line_solid(c: &mut Criterion) {
|
|
c.bench_function("thin_large_solid_line", move |b| {
|
|
let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(1.5, &Stroke::new(0.5, Color32::RED).into(), &mut mesh);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thick_line_uv(c: &mut Criterion) {
|
|
c.bench_function("thick_uv_line", move |b| {
|
|
let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(
|
|
1.5,
|
|
&PathStroke::new_uv(2.0, |_, p| {
|
|
black_box(p * 2.0);
|
|
Color32::RED
|
|
}),
|
|
&mut mesh,
|
|
);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thick_large_line_uv(c: &mut Criterion) {
|
|
c.bench_function("thick_large_uv_line", move |b| {
|
|
let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(
|
|
1.5,
|
|
&PathStroke::new_uv(2.0, |_, p| {
|
|
black_box(p * 2.0);
|
|
Color32::RED
|
|
}),
|
|
&mut mesh,
|
|
);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thin_line_uv(c: &mut Criterion) {
|
|
c.bench_function("thin_uv_line", move |b| {
|
|
let line = [pos2(0.0, 0.0), pos2(50.0, 0.0), pos2(100.0, 1.0)];
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(
|
|
1.5,
|
|
&PathStroke::new_uv(2.0, |_, p| {
|
|
black_box(p * 2.0);
|
|
Color32::RED
|
|
}),
|
|
&mut mesh,
|
|
);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn thin_large_line_uv(c: &mut Criterion) {
|
|
c.bench_function("thin_large_uv_line", move |b| {
|
|
let line = (0..1000).map(|i| pos2(i as f32, 10.0)).collect::<Vec<_>>();
|
|
let mut path = Path::default();
|
|
path.add_open_points(&line);
|
|
|
|
b.iter(|| {
|
|
let mut mesh = Mesh::default();
|
|
path.stroke_closed(
|
|
1.5,
|
|
&PathStroke::new_uv(2.0, |_, p| {
|
|
black_box(p * 2.0);
|
|
Color32::RED
|
|
}),
|
|
&mut mesh,
|
|
);
|
|
|
|
black_box(mesh);
|
|
});
|
|
});
|
|
}
|
|
|
|
fn rgba_values() -> [[u8; 4]; 1000] {
|
|
core::array::from_fn(|i| [5, 7, 11, 13].map(|m| (i * m) as u8))
|
|
}
|
|
|
|
fn from_rgba_unmultiplied_0(c: &mut Criterion) {
|
|
c.bench_function("from_rgba_unmultiplied_0", move |b| {
|
|
let values = black_box(rgba_values().map(|[r, g, b, _]| [r, g, b, 0]));
|
|
b.iter(|| {
|
|
for [r, g, b, a] in values {
|
|
let color = ecolor::Color32::from_rgba_unmultiplied(r, g, b, a);
|
|
black_box(color);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
fn from_rgba_unmultiplied_other(c: &mut Criterion) {
|
|
c.bench_function("from_rgba_unmultiplied_other", move |b| {
|
|
let values = black_box(rgba_values().map(|[r, g, b, a]| [r, g, b, a.clamp(1, 254)]));
|
|
b.iter(|| {
|
|
for [r, g, b, a] in values {
|
|
let color = ecolor::Color32::from_rgba_unmultiplied(r, g, b, a);
|
|
black_box(color);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
fn from_rgba_unmultiplied_255(c: &mut Criterion) {
|
|
c.bench_function("from_rgba_unmultiplied_255", move |b| {
|
|
let values = black_box(rgba_values().map(|[r, g, b, _]| [r, g, b, 255]));
|
|
b.iter(|| {
|
|
for [r, g, b, a] in values {
|
|
let color = ecolor::Color32::from_rgba_unmultiplied(r, g, b, a);
|
|
black_box(color);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
criterion_group!(
|
|
benches,
|
|
single_dashed_lines,
|
|
many_dashed_lines,
|
|
tessellate_circles,
|
|
thick_line_solid,
|
|
thick_large_line_solid,
|
|
thin_line_solid,
|
|
thin_large_line_solid,
|
|
thick_line_uv,
|
|
thick_large_line_uv,
|
|
thin_line_uv,
|
|
thin_large_line_uv,
|
|
from_rgba_unmultiplied_0,
|
|
from_rgba_unmultiplied_other,
|
|
from_rgba_unmultiplied_255,
|
|
);
|
|
criterion_main!(benches);
|