Skip to content

feat(sinker): Ship 8 — Yuv422p / NV16 RGBA (kernel reuse, no new SIMD)#18

Merged
uqio merged 1 commit intomainfrom
feat/ship8-rgba-yuv422p-nv16
Apr 26, 2026
Merged

feat(sinker): Ship 8 — Yuv422p / NV16 RGBA (kernel reuse, no new SIMD)#18
uqio merged 1 commit intomainfrom
feat/ship8-rgba-yuv422p-nv16

Conversation

@uqio
Copy link
Copy Markdown
Collaborator

@uqio uqio commented Apr 26, 2026

Tranche 3 of Ship 8 sink-side RGBA. Wiring-only PR — no new SIMD code. The 4:2:2 formats reuse the 4:2:0 kernels from tranches 1 + 2: Yuv422p calls yuv_420_to_rgba_row (already shipped in PR #16), and Nv16 calls nv12_to_rgba_row (already shipped in PR #17). 4:2:2's per-row contract is identical to 4:2:0's — half-width chroma, one pair per Y pair — so the same kernel handles both with no changes.

Scope

Sink-side only, default opaque alpha (0xFF). Per the tracker in docs/color-conversion-functions.md § Ship 8, this is tranche 3 of 7+:

# Tranche Formats Status
1 4:2:0 planar Yuv420p ✅ shipped (PR #16)
2 4:2:0 semi-planar Nv12, Nv21 ✅ shipped (PR #17)
3 4:2:2 planar + semi-planar Yuv422p, Nv16 this PR
4 4:4:4 + 4:4:0 Yuv444p, Nv24, Nv42, Yuv440p next
5 High-bit-depth 4:2:0 Yuv420p9/10/12/14/16, P010/P012/P016
6 High-bit-depth 4:2:2 Yuv422p9/10/12/14/16, Yuv440p10/12, P210/P212/P216
7 High-bit-depth 4:4:4 Yuv444p9/10/12/14/16, P410/P412/P416

Usage:

use colconv::{
    frame::{Yuv422pFrame, Nv16Frame},
    sinker::MixedSinker,
    yuv::{Yuv422p, Nv16, yuv422p_to, nv16_to},
    ColorMatrix,
};

// Yuv422p (planar): half-width chroma, full-height
let frame = Yuv422pFrame::new(&y_plane, &u_plane, &v_plane, w, h, w, w/2, w/2);
let mut rgba = vec![0u8; (w * h * 4) as usize];
let mut sinker = MixedSinker::<Yuv422p>::new(w as usize, h as usize)
    .with_rgba(&mut rgba)?;
yuv422p_to(&frame, true, ColorMatrix::Bt709, &mut sinker)?;
// rgba[4*i..4*i+3] = R,G,B; rgba[4*i+3] = 0xFF.

MixedSinker::<Nv16> works identically with Nv16Frame + nv16_to (semi-planar interleaved UV).

What's in this PR

Public API

  • MixedSinker<Yuv422p>::with_rgba(&mut [u8]) / set_rgba — format-specific impl block, mirrors the Yuv420p pattern.
  • MixedSinker<Nv16>::with_rgba(&mut [u8]) / set_rgba — format-specific impl block, mirrors the NV12 pattern.

No new public dispatchers — the existing row::yuv_420_to_rgba_row and row::nv12_to_rgba_row are already on the public surface and handle 4:2:2 byte-identically.

Kernel work

None. Per the existing comments in src/sinker/mixed.rs:

4:2:2 is 4:2:0's vertical-axis twin: same per-row chroma shape (half-width U / V, one pair per Y pair), just one chroma row per Y row instead of one per two. This impl reuses yuv_420_to_rgb_row — no new kernels needed.

That comment is now extended to cover the RGBA path too. Both MixedSinker<Yuv422p>::process and MixedSinker<Nv16>::process invoke the existing _to_rgba_row dispatchers as an independent kernel call (not compose) — same pattern as Yuv420p/NV12 from prior tranches.

Doc updates

  • docs/color-conversion-functions.md § Ship 8 tranche tracker — tranche 3 marked ✅ shipped, tranche 4 (Yuv444p / Nv24 / Nv42 / Yuv440p) promoted to "next".
  • The compile_fail doctest negative example on MixedSinker::<Yuv420p>::with_rgba moved forward from Nv16 to Yuv444p (the next not-yet-wired format per the tranche tracker).

Tests

+10 lib tests, total 453 (was 443):

Layer Tests added
Format-level Yuv422p 5: gray-to-gray + opaque alpha, RGB-byte invariant, buffer-too-short, random-YUV SIMD parity, cross-format invariant against Yuv420p RGBA (with solid chroma so 4:2:0 vertical upsample matches 4:2:2's per-row chroma)
Format-level Nv16 5: same shape, with cross-format invariant against NV12 RGBA (duplicated chroma rows so 4:2:0 upsample matches NV16's per-row chroma — mirrors the existing nv16_matches_nv12_mixed_sinker_with_duplicated_chroma RGB-path test)

No per-backend equivalence tests — the underlying kernels (yuv_420_to_rgba_row and nv12_to_rgba_row) are already exercised by the per-backend tests shipped in PR #16 and PR #17. The tranche-3 tests cover the wiring (MixedSinker integration, dispatcher routing); the kernel correctness is established.

Local results (aarch64 macOS): 453 lib tests + 1 doctest pass; wasm32 + x86_64 cross-targets compile clean.

CI matrix coverage is unchanged from PR #17 — the same SSE4.1 / AVX2 / AVX-512 (via SDE) / NEON / wasm32 paths are exercised via the existing per-tier colconv_disable_* rustflags. The new wiring tests run under each tier.

What's deferred

Test plan

  • CI green on test, test-sde-avx512, cross, coverage, clippy, build, miri-* jobs.
  • Verify Yuv422p → RGBA + Nv16 → RGBA pipelines end-to-end with real frames.
  • cargo doc --lib --no-deps clean (no new doc warnings vs. main).

🤖 Generated with Claude Code

@al8n al8n requested a review from Copilot April 26, 2026 04:55
@al8n al8n changed the title feat feat(sinker): Ship 8 — Yuv422p / NV16 RGBA (kernel reuse, no new SIMD) Apr 26, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds native RGBA output support to additional MixedSinker YUV formats, expanding parity with existing RGB/Luma/HSV paths and validating behavior with new test coverage.

Changes:

  • Update MixedSinker<Yuv420p>::with_rgba compile-fail doc example to use a still-not-wired format (Yuv444p).
  • Add with_rgba/set_rgba APIs and RGBA row writing to MixedSinker<Yuv422p> and MixedSinker<Nv16>.
  • Add comprehensive NV16 and Yuv422p RGBA tests (gray/alpha defaults, RGB-vs-RGBA consistency, buffer-too-short, SIMD-vs-scalar, and cross-format invariants).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/sinker/mixed.rs
Comment on lines +11406 to +11412
// ---- Yuv422p RGBA (Ship 8 PR 3) tests -----------------------------------
//
// Yuv422p reuses the Yuv420p `_to_rgba_row` dispatcher (same row
// contract). Tests mirror the Yuv420p RGBA set; the cross-format
// invariant against Yuv420p (with solid chroma so 4:2:0 vertical
// upsample matches Yuv422p's per-row chroma) catches walker
// regressions specific to the Yuv422p RGBA wiring.
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section header/comment includes an internal milestone label ("Ship 8 PR 3"), which will become stale and isn’t useful context for future readers. Consider renaming to a descriptive, format-focused header (e.g., "Yuv422p RGBA tests").

Copilot uses AI. Check for mistakes.
Comment thread src/sinker/mixed.rs
Comment on lines +9014 to +9021
// ---- NV16 RGBA (Ship 8 PR 3) tests --------------------------------------
//
// NV16 reuses the NV12 `_to_rgba_row` dispatcher (4:2:2's row
// contract is identical to NV12's). Tests mirror the NV12 set;
// the cross-format invariant against NV12 (with duplicated
// chroma rows so 4:2:0 vertical upsample matches NV16's per-row
// chroma) catches any wiring regression specific to the NV16
// walker that the kernel-level tests don't cover.
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The section header/comment mentions an internal milestone ("Ship 8 PR 3"), which is not meaningful long-term and makes the test suite harder to maintain/search. Prefer a timeless, descriptive header (e.g., "NV16 RGBA tests").

Copilot uses AI. Check for mistakes.
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

2 participants