perf(engine): #1145 render_bench scenario for drags over masks

Three masks (radial, brush, and a linear gradient with Refine Edges or a
Subject mask): a global Exposure drag and a mask's own Exposure drag, at
loupe and draft size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chad Elliott
2026-10-11 16:52:12 -05:00
co-authored by Claude Opus 5.5
parent 76b5377868
commit d499b4e4d9
+57
View File
@@ -70,6 +70,37 @@ fn typical() -> DevelopSettings {
s
}
/// [`typical`] plus three masks: a radial gradient, a brush (three strokes) and either a linear
/// gradient with Refine Edges (`refine`; such edits render on the CPU) or a Subject mask (the
/// heuristic, which the GPU renderer evaluates on the CPU).
fn with_masks(refine: bool) -> DevelopSettings {
use lightcraft_develop::{BrushStroke, LocalAdjustments, Mask, MaskComponent, MaskOp, MaskShape};
use lightcraft_geom::Point;
let mut s = typical();
let mask = |id: u32, shape: MaskShape, exposure: f64, refine: f64| Mask {
id,
components: vec![MaskComponent { name: None, op: MaskOp::Add, invert: false, shape }],
adjust: LocalAdjustments { exposure, clarity: 10.0, ..Default::default() },
refine,
..Default::default()
};
let radial = MaskShape::Radial { center: Point::new(0.45, 0.5), rx: 0.3, ry: 0.2, angle: 15.0, feather: 60.0, invert: false };
let strokes = (0..3)
.map(|i| BrushStroke {
points: (0..40).map(|k| Point::new(0.1 + k as f64 * 0.02, 0.2 + i as f64 * 0.25 + (k as f64 * 0.3).sin() * 0.05)).collect(),
size: 0.04,
..Default::default()
})
.collect();
let third = if refine {
mask(3, MaskShape::Linear { start: Point::new(0.5, 0.0), end: Point::new(0.5, 0.6) }, -0.5, 60.0)
} else {
mask(3, MaskShape::Subject, 0.3, 0.0)
};
s.masks = vec![mask(1, radial, 0.5, 0.0), mask(2, MaskShape::Brush { strokes }, -0.3, 0.0), third];
s
}
/// Max and mean |Δ| (8-bit, RGB) between two renders.
fn diff(a: &lightcraft_raster::Rgba8, b: &lightcraft_raster::Rgba8) -> (u8, f64) {
let (mut max, mut sum) = (0u8, 0u64);
@@ -214,6 +245,32 @@ fn main() {
drop(develop(&preview, &info, &t, &view, Some(&caches[g as usize]), g));
});
}
if run("masks") {
// three masks (one with Refine Edges, or one Subject mask): a global exposure drag and a
// mask's own exposure drag, both of which leave the masks' alphas as they were
for refine in [true, false] {
let m = with_masks(refine);
let what = if refine { "masks+refine" } else { "masks+subject" };
let caches = [StageCache::default(), StageCache::default()];
for (req, size) in [(&view, "loupe 1920×1280"), (&draft, "draft 1152×768")] {
for g in backends {
drop(develop(&preview, &info, &m, req, Some(&caches[*g as usize]), *g));
}
row(&format!("{size} exposure drag, {what}:"), &mut |g| {
let mut t = m.clone();
k += 1.0;
t.light.exposure = 0.3 + (k % 50.0) * 0.01;
drop(develop(&preview, &info, &t, req, Some(&caches[g as usize]), g));
});
row(&format!("{size} mask exposure drag, {what}:"), &mut |g| {
let mut t = m.clone();
k += 1.0;
t.masks[0].adjust.exposure = 0.5 + (k % 50.0) * 0.01;
drop(develop(&preview, &info, &t, req, Some(&caches[g as usize]), g));
});
}
}
}
if run("finish") {
// per-pixel stage cost by feature (warm cache: exposure changes only)
let variants: [(&str, fn(&mut DevelopSettings)); 6] = [