#3470, #3472, #3473, #3474, #3475, #3476, #3477, #3480, #3483, #3484, #3487-#3491: test the ends of the new ranges on the GPU

CPU / GPU comparisons at the new ends for Venetian Blinds (width 1, width
and feather 32000), Radial Wipe (feather 32000), Grid (border 2000),
Scatter (1000), Beam (4000), Change Color (1710 degrees) and Turbulent
Displace (seed 100000). The comparison counts infinity on both sides as
equal, so Levels (Individual Controls) gets a direct check that the GPU's
output is finite at the ends of its ranges. The Noise HLS test asserts the
spec's ranges, so it fails without them.
This commit is contained in:
arnav-hiwarkar committed 2026-11-12 16:24:17 +05:30
1 parent acf60eeaf3
commit c0387ea18a
6 files changed
+49

No files matched your search

+4
View File
@@ -978,6 +978,10 @@ mod tests {
*p = premul([0.6, 0.3, (i % 7) as f32 / 7.0], 1.0);
}
for id in ["ec.noise.noisehls", "ec.noise.noisehlsauto"] {
for (param, lo, hi) in [("hue", 0.0, 1000.0), ("lightness", 0.0, 1000.0), ("saturation", 0.0, 1000.0), ("grainSize", 0.0, 100.0)] {
let ui = &crate::find(id).unwrap().params.iter().find(|p| p.id == param).unwrap().ui;
assert!(matches!(ui, ParamUi::Slider { min, max, .. } if *min == lo && *max == hi), "{id} {param}: {ui:?}");
}
let at = |amount: f64, grain: f64| {
let vals =
[("noise", Value::Enum(2)), ("hue", num(amount)), ("lightness", num(amount)), ("saturation", num(amount)), ("grainSize", num(grain))];
+2
View File
@@ -38,6 +38,8 @@ fn twirl_bulge_ripple_wave_warp() {
#[test]
fn turbulent_displace_and_cc_lens() {
effect_case("ec.distort.turbulentdisplace", &[("size", n(30.0)), ("amount", n(20.0))]);
// #3470: After Effects' maximum Random Seed.
effect_case("ec.distort.turbulentdisplace", &[("size", n(30.0)), ("amount", n(20.0)), ("evolutionOptions/randomSeed", n(100000.0))]);
effect_case(
"ec.distort.turbulentdisplace",
&[("displacement", e(1)), ("complexity", n(3.5)), ("evolution", n(90.0)), ("size", n(25.0)), ("pinning", e(2)), ("resizeLayer", on())],
+2
View File
@@ -40,6 +40,8 @@ fn closed_form() {
env_case("ec.generate.beam", &[]);
env_case("ec.generate.beam", &[("length", n(70.0)), ("time", n(40.0)), ("softness", n(0.0)), ("perspective3d", off()), ("compositeOnOriginal", off())]);
env_case("ec.generate.beam", &[("startThickness", n(3.0)), ("endThickness", n(20.0)), ("insideColor", c(0.3, 0.9, 1.0))]);
// #3475: After Effects' maximum thickness.
env_case("ec.generate.beam", &[("startThickness", n(0.0)), ("endThickness", n(4000.0))]);
env_case("ec.generate.lensflare", &[]);
env_case("ec.generate.lensflare", &[("lensType", e(1)), ("flareCenter", pt(50.0, 30.0)), ("blendWithOriginal", n(30.0))]);
env_case("ec.generate.lensflare", &[("lensType", e(2)), ("flareBrightness", n(160.0))]);
+7
View File
@@ -83,6 +83,11 @@ fn wipes() {
effect_case("ec.transition.radialwipe", &[("completion", n(45.0)), ("wipe", e(1)), ("center", pt(30.2, 19.6))]);
effect_case("ec.transition.radialwipe", &[("completion", n(60.0)), ("wipe", e(2)), ("startAngle", n(-40.0)), ("feather", n(3.0))]);
effect_case("ec.transition.radialwipe", &[("completion", n(0.0)), ("feather", n(20.0))]);
// #3472-#3474: the ends of After Effects' ranges (a Feather wider than 8 blinds is the CPU's
// even veil).
effect_case("ec.transition.venetian", &[("completion", n(40.0)), ("width", n(1.0)), ("feather", n(6.0))]);
effect_case("ec.transition.venetian", &[("completion", n(40.0)), ("width", n(32000.0)), ("feather", n(32000.0))]);
effect_case("ec.transition.radialwipe", &[("completion", n(40.0)), ("feather", n(32000.0))]);
effect_case("ec.transition.gradientwipe", &[("completion", n(40.0))]);
effect_case("ec.transition.gradientwipe", &[("completion", n(55.0)), ("softness", n(30.0)), ("invert", b(true))]);
effect_case("ec.transition.gradientwipe", &[("completion", n(100.0))]);
@@ -147,6 +152,8 @@ fn generators() {
);
effect_case("ec.generate.checkerboard", &[("sizeFrom", e(0)), ("corner", pt(16.0, 11.0)), ("color", c(0.9, 0.3, 0.1)), ("blendingMode", e(1))]);
effect_case("ec.generate.grid", &[("width", n(12.0)), ("border", n(3.0))]);
// #3476: a border wider than the cells (After Effects' maximum).
effect_case("ec.generate.grid", &[("width", n(12.0)), ("border", n(2000.0))]);
effect_case(
"ec.generate.grid",
&[
+2
View File
@@ -109,6 +109,8 @@ fn arbitrary_map_signed_fractional_and_near_zero_wraps() {
#[test]
fn scatter_brush_strokes() {
effect_case("ec.stylize.scatter", &[("amount", n(5.0))]);
// #3477: After Effects' maximum, far beyond the layer.
effect_case("ec.stylize.scatter", &[("amount", n(1000.0))]);
effect_case("ec.stylize.scatter", &[("amount", n(3.0)), ("grain", e(1))]);
effect_case("ec.stylize.scatter", &[("amount", n(7.0)), ("grain", e(2)), ("randomizeEveryFrame", on())]);
effect_case("ec.stylize.brushstrokes", &[]);
+32
View File
@@ -81,6 +81,8 @@ fn colour_replacement() {
&[("amount", n(100.0)), ("color", c(0.8, 0.4, 0.3)), ("matchColors", e(1)), ("tolerance", n(5.0)), ("edgeSoftness", n(30.0))],
);
effect_case("ec.color.changecolor", &[("hueTransform", n(90.0)), ("colorToChange", c(0.7, 0.5, 0.4)), ("matchingSoftness", n(20.0))]);
// #3480: near the top of After Effects' range (four and three quarter turns).
effect_case("ec.color.changecolor", &[("hueTransform", n(1710.0)), ("colorToChange", c(0.7, 0.5, 0.4)), ("matchingSoftness", n(20.0))]);
effect_case(
"ec.color.changecolor",
&[
@@ -199,3 +201,33 @@ fn color_stabilizer() {
fn gradient_ramp_matches_the_cpu() {
effect_case("ec.generate.gradientramp", &[]);
}
/// #3487-#3491: at the ends of the valid ranges the GPU's Levels (Individual Controls) output is
/// finite (the CPU / GPU comparison counts infinity on both sides as equal).
#[test]
fn levels_ic_is_finite_at_the_ends_of_its_ranges() {
let Some(g) = crate::tests::gpu() else { return };
let spec = effectcraft_effects::find("ec.color.levelsic").unwrap();
let size = [16.0, 8.0];
let mut params =
effectcraft_effects::Params { values: spec.params.iter().map(|ps| (ps.id.to_string(), effectcraft_effects::default_value(ps, size))).collect() };
for (k, v) in [
("rgb/rgbInWhite", n(0.2)),
("rgb/rgbGamma", n(0.0)),
("red/redOutBlack", n(-10000.0)),
("blue/blueOutWhite", n(10000.0)),
("green/greenOutWhite", n(0.0)),
("clipToOutputWhite", e(0)),
] {
params.values.insert(k.to_string(), v);
}
let mut img = crate::tests::pattern(7, 16, 8);
for p in &mut img.data {
*p = p.map(|v| v * 3.0);
}
let buf = effectcraft_effects::Buf { img, offset: [0.0, 0.0], scale: 1.0 };
let ctx = effectcraft_effects::EffectCtx { params: &params, time: 0.0, layer_size: size, seed: 1, adjustment: false, env: Default::default() };
let out = effectcraft_render::Accelerator::effects(g, &[effectcraft_render::FxStep { spec, ctx }], &buf, None)
.expect("the GPU runs Levels (Individual Controls)");
assert!(out.img.data.iter().flatten().all(|v| v.is_finite()), "{:?}", out.img.data.iter().find(|p| p.iter().any(|v| !v.is_finite())));
}