Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

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");
}