test(pf-vkdecode): frame-hash parity vs libavcodec — bit-exact on the whole fleet

WP-D parity A/B. gpu_parity (ignored) decodes the conformance vector,
reads every frame back through the presenter's exact contract (wait,
layout round-trip, signal-back, release), crops at the copy so pitch
can never leak, and compares SHA-256s in display order against goldens
from ffmpeg software decode — cross-checked bit-identical between
ffmpeg 8.0.1 (linux) and 8.1.1 (macOS), so the reference is the spec,
not one build. PF_VKD_TEST_READBACK=1 is the one test-only hook (ORs
TRANSFER_SRC into pool usage; production pools stay zero-copy-tight).

Fleet verdict: 250/250 frames bit-identical to libavcodec on RADV
(Mesa 26.0.3, distinct), AMD proprietary Windows (25.10.30.02,
distinct) and NVIDIA Windows (610.88, coincide) — H.264 decode is
exactly specified, and the native path meets the spec on every driver
and both DPB arrangements.
This commit is contained in:
2026-08-05 20:23:18 +02:00
parent ca92dab6fd
commit e6d6498a49
5 changed files with 947 additions and 1 deletions
+12 -1
View File
@@ -1126,7 +1126,18 @@ impl VkH264Decoder {
max_active_references: (required_slots - 1).min(caps.max_active_references),
std_profile_idc: std_profile,
};
let pool_plan = plan_pools(caps, required_slots);
let mut pool_plan = plan_pools(caps, required_slots);
// TEST-ONLY readback hook: the GPU parity test (tests/gpu_parity.rs)
// copies decoded pictures back to the host to hash them against
// libavcodec's output, and `vkCmdCopyImageToBuffer` requires
// TRANSFER_SRC on the source image — a bit the zero-copy production
// pools deliberately do not carry. Opt-in via env so no production path
// ever grows it. (The fleet's drivers — RADV, NVIDIA, AMD Windows —
// advertise TRANSFER_SRC on their decode-output formats; it is the same
// bit FFmpeg's hwdownload path relies on.)
if std::env::var("PF_VKD_TEST_READBACK").is_ok_and(|v| v == "1") {
pool_plan.picture_usage |= vk::ImageUsageFlags::TRANSFER_SRC;
}
// SAFETY: live device per the constructor contract, for every create in
// this block; each created half is owned by a Drop type the moment it
// exists, so a mid-build failure unwinds cleanly.