Skip to content

Commit 422ece4

Browse files
committed
perf: eliminate overdraw for opaque image fills
1 parent c15340d commit 422ece4

File tree

12 files changed

+312
-78
lines changed

12 files changed

+312
-78
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sparse_strips/vello_bench/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ vello_common = { workspace = true }
1414
vello_cpu = { workspace = true }
1515
vello_dev_macros = { workspace = true }
1616
criterion = { workspace = true }
17+
image = { workspace = true, features = ["jpeg"] }
1718
parley = { version = "0.5.0", default-features = true }
1819
rand = { workspace = true }
1920
smallvec = { workspace = true }

sparse_strips/vello_bench/benches/main.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#![allow(dead_code, reason = "Might be unused on platforms not supporting SIMD")]
66

77
use criterion::{criterion_group, criterion_main};
8-
use vello_bench::{fine, flatten, glyph, strip, tile};
8+
use vello_bench::{fine, flatten, glyph, scene, strip, tile};
99

1010
criterion_group!(fine_solid, fine::fill);
1111
criterion_group!(fine_strip, fine::strip);
@@ -19,6 +19,7 @@ criterion_group!(flatten, flatten::flatten);
1919
criterion_group!(strokes, flatten::strokes);
2020
criterion_group!(render_strips, strip::render_strips);
2121
criterion_group!(glyph, glyph::glyph);
22+
criterion_group!(scene_bench, scene::images);
2223
criterion_main!(
2324
tile,
2425
render_strips,
@@ -31,5 +32,6 @@ criterion_main!(
3132
fine_gradient,
3233
fine_rounded_blurred_rect,
3334
fine_blend,
34-
fine_image
35+
fine_image,
36+
scene_bench
3537
);

sparse_strips/vello_bench/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod data;
1111
pub mod fine;
1212
pub mod flatten;
1313
pub mod glyph;
14+
pub mod scene;
1415
pub mod strip;
1516
pub mod tile;
1617

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2025 the Vello Authors
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
//! Full scene rendering benchmarks.
5+
6+
use std::sync::Arc;
7+
8+
use criterion::Criterion;
9+
use vello_common::kurbo::{Affine, Rect};
10+
use vello_common::paint::{Image, ImageSource};
11+
use vello_common::peniko::ImageSampler;
12+
use vello_common::peniko::{Extend, ImageQuality};
13+
use vello_common::pixmap::Pixmap;
14+
use vello_cpu::RenderContext;
15+
16+
/// Image scene rendering benchmark.
17+
pub fn images(c: &mut Criterion) {
18+
let mut g = c.benchmark_group("images");
19+
20+
let flower_image = load_flower_image();
21+
22+
const VIEWPORT_WIDTH: u16 = 1280;
23+
const VIEWPORT_HEIGHT: u16 = 960;
24+
25+
let ImageSource::Pixmap(ref image_pixmap) = flower_image else {
26+
panic!("Expected Pixmap");
27+
};
28+
let original_width = f64::from(image_pixmap.width());
29+
let original_height = f64::from(image_pixmap.height());
30+
let image_count = VIEWPORT_WIDTH / 256;
31+
32+
g.bench_function("overlapping", |b| {
33+
let mut renderer = RenderContext::new(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
34+
let mut pixmap = Pixmap::new(VIEWPORT_WIDTH, VIEWPORT_HEIGHT);
35+
36+
b.iter(|| {
37+
renderer.reset();
38+
39+
for i in (1..=image_count).rev() {
40+
let width = 256.0 * i as f64;
41+
let scale = width / original_width;
42+
let height = original_height * scale;
43+
44+
renderer.set_transform(Affine::IDENTITY);
45+
renderer.set_paint_transform(Affine::scale(scale));
46+
renderer.set_paint(Image {
47+
image: flower_image.clone(),
48+
sampler: ImageSampler {
49+
x_extend: Extend::Pad,
50+
y_extend: Extend::Pad,
51+
quality: ImageQuality::Low,
52+
alpha: 1.0,
53+
},
54+
});
55+
renderer.fill_rect(&Rect::new(0.0, 0.0, width, height));
56+
}
57+
58+
renderer.flush();
59+
renderer.render_to_pixmap(&mut pixmap);
60+
std::hint::black_box(&pixmap);
61+
});
62+
});
63+
64+
g.finish();
65+
}
66+
67+
fn load_flower_image() -> ImageSource {
68+
let image_data = include_bytes!("../../../examples/assets/splash-flower.jpg");
69+
let image = image::load_from_memory(image_data).expect("Failed to decode image");
70+
let width = image.width();
71+
let height = image.height();
72+
let rgba_data = image.into_rgba8().into_vec();
73+
74+
#[expect(
75+
clippy::cast_possible_truncation,
76+
reason = "Image dimensions fit in u16"
77+
)]
78+
let pixmap = Pixmap::from_parts(
79+
rgba_data
80+
.chunks_exact(4)
81+
.map(|rgba| {
82+
let alpha = u16::from(rgba[3]);
83+
let premultiply = |component| (alpha * u16::from(component) / 255) as u8;
84+
vello_common::color::PremulRgba8 {
85+
r: premultiply(rgba[0]),
86+
g: premultiply(rgba[1]),
87+
b: premultiply(rgba[2]),
88+
a: alpha as u8,
89+
}
90+
})
91+
.collect(),
92+
width as u16,
93+
height as u16,
94+
);
95+
96+
ImageSource::Pixmap(Arc::new(pixmap))
97+
}

0 commit comments

Comments
 (0)