tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
⎇ 168cd187519c0ad575d3ce63baee2f164c38d9d1
llimphi-test/tests/gpu_golden.rs
History
Who touched what
raw
chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros
1//! Golden del backend **GPU-directo** vía `Harness::render_gpu`. Cierra el hueco
2//! "el path GPU-directo no tenía cobertura de regresión visual" (antes sólo el
3//! smoke test "no panic" de llimphi-raster). Un starfield determinista de discos
4//! con AA por SDF — el caso canónico denso del SDD §"GPU directo". Si el
5//! rasterizado de discos/AA/color regresiona, el golden falla con DiffStats.
6//!
7//! Regenerar: `LLIMPHI_GOLDEN_UPDATE=1 cargo test -p llimphi-test`.
8
9use std::path::Path;
10
11use llimphi_hal::wgpu;
12use llimphi_raster::gpu::{GpuBatch, GpuPipelines};
13use llimphi_raster::peniko::Color;
14use llimphi_test::{compare_golden, with_harness, GoldenConfig};
15
16#[test]
17fn golden_starfield_gpu_directo() {
18 // **Este test, y sólo éste, no vale contra la GPU real.** Un golden es una
19 // imagen bendecida contra un rasterizador concreto: el de software. Medido
20 // el 2026-08-05 con `LLIMPHI_TEST_GPU=1` en una Iris Xe: **2,74 % de los
21 // píxeles difieren, Δ máximo 44**. No es un bug — es lo que significa
22 // comparar contra una foto en vez de contra un oráculo.
23 //
24 // El contraste vale la pena mirarlo: en esa misma corrida los **112** tests
25 // restantes pasaron en las dos, y todos ellos comparan contra *otra manera
26 // de calcular lo mismo* (caché contra fresco, troceado contra entero, atlas
27 // contra vello, hybrid contra CPU). Ésa es la diferencia entre un oráculo y
28 // una foto, y acá quedó medida, no argumentada.
29 //
30 // Se saltea en vez de fallar para que `LLIMPHI_TEST_GPU=1 cargo test` sea
31 // una pregunta limpia —«¿el motor se porta igual en hardware?»— y no una
32 // corrida con un fallo conocido de ruido de fondo.
33 if std::env::var("LLIMPHI_TEST_GPU").ok().as_deref() == Some("1") {
34 eprintln!(
35 "salteado: un golden se compara contra el adapter de SOFTWARE con el \
36 que se bendijo. Contra la GPU real difiere por definición (medido: \
37 2,74 % de píxeles, Δ máx 44)."
38 );
39 return;
40 }
41 let rendered = with_harness(|h| {
42 h.render_gpu(320, 200, Color::from_rgba8(6, 8, 16, 255), |device, queue, enc, view, vp| {
43 let pipelines = GpuPipelines::new(device, wgpu::TextureFormat::Rgba8Unorm);
44 let mut batch = GpuBatch::new(&pipelines);
45 // Starfield determinista (LCG con seed fijo → golden reproducible).
46 let mut s: u32 = 0x1234_5678;
47 let lcg = |s: &mut u32| -> f32 {
48 *s = s.wrapping_mul(1_664_525).wrapping_add(1_013_904_223);
49 (*s >> 8) as f32 / 16_777_216.0
50 };
51 for _ in 0..300 {
52 let x = lcg(&mut s) * vp.0;
53 let y = lcg(&mut s) * vp.1;
54 let bright = 0.3 + lcg(&mut s) * 0.7;
55 let t = lcg(&mut s);
56 // Blanco-azulado a amarillo suave, alpha ∝ brillo.
57 let col = Color::new([0.85 + 0.15 * t, 0.88 + 0.10 * (1.0 - t), 0.95, bright]);
58 let r = 0.8 + bright * 1.6;
59 batch.add_disc(x, y, r, col);
60 }
61 batch.flush(device, queue, enc, view, vp, wgpu::LoadOp::Load);
62 })
63 });
64 let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/goldens/starfield_gpu.png");
65 if let Err(e) = compare_golden(&path, &rendered, &GoldenConfig::default()) {
66 panic!("{e}");
67 }
68}