Skip to content

feat(hwdec): VAAPI backend — HEVC Main/Main10 + AV1 still decode over dlopen'd libva - #51

Merged
justin13888 merged 1 commit into
masterfrom
29-hwdec-vaapi-backend
Jul 19, 2026
Merged

feat(hwdec): VAAPI backend — HEVC Main/Main10 + AV1 still decode over dlopen'd libva#51
justin13888 merged 1 commit into
masterfrom
29-hwdec-vaapi-backend

Conversation

@justin13888

Copy link
Copy Markdown
Collaborator

Closes #29.

Stacked on #50; merge order #42#44#45#46#47#48#49#50 → this.

What is Delivered

  • dlopen'd libva, no link-time dependency: libva.so.2/libva-drm.so.2 load at runtime via libloading behind a hand-written ~26-entry-point FFI surface. Missing library/render-node/driver → decoder() == None, never a link failure — headless-safe by construction (ldd on the test binary shows no libva). Every FFI struct layout is transcribed from the system va*.h headers and pinned by tests asserting size_of/offset_of against gcc-computed values (e.g. VAPictureParameterBufferHEVC = 604 B).
  • HEVC Main + Main10: hvcC parsing in-crate (the FFI crate takes raw hvcC bytes; upstream Annex-B helpers remain gamut#255), full SPS/PPS + IRAP I-slice header parse in safe Rust, VA parameter submission, still-picture scope with honest Decode errors outside it (documented on the module).
  • AV1 Profile0: av1C + OBU framing, sequence/intra-frame header parse (tiles incl. non-uniform spacing, segmentation, CDEF, loop restoration, film grain via a second display surface).
  • NV12 (8-bit) / P010 (10-bit) → DecodedFrame, honouring driver pitches, cropping to owned planes. available_codecs() answers from real vaQueryConfigProfiles.
  • Safety boundary held: parsers are 100 % safe Rust; unsafe is confined to FFI call sites — 34 unsafe blocks total, every one with a // SAFETY: comment (grep-audited). #![deny(unsafe_op_in_unsafe_fn)], all public items safe. NVIDIA-via-nvidia-vaapi-driver documented per docs/SUPPORT.md.

Verification Done — on real hardware

Host: AMD RX 7900 XT (Mesa radeonsi, libva 2.23). Run independently after implementation:

  • cargo test -p rawshift-hwdec --features vaapi38/0, device tests ran (not skipped): real x265 Main→NV12, Main10→P010, real libaom AV1→NV12 bitstream decodes with pixel-variance assertions, probe consistency, garbage-bitstream rejection.
  • End-to-end: heic_primary_hw_decodes_end_to_end_with_real_pixels and heic_thumbnail_hw_decodes_when_listed pass — HeicFile::decode_primary → gamut-heic → VAAPI → RgbImage with real pixel statistics. -p rawshift-image --features full542/0.
  • cargo test --workspace387/0 (stub config: 9 hwdec tests, device tests compile out). Clippy -D warnings ×3 configs — all clean. Stub/hw/release builds clean. Device tests skip gracefully without hardware → CI stays green.
  • Miri: not applicable to GPU-driver FFI; everything Miri could check is safe Rust.

Upstream

Filed gamut#304 during this work (decode_item_rgba8 rejects items carrying both ICC and nclx colr — the shape of real iPhone files). HDR presentation remains gamut#303: Main10→P010 decode works and is device-tested here; 10-bit RGB presentation waits upstream. SDR HEIC is now hardware-decoded end-to-end.

… dlopen'd libva

Implements the Linux hardware decode backend behind rawshift-hwdec's safe
API. libva.so.2/libva-drm.so.2 are dlopen'd at runtime via libloading with a
hand-written minimal FFI surface (~26 entry points): no link-time libva
dependency, and a missing library, render node, or driver degrades to
decoder() == None — headless-safe by construction. Every FFI struct layout is
transcribed from the system va*.h headers and pinned by unit tests asserting
size_of/offset_of against gcc-computed values.

HEVC Main and Main10: hvcC NAL-array parsing (in-crate — the FFI crate takes
raw hvcC bytes and has no gamut-heic dependency; upstream Annex-B helpers
remain gamut#255), full SPS/PPS and IRAP I-slice header parsing in safe Rust,
VA picture/slice parameter submission, still-picture scope (IDR/CRA single
frame) with clear Decode errors outside it. AV1 Profile0: av1C + OBU framing,
sequence and intra frame header parse including tile info, film grain (second
display surface), CDEF, and loop restoration. 8-bit surfaces read back as
Nv12, 10-bit as P010, honouring driver pitches and cropping to owned planes.
available_codecs() answers from real vaQueryConfigProfiles.

Unsafe stays confined to the FFI boundary per PRINCIPLES.md: bit-level
parsers are 100% safe Rust; every unsafe block carries a SAFETY comment
(34 blocks across vaapi/mod.rs + sys.rs, grep-audited). NVIDIA support via
nvidia-vaapi-driver is documented per docs/SUPPORT.md.

Verified on real hardware (AMD RX 7900 XT, Mesa radeonsi): device-gated
tests decode real x265 Main (NV12), Main10 (P010), and libaom AV1 (NV12)
bitstreams, and the end-to-end path HeicFile::decode_primary -> gamut-heic
-> VAAPI -> RgbImage passes with real pixel statistics, including a
hardware-decoded HEIC thumbnail. All device tests skip gracefully without a
device, keeping CI green.

Upstream: filed visualcommons/gamut#304 (decode_item_rgba8 rejects items
carrying both ICC and nclx colr — the shape of real iPhone files); the HDR
presentation gap remains gamut#303 (Main10 decode works; 10-bit RGB
presentation waits upstream).
Base automatically changed from 27-hwdec-heic-gamut to master July 19, 2026 01:41
@justin13888
justin13888 merged commit 0c16610 into master Jul 19, 2026
@justin13888
justin13888 deleted the 29-hwdec-vaapi-backend branch July 19, 2026 01:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hardware decode: VAAPI backend (Linux, HEVC + AV1, dlopen)

1 participant