facett-about/tests/gpu_rows_on_real_hardware.rs
//! **THE LOOK — the About card rendered by wgpu on the real device, offscreen.**
//!
//! `egui_kittest`'s `WgpuTestRenderer` produces real pixels with no window and no
//! display, so this is a genuine render on this box's GPU. Everything in the GPU rows
//! comes from the adapter the wgpu bring-up actually opened, not from a fixture.
//!
//! ## Why this test does NOT use `default_wgpu_setup()`
//!
//! `egui_kittest`'s own headless setup installs a `native_adapter_selector` whose
//! comment reads *"Prefer software rasterizers"*. That is a sensible default for a CI
//! box — and it means **every render proof in this repo that used it has been running
//! on llvmpipe**, on a machine with an RTX 4090, with nothing anywhere saying so. So
//! this test builds its render state from `facett_wgpu_options()` — facett's OWN
//! adapter policy, the same one the shipping app installs — which is what makes the
//! rows it paints the rows a user sees.
//!
//! And because the whole point of the rows is to expose a silent software fallback,
//! the assertion is inverted relative to a normal render test: with
//! `FACETT_TEST_GPU=discrete` this test **fails if the frame was drawn by a CPU
//! rasteriser**. That is the check whose absence let llvmpipe hide.
//!
//! ## Two modes, both real
//!
//! * `FACETT_TEST_GPU=discrete` — the frame MUST come from a hardware GPU. Fails on a
//! software rasteriser.
//! * `FACETT_TEST_GPU=software FACETT_GPU_ADAPTER=llvmpipe` — force the fallback and
//! LOOK at the warning: the frame MUST come from a software rasteriser and the card
//! MUST say so, in the error colour. Without this half, "the row turns red on
//! llvmpipe" would be a claim no one had ever seen.
//!
//! Run: `FACETT_TEST_GPU=discrete cargo test -p facett-about --test gpu_rows_on_real_hardware -- --nocapture`
//! (add `env -u WAYLAND_DISPLAY -u DISPLAY` — it needs no display and must not open one).
use egui_kittest::kittest::Queryable;
use egui_kittest::wgpu::{create_render_state, WgpuTestRenderer};
use egui_kittest::Harness;
use facett_about::{draw_about_window, logo_texture, GpuStatus, Identity};
/// The env gate the repo already uses for "this proof needs the discrete GPU"
/// (`facett_map3d::gpu::proof_wgpu_setup`). Unset → the test reports LOUDLY that it
/// proved nothing and returns, rather than pretending a skip is a pass.
const GATE: &str = "FACETT_TEST_GPU";
fn identity() -> Identity {
Identity {
subtitle: "the shared About card, on the real device".into(),
engines: vec!["Hugin & Munin — two ravens in black vector metal".into()],
framework: vec!["jera — the shared job manager".into()],
..Identity::new("testapp", "TestApp", "9.9.9", "what am I rendering on?")
}
}
#[test]
fn the_about_card_renders_on_the_real_gpu_and_says_which_one() {
let mode = std::env::var(GATE).unwrap_or_default();
let want_software = match mode.as_str() {
"discrete" => false,
"software" => true,
_ => {
eprintln!(
"SKIPPED and therefore PROVED NOTHING: set {GATE}=discrete to render the About \
card on this box's discrete GPU, or {GATE}=software (with \
FACETT_GPU_ADAPTER=llvmpipe) to LOOK at the software-fallback warning. \
(Deliberately not a silent pass.)"
);
return;
}
};
// facett's OWN adapter policy — NOT `default_wgpu_setup()`, which prefers software.
// Bringing this up also records the selection in the process globals that
// `GpuStatus::probe()` reads, so the card below reports the device it is drawn by.
let setup = facett_core::render::gpu::adapter_wgpu::facett_wgpu_options().wgpu_setup;
let rs = create_render_state(setup, Default::default());
let status = GpuStatus::probe();
let sel = status
.adapter
.clone()
.expect("the wgpu bring-up must have RECORDED its adapter selection");
eprintln!("LOOK: {}", sel.banner());
for row in status.rows() {
eprintln!("LOOK row: {row}");
}
// THE inverted assertion: in `discrete` mode a CPU rasteriser must fail this,
// loudly. This is the check whose absence let every render proof in the repo run
// on llvmpipe. In `software` mode it is the mirror: the forced fallback must
// really have happened, or the warning below is being looked at on a 4090.
assert_eq!(
status.is_software(),
want_software,
"{GATE}={mode} asked for a {} adapter and the frame was drawn by: {}",
if want_software { "SOFTWARE" } else { "hardware" },
sel.banner()
);
assert!(!sel.chosen.name.is_empty(), "the driver named the device");
let id = identity();
let mut h = Harness::builder()
.with_size(egui::vec2(760.0, 560.0))
.renderer(WgpuTestRenderer::from_render_state(rs))
.build_ui(move |ui| {
let logo = logo_texture(ui.ctx(), &id);
let mut open = true;
// The plain five-argument body every app calls — the live probe path.
draw_about_window(ui.ctx(), &mut open, &id, logo.as_ref(), 360.0);
});
h.run();
// The REAL adapter name, backend and class rode into the tree the user reads.
for needle in [sel.chosen.name.as_str(), sel.chosen.backend.as_str(), "adapter(s) enumerated"] {
assert!(
h.query_all_by_label_contains(needle).next().is_some(),
"the card must paint {needle:?} — the rows are read off the live bring-up"
);
}
// …and the software verdict is on the screen, not just in the struct.
let says_software = h.query_all_by_label_contains("SOFTWARE").next().is_some();
assert_eq!(
says_software, want_software,
"the painted rows must agree with the device that drew them (SOFTWARE shown: \
{says_software}, drawn by software: {want_software})"
);
// Real pixels, from the GPU.
let img = h.render().expect("wgpu render");
let path = std::env::var("FACETT_ABOUT_LOOK_PNG")
.unwrap_or_else(|_| format!("/tmp/facett_about_gpu_rows_{mode}.png"));
image::DynamicImage::ImageRgba8(img.clone()).save(&path).expect("save the frame");
eprintln!("LOOK: saved {path} ({}×{})", img.width(), img.height());
// Not a blank frame: the card is a light panel on a dark desktop, so a real render
// has both. A one-colour buffer means the window did not paint and every label
// assertion above passed off an AccessKit tree with no pixels behind it.
let distinct = {
let mut set = std::collections::HashSet::new();
for p in img.pixels() {
set.insert((p.0[0] / 8, p.0[1] / 8, p.0[2] / 8));
if set.len() > 64 {
break;
}
}
set.len()
};
assert!(distinct > 8, "the GPU frame is essentially one colour ({distinct} buckets) — nothing painted");
}