use std::collections::BTreeMap;
use std::sync::{Mutex, OnceLock};
use super::adapter::{
gpu_unavailable_json, is_management_display, selected_adapter, AdapterSelection,
};
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct LaneRecord {
pub name: String,
pub installed: bool,
pub reports_frames: bool,
pub frames: u64,
pub idle_reason: Option<String>,
#[serde(default)]
pub clocks: LaneClockStats,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize,
)]
pub struct LaneClockStats {
pub last_upload_us: u64,
pub last_upload_bytes: u64,
pub uploads_total: u64,
pub last_gpu_pass_us: Option<u64>,
pub gpu_timestamps: Option<bool>,
}
impl LaneRecord {
#[must_use]
pub fn short_name(&self) -> &str {
self.name.rsplit("::").next().unwrap_or(self.name.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpuLaneUse {
NotInstalled,
InUse {
frames: u64,
},
InstalledUnused,
Unknown {
silent_lanes: usize,
},
}
impl GpuLaneUse {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
GpuLaneUse::NotInstalled => "not_installed",
GpuLaneUse::InUse { .. } => "in_use",
GpuLaneUse::InstalledUnused => "installed_unused",
GpuLaneUse::Unknown { .. } => "unknown",
}
}
#[must_use]
pub fn frames(self) -> u64 {
match self {
GpuLaneUse::InUse { frames } => frames,
_ => 0,
}
}
}
static LANES: OnceLock<Mutex<BTreeMap<&'static str, LaneRecord>>> = OnceLock::new();
fn lanes_slot() -> &'static Mutex<BTreeMap<&'static str, LaneRecord>> {
LANES.get_or_init(|| Mutex::new(BTreeMap::new()))
}
fn with_lane(name: &'static str, f: impl FnOnce(&mut LaneRecord)) {
if let Ok(mut g) = lanes_slot().lock() {
let rec = g.entry(name).or_insert_with(|| LaneRecord {
name: name.to_owned(),
installed: false,
reports_frames: false,
frames: 0,
idle_reason: None,
clocks: LaneClockStats::default(),
});
f(rec);
}
}
pub fn note_installed(name: &'static str) {
with_lane(name, |r| r.installed = true);
}
pub fn note_reports_frames(name: &'static str) {
with_lane(name, |r| r.reports_frames = true);
}
pub fn note_frame(name: &'static str) {
with_lane(name, |r| {
r.installed = true;
r.reports_frames = true;
r.frames += 1;
});
}
pub const PANE_NOT_PAINTED_YET: &str = "pane_not_painted_yet";
pub const AWAITING_FIRST_ENCODE: &str = "awaiting_first_encode";
pub const NOT_YET_DRAWN_REASONS: [&str; 2] = [PANE_NOT_PAINTED_YET, AWAITING_FIRST_ENCODE];
pub fn note_idle_reason(name: &'static str, reason: Option<&str>) {
with_lane(name, |r| {
r.idle_reason = reason.map(str::to_owned);
});
}
#[must_use]
pub fn idle_reason_of(name: &str) -> Option<String> {
lanes_slot().lock().ok().and_then(|g| g.get(name).and_then(|r| r.idle_reason.clone()))
}
pub fn note_upload(name: &'static str, micros: u64, bytes: u64) {
with_lane(name, |r| {
r.clocks.last_upload_us = micros;
r.clocks.last_upload_bytes = bytes;
r.clocks.uploads_total += 1;
});
}
pub fn note_gpu_pass(name: &'static str, micros: u64) {
with_lane(name, |r| {
r.clocks.last_gpu_pass_us = Some(micros);
r.clocks.gpu_timestamps = Some(true);
});
}
pub fn note_gpu_timestamps(name: &'static str, real: bool) {
with_lane(name, |r| r.clocks.gpu_timestamps = Some(real));
}
#[must_use]
pub fn clock_stats_of(name: &str) -> LaneClockStats {
lanes_slot().lock().ok().and_then(|g| g.get(name).map(|r| r.clocks)).unwrap_or_default()
}
#[must_use]
pub fn lanes() -> Vec<LaneRecord> {
lanes_slot().lock().map(|g| g.values().cloned().collect()).unwrap_or_default()
}
#[must_use]
pub fn frames_of(name: &str) -> u64 {
lanes_slot().lock().map(|g| g.get(name).map_or(0, |r| r.frames)).unwrap_or(0)
}
#[must_use]
pub fn lane_use() -> GpuLaneUse {
fold_lane_use(&lanes())
}
#[must_use]
pub fn fold_lane_use(lanes: &[LaneRecord]) -> GpuLaneUse {
let live: Vec<&LaneRecord> = lanes.iter().filter(|l| l.installed || l.frames > 0).collect();
if live.is_empty() {
return GpuLaneUse::NotInstalled;
}
let frames: u64 = live.iter().map(|l| l.frames).sum();
if frames > 0 {
return GpuLaneUse::InUse { frames };
}
let silent = live.iter().filter(|l| !l.reports_frames).count();
if silent == 0 {
GpuLaneUse::InstalledUnused
} else {
GpuLaneUse::Unknown { silent_lanes: silent }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum LaneFlag {
Gpu,
Cpu,
Pending,
}
impl LaneFlag {
#[must_use]
pub fn from_observed(observed: Option<bool>) -> Self {
match observed {
Some(true) => Self::Gpu,
Some(false) => Self::Cpu,
None => Self::Pending,
}
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::Gpu => "GPU",
Self::Cpu => "CPU",
Self::Pending => "—",
}
}
#[must_use]
pub fn token(self) -> &'static str {
match self {
Self::Gpu => "gpu",
Self::Cpu => "cpu",
Self::Pending => "pending",
}
}
#[must_use]
pub fn colors(self) -> (egui::Color32, egui::Color32) {
match self {
Self::Gpu => (egui::Color32::from_rgb(140, 230, 170), egui::Color32::from_rgb(10, 22, 14)),
Self::Cpu => (egui::Color32::from_rgb(255, 200, 120), egui::Color32::from_rgb(26, 18, 8)),
Self::Pending => (egui::Color32::from_gray(130), egui::Color32::from_rgb(20, 20, 22)),
}
}
}
pub const LANE_FLAG_ATOM: &str = "lane:";
#[must_use]
pub fn lane_flag_atom(flag: LaneFlag) -> String {
format!("{LANE_FLAG_ATOM} {}", flag.label())
}
pub fn paint_lane_flag(ui: &mut egui::Ui, rect: egui::Rect, flag: LaneFlag) {
let text = flag.label();
let (fg, bg) = flag.colors();
let font = egui::FontId::monospace(10.0);
let galley = ui.painter().layout_no_wrap(text.to_string(), font.clone(), fg);
let size = galley.size();
let chip = egui::Rect::from_min_size(egui::pos2(rect.left() + 4.0, rect.top() + 4.0), size)
.expand2(egui::vec2(4.0, 2.0));
let label = lane_flag_atom(flag);
let resp = crate::a11y::node(
ui,
ui.id(),
("lane_flag", text),
egui::Sense::hover(),
chip,
crate::a11y::Semantics::button(label),
);
let p = ui.painter();
p.rect_filled(chip, 3.0, bg);
p.text(chip.center(), egui::Align2::CENTER_CENTER, text, font, fg);
resp.on_hover_text(match flag {
LaneFlag::Gpu => "This pane's last frame was drawn by its GPU lane.",
LaneFlag::Cpu => "This pane's last frame was drawn by the CPU painter.",
LaneFlag::Pending => {
"No frame has reached this pane's renderer yet — this is NOT 'the CPU drew it'."
}
});
}
#[cfg(test)]
mod lane_flag {
use super::*;
#[test]
fn the_three_states_are_three_and_pending_is_not_cpu() {
assert_eq!(LaneFlag::from_observed(Some(true)), LaneFlag::Gpu);
assert_eq!(LaneFlag::from_observed(Some(false)), LaneFlag::Cpu);
assert_eq!(LaneFlag::from_observed(None), LaneFlag::Pending);
let labels = [LaneFlag::Gpu.label(), LaneFlag::Cpu.label(), LaneFlag::Pending.label()];
assert_eq!(
labels.iter().collect::<std::collections::HashSet<_>>().len(),
3,
"three states must produce three labels, got {labels:?}"
);
assert_ne!(
LaneFlag::Pending.label(),
LaneFlag::Cpu.label(),
"a pane that has not painted must NOT read as the CPU painter — that is the \
exact lie this type exists to prevent"
);
let tokens = [LaneFlag::Gpu.token(), LaneFlag::Cpu.token(), LaneFlag::Pending.token()];
assert_eq!(tokens.iter().collect::<std::collections::HashSet<_>>().len(), 3);
assert_eq!(tokens, ["gpu", "cpu", "pending"]);
}
#[test]
fn the_atom_label_carries_the_flag() {
assert_eq!(lane_flag_atom(LaneFlag::Gpu), "lane: GPU");
assert_eq!(lane_flag_atom(LaneFlag::Cpu), "lane: CPU");
assert_eq!(lane_flag_atom(LaneFlag::Pending), "lane: —");
}
#[test]
fn every_plate_is_opaque_so_the_flag_does_not_superimpose_two_strings() {
for f in [LaneFlag::Gpu, LaneFlag::Cpu, LaneFlag::Pending] {
let (fg, bg) = f.colors();
assert_eq!(bg.a(), 255, "{f:?}: the plate must be fully opaque, got alpha {}", bg.a());
assert_eq!(fg.a(), 255, "{f:?}: the text must be fully opaque, got alpha {}", fg.a());
}
}
}
#[cfg(test)]
mod silent_lane_naming {
use super::*;
fn rec(name: &'static str, reports: bool) -> LaneRecord {
LaneRecord {
name: name.to_string(),
installed: true,
reports_frames: reports,
frames: 0,
idle_reason: None,
clocks: LaneClockStats::default(),
}
}
#[test]
fn the_unknown_row_names_only_the_lanes_that_do_not_count() {
let lanes = vec![
rec("a::CloudRenderer", true),
rec("b::Map3dRenderer", false),
rec("c::GpuMapRenderer", true),
];
let g = GpuStatus {
notes: Vec::new(),
adapter: None,
unavailable: None,
lane: fold_lane_use(&lanes),
lanes,
};
assert_eq!(g.lane, GpuLaneUse::Unknown { silent_lanes: 1 }, "precondition");
assert_eq!(g.silent_lane_names(), "Map3dRenderer", "only the silent one is named");
let line = g.lane_line();
assert!(line.contains("Map3dRenderer"), "the row names the lane to fix: {line}");
for innocent in ["CloudRenderer", "GpuMapRenderer"] {
assert!(
!line.contains(innocent),
"a lane that IS counting must not be accused: {line}",
);
}
assert!(line.contains("1 installed lane(s)"), "count and names must agree: {line}");
}
#[test]
fn a_registry_with_nothing_silent_never_reaches_the_unknown_row() {
let lanes = vec![rec("a::CloudRenderer", true), rec("c::GpuMapRenderer", true)];
assert_eq!(
fold_lane_use(&lanes),
GpuLaneUse::InstalledUnused,
"every lane counting ⇒ a zero is a real zero, not an unknown",
);
}
}
#[doc(hidden)]
pub fn reset_for_test() {
if let Ok(mut g) = lanes_slot().lock() {
g.clear();
}
}
static STATUS_NOTES: OnceLock<Mutex<Vec<fn() -> Option<String>>>> = OnceLock::new();
fn status_note_slot() -> &'static Mutex<Vec<fn() -> Option<String>>> {
STATUS_NOTES.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn register_status_note(f: fn() -> Option<String>) {
if let Ok(mut g) = status_note_slot().lock() {
if !g.iter().any(|&h| std::ptr::fn_addr_eq(h, f)) {
g.push(f);
}
}
}
fn status_notes() -> Vec<String> {
status_note_slot().lock().map(|g| g.iter().filter_map(|f| f()).collect()).unwrap_or_default()
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GpuStatus {
pub adapter: Option<AdapterSelection>,
pub unavailable: Option<serde_json::Value>,
pub lane: GpuLaneUse,
pub lanes: Vec<LaneRecord>,
pub notes: Vec<String>,
}
impl GpuStatus {
#[must_use]
pub fn probe() -> Self {
let lanes = lanes();
let un = gpu_unavailable_json();
Self {
adapter: selected_adapter(),
unavailable: if un.is_null() { None } else { Some(un) },
lane: fold_lane_use(&lanes),
lanes,
notes: status_notes(),
}
}
#[must_use]
pub fn none() -> Self {
Self {
adapter: None,
unavailable: None,
lane: GpuLaneUse::NotInstalled,
lanes: Vec::new(),
notes: Vec::new(),
}
}
#[must_use]
pub fn of(adapter: Option<AdapterSelection>, lanes: Vec<LaneRecord>) -> Self {
Self { adapter, unavailable: None, lane: fold_lane_use(&lanes), lanes, notes: Vec::new() }
}
#[must_use]
pub fn is_software(&self) -> bool {
self.adapter.as_ref().is_some_and(|s| s.software)
}
#[must_use]
pub fn adapter_count(&self) -> usize {
self.adapter.as_ref().map_or(0, |s| s.considered.len())
}
#[must_use]
pub fn device_class(&self) -> String {
match &self.adapter {
None => "none".to_owned(),
Some(s) if s.software => "SOFTWARE (CPU rasteriser)".to_owned(),
Some(s) if is_management_display(&s.chosen) => "management console (BMC)".to_owned(),
Some(s) => s.chosen.kind.as_str().to_owned(),
}
}
#[must_use]
pub fn adapter_line(&self) -> String {
match &self.adapter {
Some(s) => format!("GPU: {}", s.chosen.name),
None => "GPU: none — no adapter was brought up in this process".to_owned(),
}
}
#[must_use]
pub fn detail_line(&self) -> String {
match &self.adapter {
Some(s) => format!(
"{} · {} · {} adapter(s) enumerated{}",
if s.chosen.backend.is_empty() { "?" } else { s.chosen.backend.as_str() },
self.device_class(),
s.considered.len(),
if s.forced { " · FORCED" } else { "" },
),
None => "no backend · no adapter enumerated".to_owned(),
}
}
#[must_use]
pub fn lane_line(&self) -> String {
let names = |only_used: bool| -> String {
let v: Vec<&str> = self
.lanes
.iter()
.filter(|l| if only_used { l.frames > 0 } else { l.installed })
.map(LaneRecord::short_name)
.collect();
if v.is_empty() { "—".to_owned() } else { v.join(", ") }
};
match self.lane {
GpuLaneUse::InUse { frames } => {
format!("GPU lane: IN USE — {frames} frame(s) encoded by {}", names(true))
}
GpuLaneUse::InstalledUnused if self.every_idle_lane_is_undrawn() => format!(
"GPU lane: installed, NOT YET DRAWN — {} has not been asked to paint (no CPU painter is doing its work either)",
names(false)
),
GpuLaneUse::InstalledUnused => format!(
"GPU lane: installed but UNUSED — {} drew 0 frames (the CPU painter is doing the work)",
names(false)
),
GpuLaneUse::NotInstalled => {
"GPU lane: not installed — the CPU painter owns every pane".to_owned()
}
GpuLaneUse::Unknown { silent_lanes } => format!(
"GPU lane: unknown — {silent_lanes} installed lane(s) ({}) do not report \
frames; the rest are counting",
self.silent_lane_names()
),
}
}
#[must_use]
pub fn silent_lane_names(&self) -> String {
let v: Vec<&str> = self
.lanes
.iter()
.filter(|l| (l.installed || l.frames > 0) && !l.reports_frames)
.map(LaneRecord::short_name)
.collect();
if v.is_empty() { "—".to_owned() } else { v.join(", ") }
}
#[must_use]
pub fn every_idle_lane_is_undrawn(&self) -> bool {
let mut idle = self.lanes.iter().filter(|l| l.installed && l.frames == 0).peekable();
idle.peek().is_some()
&& idle.all(|l| {
l.idle_reason.as_deref().is_some_and(|r| NOT_YET_DRAWN_REASONS.contains(&r))
})
}
#[must_use]
pub fn unavailable_line(&self) -> Option<String> {
let u = self.unavailable.as_ref()?;
Some(format!(
"{} — {}",
u["code"].as_str().unwrap_or("facet-gpu-?"),
u["remedy"].as_str().unwrap_or("(no remedy recorded)")
))
}
#[must_use]
pub fn rows(&self) -> Vec<String> {
let mut v = vec![self.adapter_line(), self.detail_line(), self.lane_line()];
v.extend(self.idle_reason_line());
v.extend(self.unavailable_line());
v.extend(self.notes.iter().cloned());
v
}
#[must_use]
pub fn idle_reason_line(&self) -> Option<String> {
let v: Vec<String> = self
.lanes
.iter()
.filter(|l| l.frames == 0)
.filter_map(|l| l.idle_reason.as_ref().map(|r| format!("{}: {r}", l.short_name())))
.collect();
if v.is_empty() { None } else { Some(format!("idle because — {}", v.join("; "))) }
}
#[must_use]
pub fn to_json(&self) -> serde_json::Value {
serde_json::json!({
"adapter": match &self.adapter {
Some(s) => s.to_json(),
None => serde_json::Value::Null,
},
"adapter_count": self.adapter_count(),
"backend": self.adapter.as_ref().map(|s| s.chosen.backend.clone()),
"device_class": self.device_class(),
"software": self.is_software(),
"lane": self.lane.as_str(),
"lane_frames": self.lane.frames(),
"lanes": self.lanes,
"unavailable": self.unavailable.clone().unwrap_or(serde_json::Value::Null),
"notes": self.notes,
"rows": self.rows(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::render::adapter::{AdapterFacts, AdapterKind, VENDOR_MESA, VENDOR_NVIDIA};
fn nvidia() -> AdapterFacts {
AdapterFacts::new("NVIDIA GeForce RTX 4090", AdapterKind::DiscreteGpu, "Vulkan")
.with_vendor(VENDOR_NVIDIA)
}
fn llvmpipe() -> AdapterFacts {
AdapterFacts::new("llvmpipe (LLVM 21.1.8, 256 bits)", AdapterKind::Cpu, "Vulkan")
.with_vendor(VENDOR_MESA)
}
fn lane(name: &str, installed: bool, reports: bool, frames: u64) -> LaneRecord {
LaneRecord {
name: name.to_owned(),
installed,
reports_frames: reports,
frames,
idle_reason: None,
clocks: LaneClockStats::default(),
}
}
#[test]
fn an_undrawn_lane_is_not_reported_as_the_cpu_painter_doing_the_work() {
let undrawn = |name: &str, reason: &str| LaneRecord {
idle_reason: Some(reason.to_owned()),
..lane(name, true, true, 0)
};
let status = |lanes: Vec<LaneRecord>| GpuStatus {
notes: Vec::new(),
adapter: None,
unavailable: None,
lane: fold_lane_use(&lanes),
lanes,
};
let korp = status(vec![
undrawn("facett_core::render::gpu::graphcloud::cloud::CloudRenderer", PANE_NOT_PAINTED_YET),
undrawn("facett_map::gpu::GpuMapRenderer", PANE_NOT_PAINTED_YET),
]);
assert!(korp.every_idle_lane_is_undrawn());
let line = korp.lane_line();
assert!(
!line.contains("the CPU painter is doing the work"),
"nothing was drawing this lane's pane, so nothing was doing its work: {line}",
);
assert!(line.contains("NOT YET DRAWN"), "the row must say what is actually true: {line}");
assert!(
korp.rows().iter().any(|r| r.contains("GpuMapRenderer: pane_not_painted_yet")),
"{:?}",
korp.rows(),
);
let chosen = status(vec![undrawn("facett_map::gpu::GpuMapRenderer", AWAITING_FIRST_ENCODE)]);
assert!(!chosen.lane_line().contains("the CPU painter is doing the work"));
let declined = status(vec![undrawn("facett_map::gpu::GpuMapRenderer", "rotated_camera")]);
assert!(!declined.every_idle_lane_is_undrawn());
assert!(
declined.lane_line().contains("the CPU painter is doing the work"),
"a declined lane really did hand the work over: {}",
declined.lane_line(),
);
let mixed = status(vec![
lane("facett_core::render::gpu::graphcloud::cloud::CloudRenderer", true, true, 0),
undrawn("facett_map::gpu::GpuMapRenderer", PANE_NOT_PAINTED_YET),
]);
assert!(
!mixed.every_idle_lane_is_undrawn(),
"a lane that never explained itself cannot be counted as un-drawn",
);
}
#[test]
fn an_idle_lane_names_its_reason_in_the_row_a_human_reads() {
let mut rec = lane("facett_map::gpu::GpuMapRenderer", true, true, 0);
let bare = GpuStatus {
notes: Vec::new(),
adapter: None,
unavailable: None,
lane: fold_lane_use(std::slice::from_ref(&rec)),
lanes: vec![rec.clone()],
};
let bare_rows = bare.rows();
assert!(bare_rows.iter().any(|r| r.contains("installed but UNUSED")), "{bare_rows:?}");
assert!(
!bare_rows.iter().any(|r| r.contains("idle because")),
"a lane that never explained itself must not have words put in its mouth: {bare_rows:?}"
);
assert_eq!(bare.idle_reason_line(), None);
rec.idle_reason = Some("pane_not_painted_yet".to_owned());
let told = GpuStatus {
notes: Vec::new(),
adapter: None,
unavailable: None,
lane: fold_lane_use(std::slice::from_ref(&rec)),
lanes: vec![rec.clone()],
};
let told_rows = told.rows();
assert_ne!(bare_rows, told_rows, "the reason must CHANGE what a human reads, or it is not reaching them");
assert!(
told_rows.iter().any(|r| r == "idle because — GpuMapRenderer: pane_not_painted_yet"),
"the reason must be its OWN row, naming lane and reason: {told_rows:?}"
);
assert!(
!told.lane_line().contains("pane_not_painted_yet"),
"the reason belongs on its own row: {}",
told.lane_line()
);
let painting = lane("facett_map::gpu::GpuMapRenderer", true, true, 7);
let mut idle_other = lane("facett_graph3d::gpu::CloudRenderer", true, true, 0);
idle_other.idle_reason = Some("no_graph_open".to_owned());
let mixed = GpuStatus {
notes: Vec::new(),
adapter: None,
unavailable: None,
lane: fold_lane_use(&[painting.clone(), idle_other.clone()]),
lanes: vec![painting, idle_other],
};
let mixed_rows = mixed.rows();
assert!(mixed_rows.iter().any(|r| r.contains("IN USE — 7 frame(s)")), "{mixed_rows:?}");
assert!(
!mixed_rows.iter().any(|r| r.contains("GpuMapRenderer: ")),
"the lane that PAINTED must not be listed as idle: {mixed_rows:?}"
);
assert!(
mixed_rows.iter().any(|r| r == "idle because — CloudRenderer: no_graph_open"),
"the idle lane's reason must survive alongside a painting one: {mixed_rows:?}"
);
}
#[test]
fn the_lane_fold_never_guesses() {
assert_eq!(fold_lane_use(&[]), GpuLaneUse::NotInstalled);
assert_eq!(fold_lane_use(&[lane("A", false, true, 0)]), GpuLaneUse::NotInstalled);
assert_eq!(fold_lane_use(&[lane("A", true, true, 0)]), GpuLaneUse::InstalledUnused);
assert_eq!(
fold_lane_use(&[lane("A", true, true, 7), lane("B", true, true, 5)]),
GpuLaneUse::InUse { frames: 12 }
);
assert_eq!(
fold_lane_use(&[lane("A", true, false, 0)]),
GpuLaneUse::Unknown { silent_lanes: 1 },
"a lane that never promised to count frames must fold to unknown, not to unused"
);
assert_eq!(
fold_lane_use(&[lane("A", true, true, 0), lane("B", true, false, 0)]),
GpuLaneUse::Unknown { silent_lanes: 1 }
);
assert_eq!(
fold_lane_use(&[lane("A", true, true, 3), lane("B", true, false, 0)]),
GpuLaneUse::InUse { frames: 3 }
);
}
#[test]
fn a_lying_software_adapter_still_reads_software() {
let liar = AdapterFacts::new("llvmpipe (LLVM 21.1.8)", AdapterKind::DiscreteGpu, "Vulkan")
.with_vendor(VENDOR_MESA);
let st = GpuStatus::of(AdapterSelection::decide(&[liar], None), vec![]);
assert!(st.is_software(), "llvmpipe is software however it classes itself");
assert_eq!(st.device_class(), "SOFTWARE (CPU rasteriser)");
assert!(
st.detail_line().contains("SOFTWARE"),
"the row must say SOFTWARE: {}",
st.detail_line()
);
assert!(
!st.detail_line().contains("discrete"),
"the row must NOT call a software rasteriser discrete: {}",
st.detail_line()
);
assert_eq!(st.to_json()["software"], serde_json::json!(true));
assert_eq!(st.to_json()["device_class"], serde_json::json!("SOFTWARE (CPU rasteriser)"));
}
#[test]
fn rows_carry_adapter_backend_class_and_count() {
let st = GpuStatus::of(
AdapterSelection::decide(&[llvmpipe(), nvidia()], None),
vec![lane("facett_map::gpu::GpuMapRenderer", true, true, 42)],
);
let rows = st.rows();
assert!(rows[0].contains("NVIDIA GeForce RTX 4090"), "{:?}", rows);
assert!(rows[1].contains("Vulkan"), "{:?}", rows);
assert!(rows[1].contains("discrete"), "{:?}", rows);
assert!(rows[1].contains("2 adapter(s)"), "the count of enumerated adapters: {:?}", rows);
assert!(rows[2].contains("IN USE") && rows[2].contains("42"), "{:?}", rows);
assert!(rows[2].contains("GpuMapRenderer"), "the lane is named: {:?}", rows);
assert!(!st.is_software());
let j = st.to_json();
assert_eq!(j["backend"], serde_json::json!("Vulkan"));
assert_eq!(j["device_class"], serde_json::json!("discrete"));
assert_eq!(j["adapter_count"], serde_json::json!(2));
assert_eq!(j["lane"], serde_json::json!("in_use"));
assert_eq!(j["lane_frames"], serde_json::json!(42));
assert_eq!(j["rows"].as_array().expect("rows").len(), 3);
}
#[test]
fn a_gpu_less_process_says_so() {
let st = GpuStatus::none();
assert!(st.adapter_line().contains("none"), "{}", st.adapter_line());
assert!(st.lane_line().contains("not installed"), "{}", st.lane_line());
let j = st.to_json();
assert!(j["adapter"].is_null());
assert_eq!(j["lane"], serde_json::json!("not_installed"));
assert_eq!(j["software"], serde_json::json!(false));
assert_eq!(j["adapter_count"], serde_json::json!(0));
}
static REGISTRY_LOCK: Mutex<()> = Mutex::new(());
#[test]
fn the_clock_seam_counts_uploads_and_never_fakes_a_pass_time() {
let _g = REGISTRY_LOCK.lock().unwrap();
let name = "test::ClockLane";
let z = clock_stats_of(name);
assert_eq!((z.uploads_total, z.last_upload_bytes, z.last_upload_us), (0, 0, 0));
assert_eq!(z.last_gpu_pass_us, None, "no lane, no number");
assert_eq!(z.gpu_timestamps, None, "unprobed is 'did not say', not 'unavailable'");
note_upload(name, 120, 4096);
note_upload(name, 80, 2048);
let s = clock_stats_of(name);
assert_eq!(s.uploads_total, 2, "uploads accumulate");
assert_eq!((s.last_upload_us, s.last_upload_bytes), (80, 2048), "last event replaces");
assert_eq!(s.last_gpu_pass_us, None, "uploads say nothing about pass time");
note_gpu_timestamps(name, false);
let s = clock_stats_of(name);
assert_eq!(s.gpu_timestamps, Some(false));
assert_eq!(s.last_gpu_pass_us, None, "an adapter without the feature reports None, not 0");
note_gpu_pass(name, 37);
let s = clock_stats_of(name);
assert_eq!(s.last_gpu_pass_us, Some(37));
assert_eq!(s.gpu_timestamps, Some(true), "a resolved sample IS the proof of realness");
}
#[test]
fn the_registry_records_what_the_lanes_report() {
let _g = REGISTRY_LOCK.lock().unwrap();
reset_for_test();
note_installed("test::LaneA");
assert_eq!(lane_use(), GpuLaneUse::Unknown { silent_lanes: 1 });
note_reports_frames("test::LaneA");
assert_eq!(lane_use(), GpuLaneUse::InstalledUnused);
note_frame("test::LaneB");
assert_eq!(lane_use(), GpuLaneUse::InUse { frames: 1 });
let recs = lanes();
let b = recs.iter().find(|l| l.name == "test::LaneB").expect("LaneB registered");
assert!(b.installed && b.reports_frames && b.frames == 1);
assert_eq!(b.short_name(), "LaneB");
reset_for_test();
assert_eq!(lane_use(), GpuLaneUse::NotInstalled);
}
}