test(client): the D3D11VA rung's ten-bit path, measured too
The companion to the Vulkan ten-bit leg, over the same vector and the same P010 goldens — one golden file serves both rungs because a D3D11 P010 surface and Vulkan's 3PACK16 family hold the ten bits in the same place. This is the rung where the gap mattered most. D3D11VA exposes no per-picture status query at all, so its HDR evidence was a session that built a Main10 decoder and streamed without complaint — which is precisely what a Main10 stream decoding to garbage would also produce. Now there is a number. It exercises geometry the eight-bit legs cannot reach: P010 samples are two bytes, so a row is width * 2 rather than width, and HEVC's 128-line granule pads a 240-line picture to a 256-line surface — so the chroma plane starts a long way from where the display height alone would put it. Getting either wrong is the smeared-rows failure this project has already paid for once, and it would have looked like a decoder fault. The run body now takes the stream format and the expected access-unit count rather than assuming the eight-bit envelope and 250 frames. A CPU guard pins the vector at ten bits — 4:2:0, both depths minus8 == 2, 320x240, 50 access units. A regenerated eight-bit vector would otherwise turn this into a second run of the eight-bit path under a ten-bit name, passing, because its goldens would have been regenerated with it. Hardware: HEVC Main 10 50/50 bit-identical on the RTX 4090 and on the AMD Radeon iGPU, alongside the unchanged eight-bit legs at 250/250 on both. With the Vulkan leg's two drivers that is four independent drivers across two rungs for the ten-bit path, where yesterday there were none.
This commit is contained in:
@@ -1039,6 +1039,15 @@ mod parity {
|
||||
/// Both vendored vectors are 250 display frames.
|
||||
const FRAME_COUNT: usize = 250;
|
||||
|
||||
/// The Main 10 vector: 50 frames of 320x240 HEVC Main 10 4:2:0, generated by
|
||||
/// libx265 and hashed from libavcodec's software decode as tightly packed P010.
|
||||
/// Its provenance, the generation commands and the reason P010 rather than
|
||||
/// `yuv420p10le` is the golden layout are all in the golden file's header.
|
||||
const TEST_MAIN10_H265: &[u8] = include_bytes!("../../pf-vkdecode/tests/data/test-main10.h265");
|
||||
const GOLDENS_MAIN10: &str =
|
||||
include_str!("../../pf-vkdecode/tests/data/test-main10.p010.sha256");
|
||||
const MAIN10_FRAME_COUNT: usize = 50;
|
||||
|
||||
/// The golden file's hash lines (comments and blanks skipped).
|
||||
fn golden_hashes(file: &'static str) -> Vec<&'static str> {
|
||||
file.lines()
|
||||
@@ -1344,11 +1353,19 @@ mod parity {
|
||||
|
||||
/// Decode `aus` through a real `NativeD3d11Decoder`, hash every picture, and
|
||||
/// compare the planner's display order against libavcodec's goldens.
|
||||
fn parity_run(codec: Codec, aus: &[&[u8]], order: &Order, goldens: &[&str], label: &str) {
|
||||
fn parity_run(
|
||||
codec: Codec,
|
||||
stream: StreamFormat,
|
||||
aus: &[&[u8]],
|
||||
order: &Order,
|
||||
goldens: &[&str],
|
||||
expected_aus: usize,
|
||||
label: &str,
|
||||
) {
|
||||
assert_eq!(
|
||||
aus.len(),
|
||||
FRAME_COUNT,
|
||||
"{label}: the vector must split into {FRAME_COUNT} access units — a \
|
||||
expected_aus,
|
||||
"{label}: the vector must split into {expected_aus} access units — a \
|
||||
different count means this file's splitter disagrees with pf-bitstream's, \
|
||||
and nothing below it is meaningful"
|
||||
);
|
||||
@@ -1361,7 +1378,7 @@ mod parity {
|
||||
);
|
||||
|
||||
let luid = pinned_adapter();
|
||||
let mut decoder = NativeD3d11Decoder::new(codec, StreamFormat::SDR_420_8, luid, false)
|
||||
let mut decoder = NativeD3d11Decoder::new(codec, stream, luid, false)
|
||||
.unwrap_or_else(|e| panic!("{label}: the box must host this profile — {e:#}"));
|
||||
let mut readback = Readback {
|
||||
ctx: decoder.context.clone(),
|
||||
@@ -1422,9 +1439,11 @@ mod parity {
|
||||
let order = order_h264(&aus);
|
||||
parity_run(
|
||||
Codec::H264,
|
||||
StreamFormat::SDR_420_8,
|
||||
&aus,
|
||||
&order,
|
||||
&golden_hashes(GOLDENS_H264),
|
||||
FRAME_COUNT,
|
||||
"H.264",
|
||||
);
|
||||
}
|
||||
@@ -1436,13 +1455,45 @@ mod parity {
|
||||
let order = order_h265(&aus);
|
||||
parity_run(
|
||||
Codec::H265,
|
||||
StreamFormat::SDR_420_8,
|
||||
&aus,
|
||||
&order,
|
||||
&golden_hashes(GOLDENS_H265),
|
||||
FRAME_COUNT,
|
||||
"H.265",
|
||||
);
|
||||
}
|
||||
|
||||
/// The ten-bit path, which no golden set in this program covered until now.
|
||||
///
|
||||
/// The HDR legs proved a Main10 session BUILDS and streams clean, which is a
|
||||
/// weaker claim than it looks: D3D11VA exposes no per-picture status query, so a
|
||||
/// Main10 stream decoding to garbage logs exactly as cleanly as one decoding
|
||||
/// correctly. This is the leg that can tell them apart.
|
||||
///
|
||||
/// It also exercises geometry the 8-bit legs cannot: P010 samples are two bytes,
|
||||
/// so a row is `width * 2`, and HEVC's 128-line granule pads a 240-line picture
|
||||
/// to a 256-line surface — the chroma plane therefore starts a long way from
|
||||
/// where the display height would put it.
|
||||
#[test]
|
||||
#[ignore = "needs a Windows D3D11 video device (see module docs)"]
|
||||
fn main10_every_frame_hashes_bit_identical_to_libavcodec() {
|
||||
let aus = split_h265_aus(TEST_MAIN10_H265);
|
||||
let order = order_h265(&aus);
|
||||
parity_run(
|
||||
Codec::H265,
|
||||
StreamFormat {
|
||||
chroma_format_idc: 1,
|
||||
bit_depth: 10,
|
||||
},
|
||||
&aus,
|
||||
&order,
|
||||
&golden_hashes(GOLDENS_MAIN10),
|
||||
MAIN10_FRAME_COUNT,
|
||||
"HEVC Main 10",
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// CPU guards — NOT `#[ignore]`d, so ordinary CI notices when this file's
|
||||
// splitter or the goldens drift away from pf-bitstream.
|
||||
@@ -1471,6 +1522,45 @@ mod parity {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_main10_vector_really_is_ten_bit() {
|
||||
let aus = split_h265_aus(TEST_MAIN10_H265);
|
||||
assert_eq!(
|
||||
aus.len(),
|
||||
MAIN10_FRAME_COUNT,
|
||||
"the Main 10 vector is {MAIN10_FRAME_COUNT} access units"
|
||||
);
|
||||
let order = order_h265(&aus);
|
||||
assert_eq!(
|
||||
order.display.len(),
|
||||
golden_hashes(GOLDENS_MAIN10).len(),
|
||||
"the planner's output count must match the Main 10 golden count"
|
||||
);
|
||||
|
||||
// The point of the leg. A regenerated vector that came out 8-bit would make
|
||||
// `main10_every_frame_hashes_bit_identical_to_libavcodec` a second run of the
|
||||
// 8-bit path wearing a ten-bit name — and it would pass, because the goldens
|
||||
// would have been regenerated alongside it.
|
||||
let mut planner = H265Planner::new();
|
||||
let plan = planner
|
||||
.plan_au(aus[0])
|
||||
.expect("the Main 10 vector's first access unit must plan");
|
||||
assert_eq!(
|
||||
(
|
||||
plan.picture.chroma_format_idc,
|
||||
plan.picture.bit_depth_luma_minus8,
|
||||
plan.picture.bit_depth_chroma_minus8
|
||||
),
|
||||
(1, 2, 2),
|
||||
"the Main 10 vector must be 4:2:0 at ten bits"
|
||||
);
|
||||
assert_eq!(
|
||||
(plan.picture.coded_width, plan.picture.coded_height),
|
||||
(320, 240),
|
||||
"the golden frame size is 320x240"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn both_vendored_vectors_really_do_reorder() {
|
||||
// The module docs claim the harness must reorder because these vectors do. If
|
||||
|
||||
Reference in New Issue
Block a user