GPU tests: print why the GPU didn't start, and fail with EFFECTCRAFT_REQUIRE_GPU=1 instead of skipping

This commit is contained in:
Matej Kozlovský
2026-10-15 03:53:00 +02:00
parent 05955c08b4
commit 217f0ffd59
3 changed files with 36 additions and 23 deletions
+20 -9
View File
@@ -49,18 +49,29 @@ pub(crate) fn hold_gpu_lock() {
/// The shared test device (the calling test holds [`hold_gpu_lock`]).
pub(crate) fn gpu() -> Option<&'static Gpu> {
static G: OnceLock<Option<Gpu>> = OnceLock::new();
static G: OnceLock<Result<Gpu, String>> = OnceLock::new();
hold_gpu_lock();
G.get_or_init(|| {
let g = Gpu::headless();
if g.is_none() {
eprintln!("effectcraft-gpu tests: no GPU adapter, skipping GPU comparisons");
} else if let Some(g) = &g {
eprintln!("effectcraft-gpu tests: adapter {}", g.ctx.name);
let g = G.get_or_init(|| {
let g = Gpu::try_headless();
match &g {
Ok(g) => eprintln!("effectcraft-gpu tests: adapter {}", g.ctx.name),
Err(e) => eprintln!("effectcraft-gpu tests: no GPU ({e}), skipping GPU comparisons"),
}
g
})
.as_ref()
});
require_gpu("effectcraft-gpu tests", g.as_ref())
}
/// `g`, or `None` (the GPU tests skip) when there is no GPU, unless `EFFECTCRAFT_REQUIRE_GPU=1`
/// (as in `tests_init`): then a GPU that didn't start fails the test, with why.
pub(crate) fn require_gpu<G>(tests: &str, g: Result<G, &String>) -> Option<G> {
match g {
Ok(g) => Some(g),
Err(e) if std::env::var("EFFECTCRAFT_REQUIRE_GPU").is_ok_and(|v| v == "1") => {
panic!("{tests}: EFFECTCRAFT_REQUIRE_GPU=1 but GPU initialization failed: {e}")
}
Err(_) => None,
}
}
/// The test device is on wgpu's OpenGL backend (Mesa's llvmpipe on the FreeBSD CI job).
+6 -4
View File
@@ -17,11 +17,13 @@ use crate::Gpu;
/// A device of the test's own (the test holds [`crate::tests::hold_gpu_lock`]).
fn gpu() -> Option<Gpu> {
crate::tests::hold_gpu_lock();
let g = Gpu::headless();
if g.is_none() {
eprintln!("effectcraft-gpu adv3d tests: no GPU adapter, skipping");
match Gpu::try_headless() {
Ok(g) => Some(g),
Err(e) => {
eprintln!("effectcraft-gpu adv3d tests: no GPU ({e}), skipping");
crate::tests::require_gpu("effectcraft-gpu adv3d tests", Err(&e))
}
}
g
}
fn add(p: &mut Project, cid: ItemId, src: LayerSource, edit: impl FnOnce(&mut effectcraft_project::Layer)) {
+10 -10
View File
@@ -17,19 +17,19 @@ use crate::tests::{Pattern, Scene, diff, n, set, tolerance, v3};
/// The device, held exclusively: deferred state is per device and a frame renders at a time
/// (a frame worker renders one frame at a time too).
pub(crate) fn deferred_gpu() -> Option<(&'static Gpu, std::sync::MutexGuard<'static, ()>)> {
static G: OnceLock<Option<Gpu>> = OnceLock::new();
static G: OnceLock<Result<Gpu, String>> = OnceLock::new();
static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
// Also no other GPU test while it runs (see `hold_gpu_lock`).
crate::tests::hold_gpu_lock();
let g = G
.get_or_init(|| {
let g = pollster::block_on(Gpu::request_deferred()).map_err(|e| eprintln!("deferred gpu tests skipped: {e}")).ok();
if let Some(g) = &g {
eprintln!("deferred gpu tests: adapter {}", g.ctx.name);
}
g
})
.as_ref()?;
let g = G.get_or_init(|| {
let g = pollster::block_on(Gpu::request_deferred());
match &g {
Ok(g) => eprintln!("deferred gpu tests: adapter {}", g.ctx.name),
Err(e) => eprintln!("deferred gpu tests: no GPU ({e}), skipping"),
}
g
});
let g = crate::tests::require_gpu("deferred gpu tests", g.as_ref())?;
Some((g, LOCK.lock().unwrap_or_else(|e| e.into_inner())))
}