Skip to content

Commit 01ec75e

Browse files
fix(deps): add GbmUsage::Separated for per-plane R8 VA-API export
On Mesa iris (Tiger Lake), gbm_bo_create rejects the NV12 fourcc with every usage flag (HW_VIDEO_ENCODER, HW_VIDEO_DECODER, LINEAR). Add a GbmUsage::Separated variant that bypasses native NV12 allocation entirely: each plane is allocated as a separate R8 buffer with LINEAR, then exported to VA-API via a multi-object VADRMPRIMESurfaceDescriptor (one DMA-BUF FD per plane). Changes to the vendored cros-codecs: - GbmUsage::Separated enum variant - new_frame(): when usage is Separated, take the per-plane R8 path even for formats that are normally contiguous (NV12) - GbmExternalBufferDescriptor: store Vec<File> + object_indices instead of a single File, so multi-BO frames can be exported - to_native_handle(): handle both single-BO and multi-BO frames, creating the correct num_objects / object_index mapping Changes to the encoder/decoder nodes: - Four-level GBM probe: Encode → Decode → Linear → Separated - Decoder alloc callbacks: Decode → Linear → Separated fallback Signed-off-by: StreamKit Devin <devin@streamkit.dev> Co-Authored-By: Claudio Costa <cstcld91@gmail.com>
1 parent fb7fa52 commit 01ec75e

File tree

3 files changed

+95
-31
lines changed

3 files changed

+95
-31
lines changed

crates/nodes/src/video/vaapi_av1.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,20 @@ fn vaapi_av1_decode_loop(
580580
.clone()
581581
.new_frame(nv12_fourcc(), res.clone(), res.clone(), GbmUsage::Decode)
582582
.or_else(|_| {
583-
gbm_ref.clone().new_frame(nv12_fourcc(), res.clone(), res, GbmUsage::Linear)
583+
gbm_ref.clone().new_frame(
584+
nv12_fourcc(),
585+
res.clone(),
586+
res.clone(),
587+
GbmUsage::Linear,
588+
)
589+
})
590+
.or_else(|_| {
591+
gbm_ref.clone().new_frame(
592+
nv12_fourcc(),
593+
res.clone(),
594+
res,
595+
GbmUsage::Separated,
596+
)
584597
})
585598
.ok()
586599
};
@@ -946,10 +959,16 @@ impl StandardVideoEncoder for VaapiAv1Encoder {
946959
falling back to GBM_BO_USE_LINEAR for encoder input buffers"
947960
);
948961
GbmUsage::Linear
962+
} else if try_alloc(GbmUsage::Separated).is_ok() {
963+
tracing::warn!(
964+
"GBM rejects NV12 fourcc with all usage flags; \
965+
falling back to per-plane R8 allocation (GbmUsage::Separated)"
966+
);
967+
GbmUsage::Separated
949968
} else {
950969
return Err(format!(
951970
"GBM cannot allocate NV12 {coded_width}×{coded_height} buffers \
952-
with any supported usage flag (tried Encode, Decode, Linear)"
971+
with any supported usage flag (tried Encode, Decode, Linear, Separated)"
953972
));
954973
}
955974
};

crates/nodes/src/video/vaapi_h264.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,20 @@ fn vaapi_h264_decode_loop(
309309
.clone()
310310
.new_frame(nv12_fourcc(), res.clone(), res.clone(), GbmUsage::Decode)
311311
.or_else(|_| {
312-
gbm_ref.clone().new_frame(nv12_fourcc(), res.clone(), res, GbmUsage::Linear)
312+
gbm_ref.clone().new_frame(
313+
nv12_fourcc(),
314+
res.clone(),
315+
res.clone(),
316+
GbmUsage::Linear,
317+
)
318+
})
319+
.or_else(|_| {
320+
gbm_ref.clone().new_frame(
321+
nv12_fourcc(),
322+
res.clone(),
323+
res,
324+
GbmUsage::Separated,
325+
)
313326
})
314327
.ok()
315328
};
@@ -670,10 +683,16 @@ impl StandardVideoEncoder for VaapiH264Encoder {
670683
falling back to GBM_BO_USE_LINEAR for encoder input buffers"
671684
);
672685
GbmUsage::Linear
686+
} else if try_alloc(GbmUsage::Separated).is_ok() {
687+
tracing::warn!(
688+
"GBM rejects NV12 fourcc with all usage flags; \
689+
falling back to per-plane R8 allocation (GbmUsage::Separated)"
690+
);
691+
GbmUsage::Separated
673692
} else {
674693
return Err(format!(
675694
"GBM cannot allocate NV12 {coded_width}×{coded_height} buffers \
676-
with any supported usage flag (tried Encode, Decode, Linear)"
695+
with any supported usage flag (tried Encode, Decode, Linear, Separated)"
677696
));
678697
}
679698
};

vendor/cros-codecs/src/video_frame/gbm_video_frame.rs

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,15 @@ pub struct GbmExternalBufferDescriptor {
263263
resolution: Resolution,
264264
pitches: Vec<usize>,
265265
offsets: Vec<usize>,
266-
// We use a proper File object here to correctly manage the lifetimes of the exported FDs.
266+
// We use proper File objects here to correctly manage the lifetimes of the exported FDs.
267267
// Otherwise we risk exhausting the FD limit on the machine for certain test vectors.
268-
export_file: File,
268+
//
269+
// For contiguous (single-BO) frames this contains one file.
270+
// For separated (multi-BO) frames this contains one file per BO/plane.
271+
export_files: Vec<File>,
272+
/// Maps each plane to the index in `export_files` (and thus `objects[]`).
273+
/// For contiguous frames every plane maps to object 0.
274+
object_indices: Vec<u32>,
269275
}
270276

271277
#[cfg(feature = "vaapi")]
@@ -274,22 +280,27 @@ impl ExternalBufferDescriptor for GbmExternalBufferDescriptor {
274280
type DescriptorAttribute = VADRMPRIMESurfaceDescriptor;
275281

276282
fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute {
277-
let objects = [
278-
VADRMPRIMESurfaceDescriptorObject {
279-
fd: self.export_file.as_raw_fd(),
280-
size: self.export_file.metadata().unwrap().len() as u32,
283+
let num_objects = self.export_files.len() as u32;
284+
285+
let mut objects: [VADRMPRIMESurfaceDescriptorObject; 4] = Default::default();
286+
for (i, file) in self.export_files.iter().enumerate() {
287+
objects[i] = VADRMPRIMESurfaceDescriptorObject {
288+
fd: file.as_raw_fd(),
289+
size: file.metadata().unwrap().len() as u32,
281290
drm_format_modifier: self.modifier,
282-
},
283-
Default::default(),
284-
Default::default(),
285-
Default::default(),
286-
];
291+
};
292+
}
293+
294+
let mut object_index: [u32; 4] = [0; 4];
295+
for (i, &idx) in self.object_indices.iter().enumerate().take(4) {
296+
object_index[i] = idx;
297+
}
287298

288299
let layers = [
289300
VADRMPRIMESurfaceDescriptorLayer {
290301
drm_format: u32::from(self.fourcc),
291302
num_planes: self.pitches.len() as u32,
292-
object_index: [0, 0, 0, 0],
303+
object_index,
293304
offset: self
294305
.offsets
295306
.iter()
@@ -314,16 +325,15 @@ impl ExternalBufferDescriptor for GbmExternalBufferDescriptor {
314325
Default::default(),
315326
];
316327
let resolution = self.resolution;
317-
let ret = VADRMPRIMESurfaceDescriptor {
328+
VADRMPRIMESurfaceDescriptor {
318329
fourcc: u32::from(self.fourcc),
319330
width: resolution.width,
320331
height: resolution.height,
321-
num_objects: 1,
332+
num_objects,
322333
objects,
323334
num_layers: 1,
324335
layers,
325-
};
326-
ret
336+
}
327337
}
328338
}
329339

@@ -398,28 +408,38 @@ impl VideoFrame for GbmVideoFrame {
398408
if self.is_compressed() {
399409
return Err("Compressed buffer export to VA-API is not currently supported".to_string());
400410
}
401-
if !self.is_contiguous() {
402-
return Err(
403-
"Exporting non-contiguous GBM buffers to VA-API is not currently supported"
404-
.to_string(),
405-
);
406-
}
407411

408412
// TODO: Add more supported formats
409413
let rt_format = match self.decoded_format().unwrap() {
410414
DecodedFormat::I420 | DecodedFormat::NV12 => libva::VA_RT_FORMAT_YUV420,
411415
_ => return Err("Format unsupported for VA-API export".to_string()),
412416
};
413417

418+
// SAFETY: gbm_bo_get_fd returns a new, owned FD every time it is called,
419+
// so this should be safe as long as self.bo contains valid buffer objects.
420+
let (export_files, object_indices, offsets) = if self.bo.len() == 1 {
421+
// Contiguous: single BO backs all planes.
422+
let file = unsafe { File::from_raw_fd(gbm_bo_get_fd(self.bo[0])) };
423+
let num_planes = self.num_planes();
424+
(vec![file], vec![0u32; num_planes], self.get_plane_offset())
425+
} else {
426+
// Separated: one BO per plane (used by GbmUsage::Separated).
427+
// Each plane lives in its own BO, so the per-plane offset is 0.
428+
let files: Vec<File> =
429+
self.bo.iter().map(|bo| unsafe { File::from_raw_fd(gbm_bo_get_fd(*bo)) }).collect();
430+
let indices: Vec<u32> = (0..files.len() as u32).collect();
431+
let offsets = vec![0usize; files.len()];
432+
(files, indices, offsets)
433+
};
434+
414435
let export_descriptor = vec![GbmExternalBufferDescriptor {
415436
fourcc: self.fourcc.clone(),
416437
modifier: self.get_modifier(),
417438
resolution: self.resolution(),
418439
pitches: self.get_plane_pitch(),
419-
offsets: self.get_plane_offset(),
420-
// SAFETY: gbm_bo_get_fd returns a new, owned FD every time it is called, so this should
421-
// be safe as long as self.bo contains valid buffer objects.
422-
export_file: unsafe { File::from_raw_fd(gbm_bo_get_fd(self.bo[0])) },
440+
offsets,
441+
export_files,
442+
object_indices,
423443
}];
424444

425445
let mut ret = display
@@ -461,6 +481,11 @@ pub enum GbmUsage {
461481
/// Tiger Lake with Mesa 23.x). Linear buffers are universally supported
462482
/// but may be slower due to lack of tiling optimisations.
463483
Linear,
484+
/// Allocate each plane as a separate R8 buffer with `GBM_BO_USE_LINEAR`.
485+
/// This bypasses the native NV12 GBM allocation entirely, which is
486+
/// necessary on drivers where `gbm_bo_create` rejects the NV12 fourcc
487+
/// with every usage flag (e.g. Mesa iris on Intel Tiger Lake).
488+
Separated,
464489
}
465490

466491
#[derive(Debug)]
@@ -526,7 +551,7 @@ impl GbmDevice {
526551
}
527552

528553
ret.bo.push(bo);
529-
} else if ret.is_contiguous() {
554+
} else if ret.is_contiguous() && usage != GbmUsage::Separated {
530555
// These flags are not present in every system's GBM headers.
531556
const GBM_BO_USE_HW_VIDEO_DECODER: u32 = 1 << 13;
532557
const GBM_BO_USE_HW_VIDEO_ENCODER: u32 = 1 << 14;
@@ -541,6 +566,7 @@ impl GbmDevice {
541566
GbmUsage::Decode => GBM_BO_USE_HW_VIDEO_DECODER,
542567
GbmUsage::Encode => GBM_BO_USE_HW_VIDEO_ENCODER,
543568
GbmUsage::Linear => gbm_bo_flags::GBM_BO_USE_LINEAR as u32,
569+
GbmUsage::Separated => unreachable!(),
544570
};
545571
let bo = unsafe {
546572
gbm_bo_create(

0 commit comments

Comments
 (0)