diff --git a/app/igneum-app/src/ember.rs b/app/igneum-app/src/ember.rs index fa45e767a..33538c649 100644 --- a/app/igneum-app/src/ember.rs +++ b/app/igneum-app/src/ember.rs @@ -1225,6 +1225,11 @@ pub fn amd_knob(shape: Option<&AmdTuneShape>, gclk_now_mhz: f64, linux: bool) -> if linux { return Err("not available (Linux: the clock and power limits under /sys need root; the tune's helper takes them in a later cut)".into()); } + // PC 1's integrated Radeon (8 October 2026, the exe's read-back): `gmax - gmax_range - - plimit - plimit_range - - factory 0 ok`, + // every field a dash (-1 after the parser), an ok line with no tuning interface behind it + if t.gmax_max <= t.gmax_min && t.plimit_max <= t.plimit_min { + return Err("not available (the driver exposes no tuning interface for this card: the tune line carries no clock or power range)".into()); + } if t.plimit_max < t.plimit_min || t.plimit_min <= -100.0 { return Err(format!("not available (the power-limit range {} to {} percent is not usable)", t.plimit_min, t.plimit_max)); } @@ -1291,6 +1296,11 @@ mod tests { assert!(e.contains("stock clock is not known until the card mines"), "{e}"); let e = amd_knob(Some(&t9070()), 2970.0, true).unwrap_err(); assert!(e.starts_with("not available (Linux"), "{e}"); + // the integrated Radeon's line on PC 1 (8 October 2026): ok, every range a dash; known-failed first (it read as an + // offset knob with a one-MHz ladder and a 99 percent power range) + let igpu = AmdTuneShape { ok: true, error: String::new(), gmax: -1.0, gmax_min: -1.0, gmax_max: -1.0, plimit_min: -1.0, plimit_max: -1.0 }; + let e = amd_knob(Some(&igpu), 2100.0, false).unwrap_err(); + assert!(e.starts_with("not available (the driver exposes no tuning interface"), "{e}"); assert!(control_reason("amd", &Limits::default(), "amd:gfx1201", true, false).unwrap().contains("not available (")); } diff --git a/app/igneum-app/src/engine.rs b/app/igneum-app/src/engine.rs index 1358ebd16..bb1dbe4f0 100644 --- a/app/igneum-app/src/engine.rs +++ b/app/igneum-app/src/engine.rs @@ -5933,6 +5933,20 @@ mod tests { assert!(guard < elevated, "the Windows return comes before the unix elevated step"); } + /// The two tune lines PC 1 answered on 8 October 2026 (igneum-gpu-telemetry 1d8e055d, the AMD knob's tool): the + /// integrated Radeon with no interface reads "not available", the RX 9070 XT reads the offset knob. + #[test] + fn pc1s_two_tune_lines_give_one_refusal_and_one_knob() { + let igpu = super::parse_amd_tune("tune 0 name \"AMD Radeon(TM) Graphics\" gmax - gmax_range - - plimit - plimit_range - - factory 0 ok").unwrap(); + assert!(igpu.ok && igpu.gmax_max < 0.0 && igpu.plimit_max < 0.0, "{igpu:?}"); + let shape = |t: &super::AmdTune| crate::ember::AmdTuneShape { ok: t.ok, error: t.error.clone(), gmax: t.gmax, gmax_min: t.gmax_min, gmax_max: t.gmax_max, plimit_min: t.plimit_min, plimit_max: t.plimit_max }; + assert!(crate::ember::amd_knob(Some(&shape(&igpu)), 2100.0, false).unwrap_err().contains("no tuning interface")); + let xt = super::parse_amd_tune("tune 1 name \"AMD Radeon RX 9070 XT\" gmax 0 gmax_range -500 1000 plimit 0 plimit_range -30 10 factory 1 ok").unwrap(); + let k = crate::ember::amd_knob(Some(&shape(&xt)), 3292.0, false).unwrap(); + assert_eq!((k.stock_mhz, k.gmax_offset, k.clock_max_mhz, k.clock_min_mhz, k.plimit_min, k.plimit_max), (3292, true, 3292, 2792, -30.0, 10.0), "the live clock under load (3,292 MHz in the read-back sample) is the stock the offsets apply to"); + assert_eq!(crate::ember::amd_gmax_arg(2992, 3292, true), -300); + } + /// PC 2, 7 October 2026, 14:39Z: "the helper did not run sequence 1 within 15 s (no line in ...helper.log)" is a FAULT line #[test] fn a_helper_that_does_not_answer_is_a_fault_line() {