Skip to main content

moq_vaapi/
surface.rs

1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::any::Any;
6use std::os::fd::FromRawFd;
7use std::os::fd::OwnedFd;
8use std::os::raw::c_void;
9use std::sync::Arc;
10
11use crate::bindings;
12use crate::display::Display;
13use crate::va_check;
14use crate::UsageHint;
15use crate::VASurfaceID;
16use crate::VaError;
17
18/// Trait describing a memory backing for surfaces.
19///
20/// Using external memory for backing a VA surface is done in two steps:
21///
22/// 1. Build the descriptor specific to the memory type we want to use,
23/// 2. Mention this descriptor as an attribute to be passed to `vaDeriveImage`.
24///
25/// This trait allows to do that in a as-safe-as-possible way, by adding the required attributes
26/// and returning a heap-allocated object containing the data pointed to by the attributes. That
27/// way, the caller can keep that object alive until `vaCreateSurfaces` has returned.
28pub trait SurfaceMemoryDescriptor {
29	/// Add the required attributes to `attr` in order to attach the memory of this descriptor to
30	/// the surface when it is created.
31	///
32	/// The returned object, if any, is the descriptor pointed to by the attributes. The caller
33	/// must keep it around and unmoved until `vaCreateSurfaces` has returned.
34	fn add_attrs(&mut self, attrs: &mut Vec<bindings::VASurfaceAttrib>) -> Option<Box<dyn Any>>;
35}
36
37/// VA memory types, aka `VA_SURFACE_ATTRIB_MEM_TYPE_*`.
38#[repr(u32)]
39pub enum MemoryType {
40	Va = bindings::VA_SURFACE_ATTRIB_MEM_TYPE_VA,
41	V4L2 = bindings::VA_SURFACE_ATTRIB_MEM_TYPE_V4L2,
42	UserPtr = bindings::VA_SURFACE_ATTRIB_MEM_TYPE_USER_PTR,
43	DrmPrime2 = bindings::VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
44}
45
46/// Used when we want the VA driver to allocate surface memory for us. In this case we don't need
47/// to add any specific attribute for surface creation.
48impl SurfaceMemoryDescriptor for () {
49	fn add_attrs(&mut self, _: &mut Vec<bindings::VASurfaceAttrib>) -> Option<Box<dyn Any>> {
50		None
51	}
52}
53
54/// Sealed trait pattern to avoid reimplementation of our local traits.
55mod private {
56	pub trait Sealed {}
57}
58
59/// Trait for types that can be used as a `VASurfaceAttribExternalBufferDescriptor`.
60pub trait SurfaceExternalDescriptor: private::Sealed {}
61impl private::Sealed for bindings::VASurfaceAttribExternalBuffers {}
62impl SurfaceExternalDescriptor for bindings::VASurfaceAttribExternalBuffers {}
63impl private::Sealed for bindings::VADRMPRIMESurfaceDescriptor {}
64impl SurfaceExternalDescriptor for bindings::VADRMPRIMESurfaceDescriptor {}
65
66/// Trait allowing to import an external memory source to use with a surface by setting the
67/// `VASurfaceAttribMemoryType` and `VASurfaceAttribExternalBuffers` attributes.
68pub trait ExternalBufferDescriptor {
69	/// Memory type to set for `VASurfaceAttribMemoryType`.
70	const MEMORY_TYPE: MemoryType;
71	/// Type of the descriptor to be set with `VASurfaceAttribExternalBuffers`.
72	type DescriptorAttribute: SurfaceExternalDescriptor;
73
74	/// Returns the `Self::DescriptorAttribute` instance allowing this memory to be imported
75	/// into VAAPI.
76	fn va_surface_attribute(&mut self) -> Self::DescriptorAttribute;
77}
78
79impl<T> SurfaceMemoryDescriptor for T
80where
81	T: ExternalBufferDescriptor,
82	<T as ExternalBufferDescriptor>::DescriptorAttribute: 'static,
83{
84	fn add_attrs(&mut self, attrs: &mut Vec<bindings::VASurfaceAttrib>) -> Option<Box<dyn Any>> {
85		let mut desc = Box::new(self.va_surface_attribute());
86
87		attrs.push(bindings::VASurfaceAttrib::new_memory_type(Self::MEMORY_TYPE));
88		attrs.push(bindings::VASurfaceAttrib::new_buffer_descriptor(desc.as_mut()));
89
90		Some(desc)
91	}
92}
93
94/// Decode error type aka `VADecodeErrorType`
95#[repr(u32)]
96#[derive(Debug)]
97pub enum DecodeErrorType {
98	SliceMissing = bindings::VADecodeErrorType::VADecodeSliceMissing,
99	MBError = bindings::VADecodeErrorType::VADecodeMBError,
100	#[cfg(libva_1_20_or_higher)]
101	Reset = bindings::VADecodeErrorType::VADecodeReset,
102}
103
104/// Decode error details extracted from `VASurfaceDecodeMBErrors`, result of vaQuerySurfaceError.
105#[derive(Debug)]
106pub struct SurfaceDecodeMBError {
107	/// Start mb address with errors
108	pub start_mb: u32,
109
110	/// End mb address with errors
111	pub end_mb: u32,
112
113	pub decode_error_type: DecodeErrorType,
114
115	/// Number of mbs with errors
116	pub num_mb: u32,
117}
118
119/// An owned VA surface that is tied to a particular `Display`.
120pub struct Surface<D: SurfaceMemoryDescriptor> {
121	display: Arc<Display>,
122	id: bindings::VASurfaceID,
123	descriptor: D,
124	width: u32,
125	height: u32,
126}
127
128impl From<i32> for bindings::VAGenericValue {
129	fn from(i: i32) -> Self {
130		Self {
131			type_: bindings::VAGenericValueType::VAGenericValueTypeInteger,
132			value: bindings::_VAGenericValue__bindgen_ty_1 { i },
133		}
134	}
135}
136
137impl From<f32> for bindings::VAGenericValue {
138	fn from(f: f32) -> Self {
139		Self {
140			type_: bindings::VAGenericValueType::VAGenericValueTypeFloat,
141			value: bindings::_VAGenericValue__bindgen_ty_1 { f },
142		}
143	}
144}
145
146impl From<*mut c_void> for bindings::VAGenericValue {
147	fn from(p: *mut c_void) -> Self {
148		Self {
149			type_: bindings::VAGenericValueType::VAGenericValueTypePointer,
150			value: bindings::_VAGenericValue__bindgen_ty_1 { p },
151		}
152	}
153}
154
155/// Helpers to build valid `VASurfaceAttrib`s.
156impl bindings::VASurfaceAttrib {
157	pub fn new_pixel_format(fourcc: u32) -> Self {
158		Self {
159			type_: bindings::VASurfaceAttribType::VASurfaceAttribPixelFormat,
160			flags: bindings::VA_SURFACE_ATTRIB_SETTABLE,
161			value: bindings::VAGenericValue::from(fourcc as i32),
162		}
163	}
164
165	pub fn new_usage_hint(usage_hint: UsageHint) -> Self {
166		Self {
167			type_: bindings::VASurfaceAttribType::VASurfaceAttribUsageHint,
168			flags: bindings::VA_SURFACE_ATTRIB_SETTABLE,
169			value: bindings::VAGenericValue::from(usage_hint.bits() as i32),
170		}
171	}
172
173	pub fn new_memory_type(mem_type: MemoryType) -> Self {
174		Self {
175			type_: bindings::VASurfaceAttribType::VASurfaceAttribMemoryType,
176			flags: bindings::VA_SURFACE_ATTRIB_SETTABLE,
177			value: bindings::VAGenericValue::from(mem_type as i32),
178		}
179	}
180
181	pub fn new_buffer_descriptor<T: SurfaceExternalDescriptor>(desc: &mut T) -> Self {
182		Self {
183			type_: bindings::VASurfaceAttribType::VASurfaceAttribExternalBufferDescriptor,
184			flags: bindings::VA_SURFACE_ATTRIB_SETTABLE,
185			value: bindings::VAGenericValue::from(desc as *mut _ as *mut c_void),
186		}
187	}
188}
189
190impl<D: SurfaceMemoryDescriptor> Surface<D> {
191	/// Create `Surfaces` by wrapping around a `vaCreateSurfaces` call. This is just a helper for
192	/// [`Display::create_surfaces`].
193	pub(crate) fn new(
194		display: Arc<Display>,
195		rt_format: u32,
196		va_fourcc: Option<u32>,
197		width: u32,
198		height: u32,
199		usage_hint: Option<UsageHint>,
200		descriptors: Vec<D>,
201	) -> Result<Vec<Self>, VaError> {
202		let mut surfaces = vec![];
203
204		for mut descriptor in descriptors {
205			let mut attrs = vec![];
206
207			if let Some(usage_hint) = usage_hint {
208				attrs.push(bindings::VASurfaceAttrib::new_usage_hint(usage_hint));
209			}
210
211			if let Some(fourcc) = va_fourcc {
212				attrs.push(bindings::VASurfaceAttrib::new_pixel_format(fourcc));
213			}
214
215			// Just to be kept alive until we call `vaCreateSurfaces`...
216			let mut _va_desc = descriptor.add_attrs(&mut attrs);
217			let mut surface_id: VASurfaceID = 0;
218
219			// Safe because `self` represents a valid VADisplay. The `surface` and `attrs` vectors are
220			// properly initialized and valid sizes are passed to the C function, so it is impossible to
221			// write past the end of their storage by mistake.
222			//
223			// Also all the pointers in `attrs` are pointing to valid objects that haven't been
224			// moved or destroyed.
225			match va_check(unsafe {
226				bindings::vaCreateSurfaces(
227					display.handle(),
228					rt_format,
229					width,
230					height,
231					&mut surface_id,
232					1,
233					attrs.as_mut_ptr(),
234					attrs.len() as u32,
235				)
236			}) {
237				Ok(()) => surfaces.push(Self {
238					display: Arc::clone(&display),
239					id: surface_id,
240					descriptor,
241					width,
242					height,
243				}),
244				Err(e) => return Err(e),
245			}
246		}
247
248		Ok(surfaces)
249	}
250
251	pub fn display(&self) -> &Arc<Display> {
252		&self.display
253	}
254
255	/// Wrapper around `vaSyncSurface` that blocks until all pending operations on the render
256	/// target have been completed.
257	///
258	/// Upon return it
259	/// is safe to use the render target for a different picture.
260	pub fn sync(&self) -> Result<(), VaError> {
261		// Safe because `self` represents a valid VASurface.
262		va_check(unsafe { bindings::vaSyncSurface(self.display.handle(), self.id) })
263	}
264
265	/// Convenience function to return a VASurfaceID vector. Useful to interface with the C API
266	/// where a surface array might be needed.
267	pub fn as_id_vec(surfaces: &[Self]) -> Vec<bindings::VASurfaceID> {
268		surfaces.iter().map(|surface| surface.id).collect()
269	}
270
271	/// Wrapper over `vaQuerySurfaceStatus` to find out any pending ops on the render target.
272	pub fn query_status(&self) -> Result<bindings::VASurfaceStatus::Type, VaError> {
273		let mut status: bindings::VASurfaceStatus::Type = 0;
274		// Safe because `self` represents a valid VASurface.
275		va_check(unsafe { bindings::vaQuerySurfaceStatus(self.display.handle(), self.id, &mut status) })?;
276
277		Ok(status)
278	}
279
280	pub fn query_error(&self) -> Result<Vec<SurfaceDecodeMBError>, VaError> {
281		let mut raw: *const bindings::VASurfaceDecodeMBErrors = std::ptr::null();
282
283		// Safe because `self` represents a valid VASurface.
284		va_check(unsafe {
285			bindings::vaQuerySurfaceError(
286				self.display.handle(),
287				self.id,
288				bindings::VA_STATUS_ERROR_DECODING_ERROR as i32,
289				(&mut raw) as *mut _ as *mut _,
290			)
291		})?;
292
293		let mut errors = vec![];
294
295		while !raw.is_null() {
296			// Safe because raw is a valid pointer
297			let error = unsafe { *raw };
298			if error.status == -1 {
299				break;
300			}
301
302			let type_ = match error.decode_error_type {
303				bindings::VADecodeErrorType::VADecodeSliceMissing => DecodeErrorType::SliceMissing,
304				bindings::VADecodeErrorType::VADecodeMBError => DecodeErrorType::MBError,
305				#[cfg(libva_1_20_or_higher)]
306				bindings::VADecodeErrorType::VADecodeReset => DecodeErrorType::Reset,
307				_ => {
308					log::warn!("Unrecognized `decode_error_type` value ({})", error.decode_error_type);
309
310					// Safe because status != -1
311					raw = unsafe { raw.offset(1) };
312					continue;
313				}
314			};
315
316			errors.push(SurfaceDecodeMBError {
317				start_mb: error.start_mb,
318				end_mb: error.end_mb,
319				decode_error_type: type_,
320				num_mb: error.num_mb,
321			});
322
323			// Safe because status != -1
324			raw = unsafe { raw.offset(1) };
325		}
326
327		Ok(errors)
328	}
329
330	/// Returns the ID of this surface.
331	pub fn id(&self) -> bindings::VASurfaceID {
332		self.id
333	}
334
335	/// Returns the dimensions of this surface.
336	pub fn size(&self) -> (u32, u32) {
337		(self.width, self.height)
338	}
339
340	/// Returns a PRIME descriptor for this surface.
341	pub fn export_prime(&self) -> Result<DrmPrimeSurfaceDescriptor, VaError> {
342		let mut desc: bindings::VADRMPRIMESurfaceDescriptor = Default::default();
343
344		va_check(unsafe {
345			bindings::vaExportSurfaceHandle(
346				self.display.handle(),
347				self.id(),
348				bindings::VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2,
349				bindings::VA_EXPORT_SURFACE_READ_ONLY | bindings::VA_EXPORT_SURFACE_COMPOSED_LAYERS,
350				&mut desc as *mut _ as *mut c_void,
351			)
352		})?;
353
354		// We do not use a `From<VADRMPRIMESurfaceDescriptor>` implementation as this would allow
355		// to create "safe" descriptors outside of this method and thus from made up values,
356		// violating the safety guarantee that our FDs are legit.
357
358		let objects = (0..desc.num_objects as usize)
359			// Make sure we don't go out of bounds.
360			.take(4)
361			.map(|i| desc.objects[i])
362			.map(|o| {
363				DrmPrimeSurfaceDescriptorObject {
364					// Safe because `o.fd` is a valid file descriptor returned by
365					// `vaExportSurfaceHandle`.
366					fd: unsafe { OwnedFd::from_raw_fd(o.fd) },
367					size: o.size,
368					drm_format_modifier: o.drm_format_modifier,
369				}
370			})
371			.collect();
372
373		let layers = (0..desc.num_layers as usize)
374			// Make sure we don't go out of bounds.
375			.take(4)
376			.map(|i| desc.layers[i])
377			.map(|l| DrmPrimeSurfaceDescriptorLayer {
378				drm_format: l.drm_format,
379				num_planes: l.num_planes,
380				object_index: [
381					l.object_index[0] as u8,
382					l.object_index[1] as u8,
383					l.object_index[2] as u8,
384					l.object_index[3] as u8,
385				],
386				offset: l.offset,
387				pitch: l.pitch,
388			})
389			.collect();
390
391		Ok(DrmPrimeSurfaceDescriptor {
392			fourcc: desc.fourcc,
393			width: desc.width,
394			height: desc.height,
395			objects,
396			layers,
397		})
398	}
399}
400
401impl<D: SurfaceMemoryDescriptor> AsRef<D> for Surface<D> {
402	fn as_ref(&self) -> &D {
403		&self.descriptor
404	}
405}
406
407impl<D: SurfaceMemoryDescriptor> AsMut<D> for Surface<D> {
408	fn as_mut(&mut self) -> &mut D {
409		&mut self.descriptor
410	}
411}
412
413impl<D: SurfaceMemoryDescriptor> Drop for Surface<D> {
414	fn drop(&mut self) {
415		// Safe because `self` represents a valid VASurface.
416		unsafe { bindings::vaDestroySurfaces(self.display.handle(), &mut self.id, 1) };
417	}
418}
419
420/// Safe wrapper for the `object` member of `VADRMPRIMESurfaceDescriptor`.
421pub struct DrmPrimeSurfaceDescriptorObject {
422	pub fd: OwnedFd,
423	pub size: u32,
424	pub drm_format_modifier: u64,
425}
426
427/// Safe wrapper for the `layers` member of `VADRMPRIMESurfaceDescriptor`.
428pub struct DrmPrimeSurfaceDescriptorLayer {
429	pub drm_format: u32,
430	pub num_planes: u32,
431	pub object_index: [u8; 4],
432	pub offset: [u32; 4],
433	pub pitch: [u32; 4],
434}
435
436/// Safe wrapper around `VADRMPRIMESurfaceDescriptor`.
437pub struct DrmPrimeSurfaceDescriptor {
438	pub fourcc: u32,
439	pub width: u32,
440	pub height: u32,
441	pub objects: Vec<DrmPrimeSurfaceDescriptorObject>,
442	pub layers: Vec<DrmPrimeSurfaceDescriptorLayer>,
443}