Fixes AV1 end-to-end. The fault was host-side and had been there since AV1 was wired up: every 4K AV1 frame this host encoded reached the wire truncated to its first tile.
What was actually wrong
The two halves of NVENC sub-frame readback are armed by different conditions:
the writer (enableSubFrameWrite + reportSliceOffsets) is armed by build_init_params from subframe_on alone;
the reader (poll_chunk's subframe_chunks latch) additionally requires slices >= 2 — and resolve_slices returns 1 for AV1 unconditionally, before the PUNKTFUNK_NVENC_SLICES override is even read, because AV1 partitions via tiles rather than slices.
So an AV1 session told the driver to publish its output tile by tile and then read it with a single blocking lock_bitstream, which returns only the first completed unit. At 1080p there is one tile and nothing is lost; at 4K split encode gives the frame two tile rows and half the picture was dropped.
Measured on .21 (RTX 5070 Ti, 4K60, split AUTO): every AU carried a frame header declaring two tile rows and a single Tile Group OBU with tg_start = tg_end = 0, and libdav1d rejected 835 of 836 access units with Error parsing frame header.
NVIDIA's hardware decoder accepts the truncated stream. That is the whole reason this survived: native Vulkan Video looked healthy at 60 fps while both conformant software decoders — rav1d in-tree and libdav1d out-of-tree — refused every frame.
The fix
resolve_split_subframe — the one arbitration point both direct-SDK backends already call — disarms sub-frame for AV1 and returns split_modeuntouched. AV1 keeps every engine split encode gives it; this costs no split.
Arming the reader for AV1 instead is not a drop-in alternative: poll_chunk cuts at bitstreamSizeInBytes on the reasoning that "slices are contiguous Annex-B", which AV1's OBUs are not.
Verified on hardware
.21 running this build at the real default (no env override), split_mode=0 AUTO, engines=2:
before
after
libdav1d over a 4K AV1 capture
835/836 AUs rejected, 0 frames decoded
715/715 frames clean, 0 errors
client, native Vulkan
60 fps but half-frames
60 fps, 74.2 Mb/s, decode 6.6 ms
client, native D3D11VA (.221)
72× reference picture N holds no DPB slot → demoted
60 fps, decode 1.9 ms, no demotion
client, software 1080p
Error parsing OBU data ×497
40 fps, decode 2.1 ms
HEVC is untouched and still gets its sub-frame chunked poll (subframe=true, slices=4) — asserted by the pre-existing tests, which still pass.
Gates: fmt, clippy --workspace --all-targets -D warnings, pf-client-core 163/163, pf-bitstream + pf-vkdecode, and gpu_parity on .21 — all 8 legs bit-identical to libavcodec.
Tests
The test that pinned this as correct was av1_untouched ("both features are legal together"). Legal for the driver, yes — but not for a caller whose reader can never arm. It is replaced by one that pins the disarm, and by one that checks the reader's gate against the writer's — the comparison nothing made. The Linux latch comment claiming the two "can't disagree" is corrected; that claim is what made this invisible.
Two things this exposes, not fixed here
The D3D11VA AV1 rung works — first hardware decode ever for that pair. Its M7 wiring was right all along; what looked like a DXVA reference-mapping bug was the truncated stream. It stays verified = false on purpose: that flag gates whether auto may pick it ahead of Vulkan Video, and one 25-second session with no parity check and no soak does not earn that. The note no longer claims it has never decoded. Promoting it wants a deliberate gpu_parity-style run.
rav1d 1.1.0 aborts the process on 4K AV1. With a whole stream it now gets much further, then takes an internal error path and panics inside its own on_error (decode.rs:4997, unwrap() on a None frame header). The panic crosses the extern "C" boundary in dav1d_send_data, so it is panic_cannot_unwind — an abort, which no rung demotion or NoSoftwareRung refusal can catch. 1080p is fine and libdav1d decodes the same 4K stream 715/715, so this is rav1d's own defect. Recorded in the evidence table where the next person to reach that rung will see it.
Fixes AV1 end-to-end. The fault was host-side and had been there since AV1 was wired up: **every 4K AV1 frame this host encoded reached the wire truncated to its first tile.**
## What was actually wrong
The two halves of NVENC sub-frame readback are armed by different conditions:
* the **writer** (`enableSubFrameWrite` + `reportSliceOffsets`) is armed by `build_init_params` from `subframe_on` alone;
* the **reader** (`poll_chunk`'s `subframe_chunks` latch) additionally requires `slices >= 2` — and `resolve_slices` returns 1 for AV1 *unconditionally*, before the `PUNKTFUNK_NVENC_SLICES` override is even read, because AV1 partitions via tiles rather than slices.
So an AV1 session told the driver to publish its output tile by tile and then read it with a single blocking `lock_bitstream`, which returns only the first completed unit. At 1080p there is one tile and nothing is lost; at 4K split encode gives the frame two tile rows and half the picture was dropped.
Measured on `.21` (RTX 5070 Ti, 4K60, split AUTO): every AU carried a frame header declaring two tile rows and a single Tile Group OBU with `tg_start = tg_end = 0`, and **libdav1d rejected 835 of 836 access units** with `Error parsing frame header`.
NVIDIA's hardware decoder accepts the truncated stream. That is the whole reason this survived: native Vulkan Video looked healthy at 60 fps while both conformant software decoders — rav1d in-tree and libdav1d out-of-tree — refused every frame.
## The fix
`resolve_split_subframe` — the one arbitration point both direct-SDK backends already call — disarms sub-frame for AV1 and returns `split_mode` **untouched**. AV1 keeps every engine split encode gives it; this costs no split.
Arming the reader for AV1 instead is not a drop-in alternative: `poll_chunk` cuts at `bitstreamSizeInBytes` on the reasoning that "slices are contiguous Annex-B", which AV1's OBUs are not.
## Verified on hardware
`.21` running this build at the real default (no env override), `split_mode=0` AUTO, `engines=2`:
| | before | after |
|---|---|---|
| libdav1d over a 4K AV1 capture | 835/836 AUs rejected, 0 frames decoded | **715/715 frames clean, 0 errors** |
| client, native Vulkan | 60 fps but half-frames | 60 fps, 74.2 Mb/s, decode 6.6 ms |
| client, native D3D11VA (.221) | 72× `reference picture N holds no DPB slot` → demoted | **60 fps, decode 1.9 ms, no demotion** |
| client, software 1080p | `Error parsing OBU data` ×497 | **40 fps, decode 2.1 ms** |
HEVC is untouched and still gets its sub-frame chunked poll (`subframe=true`, `slices=4`) — asserted by the pre-existing tests, which still pass.
Gates: `fmt`, `clippy --workspace --all-targets -D warnings`, `pf-client-core` 163/163, `pf-bitstream` + `pf-vkdecode`, and **`gpu_parity` on `.21` — all 8 legs bit-identical to libavcodec**.
## Tests
The test that pinned this as correct was `av1_untouched` ("both features are legal together"). Legal for the *driver*, yes — but not for a caller whose reader can never arm. It is replaced by one that pins the disarm, and by one that checks the reader's gate against the writer's — the comparison nothing made. The Linux latch comment claiming the two "can't disagree" is corrected; that claim is what made this invisible.
## Two things this exposes, not fixed here
1. **The D3D11VA AV1 rung works** — first hardware decode ever for that pair. Its M7 wiring was right all along; what looked like a DXVA reference-mapping bug was the truncated stream. It stays `verified = false` on purpose: that flag gates whether `auto` may pick it ahead of Vulkan Video, and one 25-second session with no parity check and no soak does not earn that. The note no longer claims it has never decoded. Promoting it wants a deliberate `gpu_parity`-style run.
2. **rav1d 1.1.0 aborts the process on 4K AV1.** With a whole stream it now gets much further, then takes an internal error path and panics inside its own `on_error` (`decode.rs:4997`, `unwrap()` on a `None` frame header). The panic crosses the `extern "C"` boundary in `dav1d_send_data`, so it is `panic_cannot_unwind` — an abort, which no rung demotion or `NoSoftwareRung` refusal can catch. 1080p is fine and libdav1d decodes the same 4K stream 715/715, so this is rav1d's own defect. Recorded in the evidence table where the next person to reach that rung will see it.
Every 4K AV1 frame this host encoded reached the wire truncated to its
first tile, and had since AV1 was wired up. Measured on .21 (RTX 5070 Ti,
4K60, split AUTO): each access unit carried a frame header declaring two
tile rows and a single Tile Group OBU with tg_start = tg_end = 0, so
libdav1d rejected 835 of 836 AUs with "Error parsing frame header".
NVIDIA's hardware decoder accepts the truncated stream, which is why
native Vulkan Video looked healthy at 60 fps while both conformant
software decoders — rav1d in-tree and libdav1d out-of-tree — refused
every frame and clients fell to a black screen.
The two halves of sub-frame readback are armed by different conditions.
build_init_params arms the WRITER (enableSubFrameWrite +
reportSliceOffsets) from subframe_on alone; the chunked READER
additionally requires slices >= 2, and resolve_slices returns 1 for AV1
unconditionally — before the PUNKTFUNK_NVENC_SLICES override is even
read, because AV1 partitions via tiles rather than slices. So an AV1
session asked the driver to publish its output tile by tile and then took
only the first tile with one blocking lock_bitstream.
resolve_split_subframe — the one arbitration point both direct-SDK
backends already call — now disarms sub-frame for AV1 and returns
split_mode untouched, so AV1 keeps every engine split encode gives it.
Arming the reader instead is not a drop-in alternative: poll_chunk cuts
at bitstreamSizeInBytes on the reasoning that "slices are contiguous
Annex-B", which AV1's OBUs are not.
With sub-frame disarmed and split still AUTO, the same session decodes
654/654 frames clean through libdav1d.
The test that pinned this as correct (av1_untouched, "both features are
legal together") is replaced by one that pins the disarm, and by one that
checks the reader's gate against the writer's — the comparison nothing
made. The Linux latch comment claiming the two "can't disagree" is
corrected; that claim is what made this invisible.
Two of its notes became false the moment the host stopped truncating AV1.
native D3D11VA / AV1 said "NEVER decoded a frame on any hardware". It has
now decoded 4K60 on an RTX 3500 Ada — and the same run is why the note
matters: its warn line named the rung as unproven moments before it
failed 72 access units running with "reference picture N holds no DPB
slot". That was the host shipping half of every frame, not the rung, so
the M7 wiring was right all along.
It stays UNVERIFIED regardless. `verified` gates `native_rung_admitted` —
whether `auto` may pick this rung ahead of Vulkan Video — and one
25-second session with no frame-hash parity and no soak does not buy
that. Promoting it wants a deliberate gpu_parity-style run. The note now
says what is true instead of what is convenient.
software / AV1 said rav1d had "CPU unit tests only". rav1d has now run on
glass: 1080p AV1 decodes, and 4K ABORTS THE PROCESS. It takes an internal
error path and panics inside its own on_error (rav1d 1.1.0
decode.rs:4997, unwrap on a None frame header); the panic crosses the
extern "C" boundary in dav1d_send_data, so it is panic_cannot_unwind and
no rung demotion or NoSoftwareRung refusal can catch it. libdav1d decodes
the same 4K stream 715/715, so this is rav1d's own defect and is recorded
where the next person to reach that rung will see it.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes AV1 end-to-end. The fault was host-side and had been there since AV1 was wired up: every 4K AV1 frame this host encoded reached the wire truncated to its first tile.
What was actually wrong
The two halves of NVENC sub-frame readback are armed by different conditions:
enableSubFrameWrite+reportSliceOffsets) is armed bybuild_init_paramsfromsubframe_onalone;poll_chunk'ssubframe_chunkslatch) additionally requiresslices >= 2— andresolve_slicesreturns 1 for AV1 unconditionally, before thePUNKTFUNK_NVENC_SLICESoverride is even read, because AV1 partitions via tiles rather than slices.So an AV1 session told the driver to publish its output tile by tile and then read it with a single blocking
lock_bitstream, which returns only the first completed unit. At 1080p there is one tile and nothing is lost; at 4K split encode gives the frame two tile rows and half the picture was dropped.Measured on
.21(RTX 5070 Ti, 4K60, split AUTO): every AU carried a frame header declaring two tile rows and a single Tile Group OBU withtg_start = tg_end = 0, and libdav1d rejected 835 of 836 access units withError parsing frame header.NVIDIA's hardware decoder accepts the truncated stream. That is the whole reason this survived: native Vulkan Video looked healthy at 60 fps while both conformant software decoders — rav1d in-tree and libdav1d out-of-tree — refused every frame.
The fix
resolve_split_subframe— the one arbitration point both direct-SDK backends already call — disarms sub-frame for AV1 and returnssplit_modeuntouched. AV1 keeps every engine split encode gives it; this costs no split.Arming the reader for AV1 instead is not a drop-in alternative:
poll_chunkcuts atbitstreamSizeInByteson the reasoning that "slices are contiguous Annex-B", which AV1's OBUs are not.Verified on hardware
.21running this build at the real default (no env override),split_mode=0AUTO,engines=2:reference picture N holds no DPB slot→ demotedError parsing OBU data×497HEVC is untouched and still gets its sub-frame chunked poll (
subframe=true,slices=4) — asserted by the pre-existing tests, which still pass.Gates:
fmt,clippy --workspace --all-targets -D warnings,pf-client-core163/163,pf-bitstream+pf-vkdecode, andgpu_parityon.21— all 8 legs bit-identical to libavcodec.Tests
The test that pinned this as correct was
av1_untouched("both features are legal together"). Legal for the driver, yes — but not for a caller whose reader can never arm. It is replaced by one that pins the disarm, and by one that checks the reader's gate against the writer's — the comparison nothing made. The Linux latch comment claiming the two "can't disagree" is corrected; that claim is what made this invisible.Two things this exposes, not fixed here
The D3D11VA AV1 rung works — first hardware decode ever for that pair. Its M7 wiring was right all along; what looked like a DXVA reference-mapping bug was the truncated stream. It stays
verified = falseon purpose: that flag gates whetherautomay pick it ahead of Vulkan Video, and one 25-second session with no parity check and no soak does not earn that. The note no longer claims it has never decoded. Promoting it wants a deliberategpu_parity-style run.rav1d 1.1.0 aborts the process on 4K AV1. With a whole stream it now gets much further, then takes an internal error path and panics inside its own
on_error(decode.rs:4997,unwrap()on aNoneframe header). The panic crosses theextern "C"boundary indav1d_send_data, so it ispanic_cannot_unwind— an abort, which no rung demotion orNoSoftwareRungrefusal can catch. 1080p is fine and libdav1d decodes the same 4K stream 715/715, so this is rav1d's own defect. Recorded in the evidence table where the next person to reach that rung will see it.