Skip to main content

moq_vaapi/
display.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::ffi::CStr;
6use std::fs::File;
7use std::io;
8use std::os::unix::io::AsRawFd;
9use std::path::Path;
10use std::path::PathBuf;
11use std::rc::Rc;
12use std::sync::Arc;
13
14use thiserror::Error;
15
16use crate::bindings;
17use crate::config::Config;
18use crate::context::Context;
19use crate::surface::Surface;
20use crate::va_check;
21use crate::SurfaceMemoryDescriptor;
22use crate::UsageHint;
23use crate::VaError;
24
25/// Iterates over existing DRM devices.
26///
27/// DRM devices can be passed to [`Display::open_drm_display`] in order to create a `Display` on
28/// that device.
29pub struct DrmDeviceIterator {
30	cur_idx: usize,
31}
32
33const DRM_NODE_DEFAULT_PREFIX: &str = "/dev/dri/renderD";
34const DRM_NUM_NODES: usize = 64;
35const DRM_RENDER_NODE_START: usize = 128;
36
37impl Default for DrmDeviceIterator {
38	fn default() -> Self {
39		Self {
40			cur_idx: DRM_RENDER_NODE_START,
41		}
42	}
43}
44
45impl Iterator for DrmDeviceIterator {
46	type Item = PathBuf;
47
48	fn next(&mut self) -> Option<Self::Item> {
49		match self.cur_idx {
50			idx if idx >= DRM_RENDER_NODE_START + DRM_NUM_NODES => None,
51			idx => {
52				let path = PathBuf::from(format!("{}{}", DRM_NODE_DEFAULT_PREFIX, idx));
53				if !path.exists() {
54					None
55				} else {
56					self.cur_idx += 1;
57					Some(path)
58				}
59			}
60		}
61	}
62}
63
64/// A VADisplay opened over DRM.
65///
66/// A Display is the starting point to using libva. This struct is essentially a safe wrapper over
67/// `VADisplay`, from which [`Surface`]s and [`Context`]s can be allocated in order to perform
68/// actual work using [`Display::create_surfaces`] and [`Display::create_context`], respectively.
69///
70/// Although libva offers several ways to create a display, this struct currently only supports
71/// opening through DRM. It may be extended to support other display types (X11, Wayland) in the
72/// future.
73pub struct Display {
74	/// Handle to interact with the underlying `VADisplay`.
75	handle: bindings::VADisplay,
76	/// DRM file that must be kept open while the display is in use.
77	#[allow(dead_code)]
78	drm_file: File,
79}
80
81/// Error type for `Display::open_drm_display`.
82#[derive(Debug, Error)]
83pub enum OpenDrmDisplayError {
84	#[error("cannot open DRM device: {0}")]
85	DeviceOpen(io::Error),
86	#[error("vaGetDisplayDRM returned NULL")]
87	VaGetDisplayDrm,
88	#[error("call to vaInitialize failed: {0}")]
89	VaInitialize(VaError),
90}
91
92impl Display {
93	/// Opens and initializes a specific DRM `Display`.
94	///
95	/// `path` is the path to a DRM device that supports VAAPI, e.g. `/dev/dri/renderD128`.
96	pub fn open_drm_display<P: AsRef<Path>>(path: P) -> Result<Arc<Self>, OpenDrmDisplayError> {
97		let file = std::fs::File::options()
98			.read(true)
99			.write(true)
100			.open(path.as_ref())
101			.map_err(OpenDrmDisplayError::DeviceOpen)?;
102
103		// Safe because fd represents a valid file descriptor and the pointer is checked for
104		// NULL afterwards.
105		let display = unsafe { bindings::vaGetDisplayDRM(file.as_raw_fd()) };
106		if display.is_null() {
107			return Err(OpenDrmDisplayError::VaGetDisplayDrm);
108		}
109
110		let mut major = 0i32;
111		let mut minor = 0i32;
112		// Safe because we ensure that the display is valid (i.e not NULL) before calling
113		// vaInitialize. The File will close the DRM fd on drop.
114		va_check(unsafe { bindings::vaInitialize(display, &mut major, &mut minor) })
115			.map(|()| {
116				Arc::new(Self {
117					handle: display,
118					drm_file: file,
119				})
120			})
121			.map_err(OpenDrmDisplayError::VaInitialize)
122	}
123
124	/// Opens the first device that succeeds and returns its `Display`.
125	///
126	/// If an error occurs on a given device, it is ignored and the next one is tried until one
127	/// succeeds or we reach the end of the iterator.
128	pub fn open() -> Option<Arc<Self>> {
129		let devices = DrmDeviceIterator::default();
130
131		// Try all the DRM devices until one succeeds.
132		for device in devices {
133			if let Ok(display) = Self::open_drm_display(device) {
134				return Some(display);
135			}
136		}
137
138		None
139	}
140
141	/// Returns the handle of this display.
142	pub fn handle(&self) -> bindings::VADisplay {
143		self.handle
144	}
145
146	/// Queries supported profiles by this display by wrapping `vaQueryConfigProfiles`.
147	pub fn query_config_profiles(&self) -> Result<Vec<bindings::VAProfile::Type>, VaError> {
148		// Safe because `self` represents a valid VADisplay.
149		let mut max_num_profiles = unsafe { bindings::vaMaxNumProfiles(self.handle) };
150		let mut profiles = Vec::with_capacity(max_num_profiles as usize);
151
152		// Safe because `self` represents a valid `VADisplay` and the vector has `max_num_profiles`
153		// as capacity.
154		va_check(unsafe {
155			bindings::vaQueryConfigProfiles(self.handle, profiles.as_mut_ptr(), &mut max_num_profiles)
156		})?;
157
158		// Safe because `profiles` is allocated with a `max_num_profiles` capacity and
159		// `vaQueryConfigProfiles` wrote the actual number of profiles to `max_num_entrypoints`.
160		unsafe {
161			profiles.set_len(max_num_profiles as usize);
162		};
163
164		Ok(profiles)
165	}
166
167	/// Returns a string describing some aspects of the VA implemenation on the specific hardware
168	/// accelerator used by this display. Wrapper over `vaQueryVendorString`.
169	///
170	/// The format of the returned string is vendor specific and at the discretion of the
171	/// implementer. e.g. for the Intel GMA500 implementation, an example would be: `Intel GMA500 -
172	/// 2.0.0.32L.0005`.
173	pub fn query_vendor_string(&self) -> std::result::Result<String, &'static str> {
174		// Safe because `self` represents a valid VADisplay.
175		let vendor_string = unsafe { bindings::vaQueryVendorString(self.handle) };
176
177		if vendor_string.is_null() {
178			return Err("vaQueryVendorString() returned NULL");
179		}
180
181		// Safe because we check the whether the vendor_String pointer is NULL
182		Ok(unsafe { CStr::from_ptr(vendor_string) }.to_string_lossy().to_string())
183	}
184
185	/// Query supported entrypoints for a given profile by wrapping `vaQueryConfigEntrypoints`.
186	pub fn query_config_entrypoints(
187		&self,
188		profile: bindings::VAProfile::Type,
189	) -> Result<Vec<bindings::VAEntrypoint::Type>, VaError> {
190		// Safe because `self` represents a valid VADisplay.
191		let mut max_num_entrypoints = unsafe { bindings::vaMaxNumEntrypoints(self.handle) };
192		let mut entrypoints = Vec::with_capacity(max_num_entrypoints as usize);
193
194		// Safe because `self` represents a valid VADisplay and the vector has `max_num_entrypoints`
195		// as capacity.
196		va_check(unsafe {
197			bindings::vaQueryConfigEntrypoints(self.handle, profile, entrypoints.as_mut_ptr(), &mut max_num_entrypoints)
198		})?;
199
200		// Safe because `entrypoints` is allocated with a `max_num_entrypoints` capacity, and
201		// `vaQueryConfigEntrypoints` wrote the actual number of entrypoints to
202		// `max_num_entrypoints`
203		unsafe {
204			entrypoints.set_len(max_num_entrypoints as usize);
205		}
206
207		Ok(entrypoints)
208	}
209
210	/// Writes attributes for a given `profile`/`entrypoint` pair into `attributes`. Wrapper over
211	/// `vaGetConfigAttributes`.
212	///
213	/// Entries of `attributes` must have their `type_` member initialized to the desired attribute
214	/// to retrieve.
215	pub fn get_config_attributes(
216		&self,
217		profile: bindings::VAProfile::Type,
218		entrypoint: bindings::VAEntrypoint::Type,
219		attributes: &mut [bindings::VAConfigAttrib],
220	) -> Result<(), VaError> {
221		// Safe because `self` represents a valid VADisplay. The slice length is passed to the C
222		// function, so it is impossible to write past the end of the slice's storage by mistake.
223		va_check(unsafe {
224			bindings::vaGetConfigAttributes(
225				self.handle,
226				profile,
227				entrypoint,
228				attributes.as_mut_ptr(),
229				attributes.len() as i32,
230			)
231		})
232	}
233
234	/// Creates `Surface`s by wrapping around a `vaCreateSurfaces` call.
235	///
236	/// The number of surfaces created will be equal to the length of `descriptors`.
237	///
238	/// # Arguments
239	///
240	/// * `rt_format` - The desired surface format. See `VA_RT_FORMAT_*`
241	/// * `va_fourcc` - The desired pixel format (optional). See `VA_FOURCC_*`
242	/// * `width` - Width for the create surfaces
243	/// * `height` - Height for the created surfaces
244	/// * `usage_hint` - Optional hint of intended usage to optimize allocation (e.g. tiling)
245	/// * `num_surfaces` - Number of surfaces to create
246	/// * `descriptors` - Memory descriptors used as surface memory backing.
247	///
248	/// # Return value
249	///
250	/// Returns as many surfaces as the length of `descriptors`.
251	///
252	/// Note that the `descriptors`'s ownership is irrevocably given to the surfaces, and that in
253	/// case of error the `descriptors` will be destroyed. Make sure to duplicate the descriptors
254	/// if you need something outside of libva to access them.
255	pub fn create_surfaces<D: SurfaceMemoryDescriptor>(
256		self: &Arc<Self>,
257		rt_format: u32,
258		va_fourcc: Option<u32>,
259		width: u32,
260		height: u32,
261		usage_hint: Option<UsageHint>,
262		descriptors: Vec<D>,
263	) -> Result<Vec<Surface<D>>, VaError> {
264		Surface::new(
265			Arc::clone(self),
266			rt_format,
267			va_fourcc,
268			width,
269			height,
270			usage_hint,
271			descriptors,
272		)
273	}
274
275	/// Creates a `Context` by wrapping around a `vaCreateContext` call.
276	///
277	/// # Arguments
278	///
279	/// * `config` - The configuration for the context
280	/// * `coded_width` - The coded picture width
281	/// * `coded_height` - The coded picture height
282	/// * `surfaces` - Optional hint for the amount of surfaces tied to the context
283	/// * `progressive` - Whether only progressive frame pictures are present in the sequence
284	pub fn create_context<D: SurfaceMemoryDescriptor>(
285		self: &Arc<Self>,
286		config: &Config,
287		coded_width: u32,
288		coded_height: u32,
289		surfaces: Option<&Vec<Surface<D>>>,
290		progressive: bool,
291	) -> Result<Rc<Context>, VaError> {
292		Context::new(
293			Arc::clone(self),
294			config,
295			coded_width,
296			coded_height,
297			surfaces,
298			progressive,
299		)
300	}
301
302	/// Creates a `Config` by wrapping around the `vaCreateConfig` call.
303	///
304	/// `attrs` describe the attributes to set for this config. A list of the supported attributes
305	/// for a given profile/entrypoint pair can be retrieved using
306	/// [`Display::get_config_attributes`]. Other attributes will take their default values, and
307	/// `attrs` can be empty in order to obtain a default configuration.
308	pub fn create_config(
309		self: &Arc<Self>,
310		attrs: Vec<bindings::VAConfigAttrib>,
311		profile: bindings::VAProfile::Type,
312		entrypoint: bindings::VAEntrypoint::Type,
313	) -> Result<Config, VaError> {
314		Config::new(Arc::clone(self), attrs, profile, entrypoint)
315	}
316
317	/// Returns available image formats for this display by wrapping around `vaQueryImageFormats`.
318	pub fn query_image_formats(&self) -> Result<Vec<bindings::VAImageFormat>, VaError> {
319		// Safe because `self` represents a valid VADisplay.
320		let mut num_image_formats = unsafe { bindings::vaMaxNumImageFormats(self.handle) };
321		let mut image_formats = Vec::with_capacity(num_image_formats as usize);
322
323		// Safe because `self` represents a valid VADisplay. The `image_formats` vector is properly
324		// initialized and a valid size is passed to the C function, so it is impossible to write
325		// past the end of their storage by mistake.
326		va_check(unsafe {
327			bindings::vaQueryImageFormats(self.handle, image_formats.as_mut_ptr(), &mut num_image_formats)
328		})?;
329
330		// Safe because the C function will have written exactly `num_image_format` entries, which
331		// is known to be within the vector's capacity.
332		unsafe {
333			image_formats.set_len(num_image_formats as usize);
334		}
335
336		Ok(image_formats)
337	}
338}
339
340impl Drop for Display {
341	fn drop(&mut self) {
342		// Safe because `self` represents a valid VADisplay.
343		unsafe {
344			bindings::vaTerminate(self.handle);
345			// The File will close the DRM fd on drop.
346		}
347	}
348}
349
350// Safe because because it only contains a `File` (which is Send+Sync), and a VADisplay handle
351// which is also thread-safe. The Drop handler can also be safely called from any thread.
352unsafe impl Send for Display {}
353unsafe impl Sync for Display {}