Merge pull request #699 from didpublishing/fix/596-gpu-information

#596: GPU Information names the adapter, or why the GPU compositor is off
This commit is contained in:
Brandon Thomas 2026-10-12 03:02:43 -04:00
commit 162a2e2e01
4 changed files with 81 additions and 2 deletions

View File

@ -190,6 +190,25 @@ pub fn cpu_cores() -> usize {
std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
}
/// The graphics lines of Settings ▸ GPU Information and Help ▸ System Report: whether the GPU
/// compositor runs (and on which adapter), else why compositing is on the CPU, and the adapter
/// the window itself got. Without this a card the compositor couldn't use looked undetected (#596).
pub fn gpu_summary(s: &crate::Session) -> String {
let mut text = match s.accel.as_ref().map(|a| a.name()) {
Some(name) => format!("Compositing: GPU, {name}.\n"),
None => {
let threads = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
let why = s.accel_note.as_deref().unwrap_or("no reason was recorded");
format!("Compositing: CPU, {threads} threads (pure Rust, rayon).\nGPU compositing is off: {why}\n")
}
};
match &s.window_adapter {
Some(adapter) => text.push_str(&format!("Window graphics: {adapter}\n")),
None => text.push_str("Window graphics: not known yet (the window has not drawn its first frame).\n"),
}
text
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -650,3 +650,44 @@ fn render_backend_says_why_it_renders_on_the_cpu() {
assert_eq!(why(&mut s), json!("headless: renders on the CPU unless started with --gpu"));
assert_eq!(s.execute("render.backend", json!({"backend": "cpu"})).unwrap()["why"], json!("the project's renderer is Mercury Software Only"));
}
/// Settings ▸ GPU Information names what the GPU compositor runs on, or why it is off, and the
/// adapter the window got, so a card the compositor couldn't use doesn't look undetected (#596).
#[test]
fn gpu_summary_names_the_adapter_or_says_why_there_is_none() {
use crate::sysinfo::gpu_summary;
use effectcraft_render::{Accelerator, FxStep, Renderer};
struct Named;
impl Accelerator for Named {
fn name(&self) -> String {
"NVIDIA GeForce RTX 4070 (Vulkan)".into()
}
fn comp_frame(&self, _: &Renderer, _: crate::project::ItemId, _: effectcraft_time::Tick) -> Option<effectcraft_raster::Image> {
None
}
fn supports_effect(&self, _: &str) -> bool {
false
}
fn effects(&self, _: &[FxStep], _: &effectcraft_effects::Buf, _: Option<f32>) -> Option<effectcraft_effects::Buf> {
None
}
}
let mut s = Session::default();
let off = gpu_summary(&s);
assert!(off.contains("Compositing: CPU"), "{off}");
assert!(off.contains("GPU compositing is off: no reason was recorded"), "{off}");
assert!(off.contains("Window graphics: not known yet"), "{off}");
s.accel_note = Some("GPU compositor unavailable: no compute shaders".into());
s.window_adapter = Some("NVIDIA GeForce RTX 4070 (Dx12, DiscreteGpu)".into());
let declined = gpu_summary(&s);
assert!(declined.contains("GPU compositing is off: GPU compositor unavailable: no compute shaders"), "{declined}");
assert!(declined.contains("Window graphics: NVIDIA GeForce RTX 4070 (Dx12, DiscreteGpu)"), "the card the window got is listed: {declined}");
s.accel = Some(std::sync::Arc::new(Named));
let on = gpu_summary(&s);
assert!(on.starts_with("Compositing: GPU, NVIDIA GeForce RTX 4070 (Vulkan)."), "{on}");
assert!(!on.contains("compositing is off"), "{on}");
}

View File

@ -775,12 +775,12 @@ pub fn frontend(app: &mut EffectcraftApp, ctx: &egui::Context, id: &str, p: Valu
Value::Null
}
"app.gpuInfo" => {
let threads = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
crate::panels::dialogs::info(
app,
"GPU Information",
&format!(
"Compositing: CPU, {threads} threads (pure Rust, rayon).\nDisplay: egui on wgpu.\nLayer cache: {} MB • Preview cache: {} MB.",
"{}Layer cache: {} MB • Preview cache: {} MB.",
effectcraft_engine::sysinfo::gpu_summary(&app.session),
app.session.layer_cache.budget() >> 20,
app.frames.budget() >> 20
),

View File

@ -111,3 +111,22 @@ fn gpu_information_ok_returns_to_settings_and_cancel_restores_preferences() {
fn gpu_information_escape_returns_to_settings_and_ok_saves_preferences() {
check_info_transaction(true);
}
/// GPU Information names the adapter the window got and says why the compositor is off, so a
/// card that the compositor could not use does not look undetected (#596).
#[test]
fn gpu_information_names_the_adapter_and_why_the_gpu_is_off() {
let session = Session {
accel_note: Some("GPU compositor unavailable: no compute shaders".into()),
window_adapter: Some("NVIDIA GeForce RTX 4070 (Dx12, DiscreteGpu)".into()),
..Default::default()
};
let mut h = Harness::builder().with_size(vec2(1600.0, 1000.0)).build_eframe(|_| EffectcraftApp::new(session));
h.run_steps(4);
let ctx = h.ctx.clone();
menus::invoke(h.state_mut(), &ctx, "app.gpuInfo", json!({})).unwrap();
h.run_steps(3);
assert_eq!(h.state().dialog, Some(Dialog::Info));
assert!(h.query_by_label_contains("Window graphics: NVIDIA GeForce RTX 4070").is_some(), "the window's adapter is shown");
assert!(h.query_by_label_contains("GPU compositing is off: GPU compositor unavailable").is_some(), "the reason is shown");
}