mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
GPU tests: pass on Mesa lavapipe (boundary flips, skip the Reshape case that crashes it); cargo fmt (#2239)
* cargo fmt * GPU tests: llvmpipe on Vulkan gets the boundary-flip allowance; skip the Reshape boundary cases that crash lavapipe (#2238) * GPU tests: the llvmpipe boundary-flip allowance is an opt-in of the two tests that need it
This commit is contained in:
@@ -50,9 +50,7 @@ pub fn resolve(stored: &str, project: Option<&str>) -> Result<PathBuf, NotFollow
|
||||
// Resolved, but still relative: it would be taken from the working directory, which the
|
||||
// project does not choose. A Windows drive or share path (`C:\x`) stays invalid outside
|
||||
// Windows even with a project.
|
||||
Refused::Invalid
|
||||
if project.is_none() && craft_docpath::resolve(stored, None).is_ok_and(|p| cfg!(windows) || !is_windows_rooted(&p)) =>
|
||||
{
|
||||
Refused::Invalid if project.is_none() && craft_docpath::resolve(stored, None).is_ok_and(|p| cfg!(windows) || !is_windows_rooted(&p)) => {
|
||||
NotFollowed::NoProjectFolder
|
||||
}
|
||||
why => NotFollowed::Refused(why),
|
||||
|
||||
+31
-2
@@ -79,6 +79,35 @@ pub(crate) fn on_gl() -> bool {
|
||||
gpu().is_some_and(|g| g.ctx.name.ends_with("(Gl)"))
|
||||
}
|
||||
|
||||
/// The test device is Mesa's software rasteriser, llvmpipe: on OpenGL, or as lavapipe on Vulkan
|
||||
/// (the Linux CI gate).
|
||||
pub(crate) fn on_llvmpipe() -> bool {
|
||||
gpu().is_some_and(|g| g.ctx.name.starts_with("llvmpipe"))
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static LLVMPIPE_FLIPS: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
|
||||
}
|
||||
|
||||
/// Until the guard drops, comparisons on lavapipe get [`GL_BOUNDARY_FLIPS`] too. Only the tests
|
||||
/// that measured such flips there opt in (CC HexTile cell edges, CC Threshold RGB thresholds);
|
||||
/// every other comparison on the Linux gate stays exact.
|
||||
pub(crate) fn allow_llvmpipe_boundary_flips() -> impl Drop {
|
||||
struct Reset;
|
||||
impl Drop for Reset {
|
||||
fn drop(&mut self) {
|
||||
LLVMPIPE_FLIPS.with(|f| f.set(false));
|
||||
}
|
||||
}
|
||||
LLVMPIPE_FLIPS.with(|f| f.set(true));
|
||||
Reset
|
||||
}
|
||||
|
||||
/// The boundary-flip allowance applies: OpenGL, or llvmpipe in a test that opted in.
|
||||
fn boundary_flips_allowed() -> bool {
|
||||
on_gl() || (LLVMPIPE_FLIPS.with(std::cell::Cell::get) && on_llvmpipe())
|
||||
}
|
||||
|
||||
/// On GL, the fraction of pixels allowed past the tolerance: pixels on a decision boundary (a
|
||||
/// hex cell's edge, a threshold, a corner pin's coverage edge, a median's rank tie) may land on
|
||||
/// the other side, since GL drivers may divide through a reciprocal and GLSL's `fma` need not be
|
||||
@@ -255,7 +284,7 @@ pub(crate) fn check(label: &str, d: Option<Diff>, allow: f64) {
|
||||
let frac = d.over as f64 / d.total as f64;
|
||||
// Quantised depths: rounding-boundary flips (see the module docs).
|
||||
let (allow, cap) = if d.quantized && allow == 0.0 { (0.003, 4.0 / 255.0 + 1e-6) } else { (allow, f32::INFINITY) };
|
||||
let (allow, cap) = if on_gl() { (allow.max(GL_BOUNDARY_FLIPS), f32::INFINITY) } else { (allow, cap) };
|
||||
let (allow, cap) = if boundary_flips_allowed() { (allow.max(GL_BOUNDARY_FLIPS), f32::INFINITY) } else { (allow, cap) };
|
||||
assert!(frac <= allow && d.max <= cap, "{label}: {} of {} pixels over tolerance (max {:.6}); worst {:?}", d.over, d.total, d.max, d.worst);
|
||||
if d.over > 0 {
|
||||
eprintln!("{label}: {} of {} pixels over tolerance (max {:.6})", d.over, d.total, d.max);
|
||||
@@ -454,7 +483,7 @@ pub(crate) fn effect_direct(id: &str, vals: &[(&str, Value)], adjustment: bool)
|
||||
let out = effectcraft_render::Accelerator::effects(g, &[effectcraft_render::FxStep { spec, ctx: ctx() }], &buf, None).expect("the GPU runs the effect");
|
||||
assert_eq!((out.offset, out.scale), (cpu.offset, cpu.scale), "{id}: geometry");
|
||||
let d = diff(&cpu.img, &out.img, 1e-3);
|
||||
let allow = if on_gl() { (d.total as f64 * GL_BOUNDARY_FLIPS) as usize } else { 0 };
|
||||
let allow = if boundary_flips_allowed() { (d.total as f64 * GL_BOUNDARY_FLIPS) as usize } else { 0 };
|
||||
assert!(
|
||||
d.over <= allow,
|
||||
"{id} {vals:?} adj {adjustment} scale {scale}: {} of {} pixels over 1e-3 (max {}); worst {:?}",
|
||||
|
||||
@@ -12,7 +12,7 @@ use effectcraft_project::{BitDepth, MaskMode};
|
||||
use effectcraft_render::RenderOpts;
|
||||
use effectcraft_time::Tick;
|
||||
|
||||
use crate::tests::{Scene, c, check, compare_at, diff, effect_case, gpu, n, opts, pattern, set};
|
||||
use crate::tests::{Scene, allow_llvmpipe_boundary_flips, c, check, compare_at, diff, effect_case, gpu, n, opts, pattern, set};
|
||||
|
||||
pub(crate) fn e(v: u32) -> Value {
|
||||
Value::Enum(v)
|
||||
@@ -193,6 +193,7 @@ fn composites_and_colour() {
|
||||
|
||||
#[test]
|
||||
fn stylize() {
|
||||
let _flips = allow_llvmpipe_boundary_flips();
|
||||
for (done, flags) in
|
||||
[(30.0, [true, true, true, false]), (55.0, [false, true, false, true]), (70.0, [true, false, false, false]), (10.0, [false, false, true, true])]
|
||||
{
|
||||
|
||||
@@ -6,7 +6,7 @@ use effectcraft_project::BitDepth;
|
||||
use effectcraft_render::RenderOpts;
|
||||
use effectcraft_time::Tick;
|
||||
|
||||
use crate::tests::{Scene, c, check, compare_at, effect_case, n, opts, set};
|
||||
use crate::tests::{Scene, allow_llvmpipe_boundary_flips, c, check, compare_at, effect_case, n, opts, set};
|
||||
|
||||
fn e(v: u32) -> Value {
|
||||
Value::Enum(v)
|
||||
@@ -22,6 +22,7 @@ fn pt(x: f64, y: f64) -> Value {
|
||||
|
||||
#[test]
|
||||
fn posterize_thresholds_strobe_vignette() {
|
||||
let _flips = allow_llvmpipe_boundary_flips();
|
||||
effect_case("ec.stylize.posterize", &[("level", n(4.0))]);
|
||||
effect_case("ec.stylize.posterize", &[("level", n(9.0))]);
|
||||
effect_case("ec.stylize.threshold", &[("level", n(100.0))]);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
//! Emboss, Cartoon) vs the CPU effects (the oracle): direct on a buffer at full and half
|
||||
//! resolution, as adjustment, and composited at 8 and 32 bpc.
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use effectcraft_effects::MaskShape;
|
||||
use effectcraft_keyframe::{ShapePath, Value};
|
||||
use effectcraft_project::build::{self, Ids};
|
||||
@@ -9,7 +11,7 @@ use effectcraft_project::{BitDepth, MaskMode};
|
||||
use effectcraft_render::RenderOpts;
|
||||
use effectcraft_time::Tick;
|
||||
|
||||
use crate::tests::{Scene, c, check, compare_at, diff, effect_case, gpu, n, opts, pattern};
|
||||
use crate::tests::{Scene, c, check, compare_at, diff, effect_case, gpu, n, on_llvmpipe, opts, pattern};
|
||||
|
||||
fn e(v: u32) -> Value {
|
||||
Value::Enum(v)
|
||||
@@ -182,6 +184,12 @@ fn smear_and_reshape() {
|
||||
],
|
||||
];
|
||||
for vals in reshape {
|
||||
if on_llvmpipe() && vals.iter().any(|(k, _)| *k == "boundaryMask") {
|
||||
// Straight to stderr: libtest captures `eprintln!` of passing tests, and the skip
|
||||
// should show in the log.
|
||||
let _ = writeln!(std::io::stderr(), "tests_fx_warp::smear_and_reshape: skipping ec.distort.reshape {vals:?} on llvmpipe (crashes lavapipe, #2238)");
|
||||
continue;
|
||||
}
|
||||
direct_with_masks("ec.distort.reshape", vals);
|
||||
composite_with_masks("ec.distort.reshape", vals);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user