diff --git a/clients/session/src/main.rs b/clients/session/src/main.rs index ee11a1dc..88a8d46a 100644 --- a/clients/session/src/main.rs +++ b/clients/session/src/main.rs @@ -548,12 +548,21 @@ mod session_main { println!("no Vulkan physical devices"); } for (i, a) in adapters.iter().enumerate() { - // The index IS the PUNKTFUNK_VK_DEVICE value, and entry 0 is what + // The bracketed number is the PUNKTFUNK_VK_DEVICE value, and the + // FIRST listed entry is what // a default run presents on — the decoder shares that device, so // on a hybrid box this line is usually the answer. let kind = if a.discrete { "discrete" } else { "integrated" }; + // `a.index`, NOT the loop position. This list is sorted + // discrete-first for reading, but PUNKTFUNK_VK_DEVICE indexes the + // raw enumeration, which puts the iGPU first on some hybrids — + // printing the loop position would name the other GPU on exactly + // the machines this flag is for. The `i == 0` marker is still the + // loop position, because sorted-first IS what pick_device lands on + // when nothing overrides it. println!( - "[{i}] {} ({kind}){}", + "[{}] {} ({kind}){}", + a.index, a.name, if i == 0 { " <- default presenter" } else { "" } ); @@ -620,6 +629,26 @@ mod session_main { println!(" extensions: {}", a.codec_exts.join(", ")); } } + if adapters.len() > 1 { + // The single most common misreading of this output: seeing a + // capable GPU listed and concluding the decoder will use it. + // Vulkan Video decodes on the PRESENTER's device, and the decoder + // preference does not move the presenter. + println!(); + println!( + "Vulkan Video decodes on the presenter's device. PUNKTFUNK_DECODER \ + picks the rung," + ); + println!( + "not the GPU — move the presenter with PUNKTFUNK_VK_DEVICE= or" + ); + println!( + "PUNKTFUNK_VK_ADAPTER=, which is the safer knob \ + where two" + ); + println!("adapters share a name."); + } 0 } Err(e) => { diff --git a/crates/pf-presenter/src/vk/setup.rs b/crates/pf-presenter/src/vk/setup.rs index 33ad5ce6..64f59d7a 100644 --- a/crates/pf-presenter/src/vk/setup.rs +++ b/crates/pf-presenter/src/vk/setup.rs @@ -55,7 +55,19 @@ pub(crate) fn video_decode_gate( /// hardware decode starts here: WHICH adapter, and does it advertise the codec. #[derive(Debug, Clone)] pub struct AdapterDecode { - /// Marketing name — also the `PUNKTFUNK_VK_ADAPTER` match key. + /// The device's position in the RAW `vkEnumeratePhysicalDevices` order — and + /// therefore the value `PUNKTFUNK_VK_DEVICE` takes, because `pick_device` indexes the + /// unsorted list (`devices.get(i)`) before any ranking runs. + /// + /// ⚠ NOT the display position. This list is sorted discrete-first for readability, + /// while enumeration order puts the iGPU first on some hybrids — so the two disagree + /// on exactly the machines this probe exists to diagnose. Printing the display + /// position as if it were the env value would hand a hybrid-laptop reporter the + /// number for the other GPU. + pub index: usize, + /// Marketing name — also the `PUNKTFUNK_VK_ADAPTER` match key. Not necessarily + /// unique: a hybrid can expose the same iGPU twice, and a name match then resolves to + /// whichever enumerates first. pub name: String, /// Discrete GPUs sort first, exactly as `pick_device` ranks them, so index 0 here is /// the device a default run will pick. @@ -743,7 +755,9 @@ pub fn probe_decode() -> Result> { // filling locals returned by value. let devices = unsafe { instance.enumerate_physical_devices() }?; let mut out: Vec<(u8, AdapterDecode)> = Vec::with_capacity(devices.len()); - for pdev in devices { + // `enumerate()` BEFORE any filtering or sorting: this index is what + // `PUNKTFUNK_VK_DEVICE` selects, so it has to survive both. + for (raw_index, pdev) in devices.into_iter().enumerate() { // SAFETY: per the Vulkan contract above - a read-only query on the live // instance/device, filling locals returned by value. let props = unsafe { instance.get_physical_device_properties(pdev) }; @@ -834,6 +848,7 @@ pub fn probe_decode() -> Result> { out.push(( rank, AdapterDecode { + index: raw_index, name, discrete: rank == 0, api_1_3,