Skip to content

Commit 4ffcce4

Browse files
committed
fix(compositor): leave BT.2020 CL on the default path, convert 10-bit samples with 10-bit bounds
1 parent ce37988 commit 4ffcce4

2 files changed

Lines changed: 45 additions & 15 deletions

File tree

‎crates/compositor/src/compositor_macos.rs‎

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,16 @@ fn swizzled_view(tex: &metal::TextureRef, swizzle: [u8; 4]) -> metal::Texture {
235235
/// Le swizzle du plan Y qui porte la matrice de la frame, ou `None` pour BT.709 (aussi le
236236
/// défaut d'un flux non tagué). La matrice vient de `AVFrame::colorspace`, que libavcodec lit
237237
/// dans le VUI : VideoToolbox ne pose pas `kCVImageBufferYCbCrMatrixKey` sur ses buffers.
238-
unsafe fn matrix_swizzle(frame: *const AVFrame) -> Option<[u8; 4]> {
239-
match (*frame).colorspace {
238+
/// BT.2020 CL n'est pas une simple matrice : il garde le défaut plutôt qu'un rendu NCL faux.
239+
///
240+
/// Alpha à 0 : échantillons 10 bits (P010), que `yuv_matrix` convertit avec les bornes 10 bits.
241+
fn matrix_swizzle(colorspace: AVColorSpace::Type, ten_bit: bool) -> Option<[u8; 4]> {
242+
let depth = if ten_bit { SWZ_ZERO } else { SWZ_ONE };
243+
match colorspace {
240244
AVColorSpace::AVCOL_SPC_BT470BG | AVColorSpace::AVCOL_SPC_SMPTE170M => {
241-
Some([SWZ_RED, SWZ_ONE, SWZ_ZERO, SWZ_ONE])
242-
}
243-
AVColorSpace::AVCOL_SPC_BT2020_NCL | AVColorSpace::AVCOL_SPC_BT2020_CL => {
244-
Some([SWZ_RED, SWZ_ZERO, SWZ_ONE, SWZ_ONE])
245+
Some([SWZ_RED, SWZ_ONE, SWZ_ZERO, depth])
245246
}
247+
AVColorSpace::AVCOL_SPC_BT2020_NCL => Some([SWZ_RED, SWZ_ZERO, SWZ_ONE, depth]),
246248
_ => None,
247249
}
248250
}
@@ -1230,7 +1232,8 @@ impl Compositor {
12301232
};
12311233
let mut y = cache.make_texture_from_pixel_buffer(pb, 0, y_fmt)?;
12321234
let mut uv = cache.make_texture_from_pixel_buffer(pb, 1, uv_fmt)?;
1233-
if let Some(swizzle) = matrix_swizzle(frame) {
1235+
let ten_bit = matches!(pixel_format, CV_420_10_BIPLANAR_VIDEO | CV_420_10_BIPLANAR_FULL);
1236+
if let Some(swizzle) = matrix_swizzle((*frame).colorspace, ten_bit) {
12341237
y = swizzled_view(&y, swizzle);
12351238
}
12361239
if matches!(pixel_format, CV_420_8_BIPLANAR_FULL | CV_420_10_BIPLANAR_FULL) {
@@ -5451,4 +5454,19 @@ mod tests {
54515454
crate::shader_fx::compile_metal(&gpu.device, &code, &params).expect("compile");
54525455
eprintln!("recompilation d'une source déjà vue : {:.1} ms", t0.elapsed().as_secs_f64() * 1000.0);
54535456
}
5457+
5458+
#[test]
5459+
fn matrix_swizzle_marks_matrix_and_depth() {
5460+
use AVColorSpace::*;
5461+
assert_eq!(
5462+
matrix_swizzle(AVCOL_SPC_SMPTE170M, false),
5463+
Some([SWZ_RED, SWZ_ONE, SWZ_ZERO, SWZ_ONE])
5464+
);
5465+
assert_eq!(
5466+
matrix_swizzle(AVCOL_SPC_BT2020_NCL, true),
5467+
Some([SWZ_RED, SWZ_ZERO, SWZ_ONE, SWZ_ZERO])
5468+
);
5469+
assert_eq!(matrix_swizzle(AVCOL_SPC_BT709, false), None);
5470+
assert_eq!(matrix_swizzle(AVCOL_SPC_BT2020_CL, true), None);
5471+
}
54545472
}

‎crates/compositor/src/shaders.metal‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,21 @@ inline float3 yuv709_full(float y, float2 cbcr)
155155

156156
// BT.601 / BT.2020 -> RGB. `m` : coefficients Cr->R, Cb->G, Cr->G, Cb->B. BT.709 garde ses
157157
// deux fonctions à constantes littérales, pour rendre ses frames à l'octet près comme avant.
158-
inline float3 yuv_matrix(float y, float2 cbcr, float4 m, bool full)
159-
{
160-
float Yf = full ? y : (y * 255.0 - 16.0) / 219.0;
161-
float Cb = full ? cbcr.x - 128.0 / 255.0 : (cbcr.x * 255.0 - 128.0) / 224.0;
162-
float Cr = full ? cbcr.y - 128.0 / 255.0 : (cbcr.y * 255.0 - 128.0) / 224.0;
158+
// `ten_bit` : un échantillon P010 lu en `R16Unorm` vaut code10 * 64 / 65535, pas code8 / 255 ;
159+
// on revient au code 10 bits pour appliquer ses bornes (64..940, chroma 512, pleine 0..1023).
160+
inline float3 yuv_matrix(float y, float2 cbcr, float4 m, bool full, bool ten_bit)
161+
{
162+
float Yf, Cb, Cr;
163+
if (ten_bit) {
164+
float code = 65535.0 / 64.0;
165+
Yf = full ? y * code / 1023.0 : (y * code - 64.0) / 876.0;
166+
Cb = full ? (cbcr.x * code - 512.0) / 1023.0 : (cbcr.x * code - 512.0) / 896.0;
167+
Cr = full ? (cbcr.y * code - 512.0) / 1023.0 : (cbcr.y * code - 512.0) / 896.0;
168+
} else {
169+
Yf = full ? y : (y * 255.0 - 16.0) / 219.0;
170+
Cb = full ? cbcr.x - 128.0 / 255.0 : (cbcr.x * 255.0 - 128.0) / 224.0;
171+
Cr = full ? cbcr.y - 128.0 / 255.0 : (cbcr.y * 255.0 - 128.0) / 224.0;
172+
}
163173
float3 rgb;
164174
rgb.r = Yf + m.x * Cr;
165175
rgb.g = Yf - m.y * Cb - m.z * Cr;
@@ -178,11 +188,13 @@ inline float3 sample_yuv(float2 uv,
178188
float4 c = texUV.sample(samp, uv);
179189
// Bleu à 1 sur la vue CbCr : pleine plage (420f, xf20).
180190
bool full = c.b > 0.5;
181-
// Vert ou bleu à 1 sur la vue Y : matrice BT.601 ou BT.2020 posée par `nv12_srvs`.
191+
// Vert ou bleu à 1 sur la vue Y : matrice BT.601 ou BT.2020 posée par `nv12_srvs` ;
192+
// alpha à 0 sur cette vue : échantillons 10 bits.
193+
bool ten_bit = y.a < 0.5;
182194
if (y.g > 0.5)
183-
return yuv_matrix(y.r, c.rg, float4(1.402, 0.344136, 0.714136, 1.772), full);
195+
return yuv_matrix(y.r, c.rg, float4(1.402, 0.344136, 0.714136, 1.772), full, ten_bit);
184196
if (y.b > 0.5)
185-
return yuv_matrix(y.r, c.rg, float4(1.4746, 0.164553, 0.571353, 1.8814), full);
197+
return yuv_matrix(y.r, c.rg, float4(1.4746, 0.164553, 0.571353, 1.8814), full, ten_bit);
186198
return full ? yuv709_full(y.r, c.rg) : yuv709_limited(y.r, c.rg);
187199
}
188200

0 commit comments

Comments
 (0)