diff --git a/crates/pf-vkdecode/src/caps.rs b/crates/pf-vkdecode/src/caps.rs index e42c89ac..722b1772 100644 --- a/crates/pf-vkdecode/src/caps.rs +++ b/crates/pf-vkdecode/src/caps.rs @@ -605,9 +605,12 @@ pub(crate) unsafe fn query_h264_caps( let mut h264_caps = vk::VideoDecodeH264CapabilitiesKHR::default(); let mut decode_caps = vk::VideoDecodeCapabilitiesKHR::default(); + // ⚠ ORDER IS LOAD-BEARING — see the measured Intel Arc swap in + // [`crate::caps_h265::query_h265_caps`]. `push_next` prepends, so pushing the codec + // struct FIRST leaves VkVideoDecodeCapabilitiesKHR directly after the base struct. let mut caps = vk::VideoCapabilitiesKHR::default() - .push_next(&mut decode_caps) - .push_next(&mut h264_caps); + .push_next(&mut h264_caps) + .push_next(&mut decode_caps); // SAFETY: physical device is live (DeviceHandles contract); `profile` roots a // fully wired, immovable chain; `caps` chains driver-fillable structs that all // outlive the call. diff --git a/crates/pf-vkdecode/src/caps_av1.rs b/crates/pf-vkdecode/src/caps_av1.rs index 40ae6d7a..509ae1b9 100644 --- a/crates/pf-vkdecode/src/caps_av1.rs +++ b/crates/pf-vkdecode/src/caps_av1.rs @@ -278,9 +278,12 @@ pub(crate) unsafe fn query_av1_caps( let mut av1_caps = vk::VideoDecodeAV1CapabilitiesKHR::default(); let mut decode_caps = vk::VideoDecodeCapabilitiesKHR::default(); + // ⚠ ORDER IS LOAD-BEARING — see the measured Intel Arc swap in + // [`crate::caps_h265::query_h265_caps`]. `push_next` prepends, so pushing the codec + // struct FIRST leaves VkVideoDecodeCapabilitiesKHR directly after the base struct. let mut caps = vk::VideoCapabilitiesKHR::default() - .push_next(&mut decode_caps) - .push_next(&mut av1_caps); + .push_next(&mut av1_caps) + .push_next(&mut decode_caps); // SAFETY: physical device is live (DeviceHandles contract); `profile` roots a // fully wired, immovable chain; `caps` chains driver-fillable structs that all // outlive the call. diff --git a/crates/pf-vkdecode/src/caps_h265.rs b/crates/pf-vkdecode/src/caps_h265.rs index d19852e7..a25c92ff 100644 --- a/crates/pf-vkdecode/src/caps_h265.rs +++ b/crates/pf-vkdecode/src/caps_h265.rs @@ -279,9 +279,22 @@ pub(crate) unsafe fn query_h265_caps( let mut h265_caps = vk::VideoDecodeH265CapabilitiesKHR::default(); let mut decode_caps = vk::VideoDecodeCapabilitiesKHR::default(); + // ⚠ ORDER IS LOAD-BEARING on at least one shipping driver. `push_next` PREPENDS, so + // the chain is the reverse of the call order: pushing the codec struct last puts + // VkVideoDecodeCapabilitiesKHR FIRST after the base struct, which is the order every + // Vulkan sample writes it in. + // + // Measured on Intel Arc (Windows 101.8724) with the previous order — codec struct + // first — the driver filled the two by POSITION rather than by sType and returned + // them SWAPPED: `decode_caps.flags` came back 12 (= STD_VIDEO_H265_LEVEL_IDC_6_2) + // and `h265_caps.maxLevelIdc` came back 1 (= DPB_AND_OUTPUT_COINCIDE). Reading a + // level as a flag bitmask means neither COINCIDE nor DISTINCT appeared set, so the + // rung refused a device that in fact supports it, and every Arc fell back to D3D11VA. + // NVIDIA and RADV dispatch by sType and are indifferent to the order, which is why + // the fleet was green and this survived to the field. let mut caps = vk::VideoCapabilitiesKHR::default() - .push_next(&mut decode_caps) - .push_next(&mut h265_caps); + .push_next(&mut h265_caps) + .push_next(&mut decode_caps); // SAFETY: physical device is live (DeviceHandles contract); `profile` roots a // fully wired, immovable chain; `caps` chains driver-fillable structs that all // outlive the call.