feat(mgmt/web): surface the host.env encoder pin so a conflicting GPU choice isn't invisible

listGpus gains encoder_pin (PUNKTFUNK_ENCODER when it actually pins something —
unset/auto stay null), and the console's GPU card shows it: a muted note when
the pin is compatible, an amber warning naming the pinned vendor and the next
session's GPU when it contradicts the selection (the host overrides such a pin
at session open — without this field the selection just looked broken). Docs
updated to say the adapter wins.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-26 16:53:19 +02:00
co-authored by Claude Fable 5
parent 751d1de506
commit 6c2c8b6eab
8 changed files with 97 additions and 3 deletions
+30
View File
@@ -66,6 +66,11 @@ pub(crate) struct GpuState {
/// `PUNKTFUNK_RENDER_ADAPTER` (the host.env pin), when set — it applies while `mode` is
/// `auto`; a manual preference overrides it.
env_override: Option<String>,
/// `PUNKTFUNK_ENCODER` (the host.env encoder pin), when set to something other than `auto`
/// (e.g. `qsv`, `nvenc`, `amf`, `software`). A pin whose vendor contradicts the selected
/// GPU is overridden at session open — the adapter wins — so the console can warn that the
/// pin is stale rather than letting the selection look broken.
encoder_pin: Option<String>,
/// The GPU the next session will use.
selected: Option<ApiSelectedGpu>,
/// The GPU live sessions use right now (absent while nothing is streaming).
@@ -143,11 +148,36 @@ pub(crate) fn gpu_state() -> GpuState {
.render_adapter
.clone()
.filter(|s| !s.is_empty()),
encoder_pin: encoder_pin_of(&pf_host_config::config().encoder_pref),
selected,
active,
}
}
/// The `PUNKTFUNK_ENCODER` value worth surfacing to the console: `None` for unset/empty and for
/// an explicit `auto` (both mean "derive from the selected adapter" — nothing is pinned).
fn encoder_pin_of(pref: &str) -> Option<String> {
match pref {
"" | "auto" => None,
pin => Some(pin.to_string()),
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Only a real pin surfaces to the console — the `auto` spellings pin nothing, and showing
/// them would put a permanent scary note under every default install's GPU card.
#[test]
fn encoder_pin_surfaces_only_real_pins() {
assert_eq!(encoder_pin_of(""), None);
assert_eq!(encoder_pin_of("auto"), None);
assert_eq!(encoder_pin_of("qsv"), Some("qsv".into()));
assert_eq!(encoder_pin_of("software"), Some("software".into()));
}
}
/// GPU inventory and selection
///
/// Lists the host's hardware GPUs, the persisted auto/manual preference, the GPU the next session
+7
View File
@@ -1234,6 +1234,13 @@ async fn gpu_endpoints_list_and_validate() {
assert_eq!(s, StatusCode::OK);
assert!(b["gpus"].is_array());
assert!(b["mode"].is_string());
// The host.env encoder pin is part of the schema (null when nothing is pinned) — the
// console warns off it when a pin contradicts the selected GPU (the pin is overridden at
// session open, and without this field the selection would just look broken).
assert!(
b.as_object().unwrap().contains_key("encoder_pin"),
"listGpus must carry encoder_pin"
);
// Unknown mode → 400.
let (s, _) = send(