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.
7 use std::os::unix::io::AsRawFd;
9 use std::path::PathBuf;
13 use crate::config::Config;
14 use crate::context::Context;
15 use crate::formats::{RTFormat, VAFourcc};
17 use crate::surface::Surface;
18 use crate::UsageHints;
20 /// Iterates over existing DRM devices.
22 /// DRM devices can be passed to [`Display::open_drm_display`] in order to create a `Display` on
24 pub struct DrmDeviceIterator {
28 const DRM_NODE_DEFAULT_PREFIX: &str = "/dev/dri/renderD";
29 const DRM_NUM_NODES: usize = 64;
30 const DRM_RENDER_NODE_START: usize = 128;
32 impl Default for DrmDeviceIterator {
33 fn default() -> Self {
35 cur_idx: DRM_RENDER_NODE_START,
40 impl Iterator for DrmDeviceIterator {
43 fn next(&mut self) -> Option<Self::Item> {
45 idx if idx >= DRM_RENDER_NODE_START + DRM_NUM_NODES => None,
47 let path = PathBuf::from(format!("{}{}", DRM_NODE_DEFAULT_PREFIX, idx));
59 /// A VADisplay opened over DRM.
61 /// A Display is the starting point to using libva. This struct is essentially a safe wrapper over
62 /// `VADisplay`, from which [`Surface`]s and [`Context`]s can be allocated in order to perform
63 /// actual work using [`Display::create_surfaces`] and [`Display::create_context`], respectively.
65 /// Although libva offers several ways to create a display, this struct currently only supports
66 /// opening through DRM. It may be extended to support other display types (X11, Wayland) in the
69 /// Handle to interact with the underlying `VADisplay`.
70 handle: bindings::VADisplay,
71 /// DRM file that must be kept open while the display is in use.
77 /// Opens and initializes a specific DRM `Display`.
79 /// `path` is the path to a DRM device that supports VAAPI, e.g. `/dev/dri/renderD128`.
80 pub fn open_drm_display<P: AsRef<Path>>(path: P) -> VAResult<Rc<Self>> {
81 let file = std::fs::File::options()
85 .map_err(|_| VAError::InvalidValue)?;
87 // Safe because fd represents a valid file descriptor and the pointer is checked for
89 let display = unsafe { bindings::vaGetDisplayDRM(file.as_raw_fd()) };
90 if display.is_null() {
91 // The File will close the DRM fd on drop.
92 return Err(VAError::InvalidDisplay);
97 // Safe because we ensure that the display is valid (i.e not NULL) before calling
98 // vaInitialize. The File will close the DRM fd on drop.
99 (unsafe { bindings::vaInitialize(display, &mut major, &mut minor) }).check()?;
107 /// Opens the first device that succeeds and returns its `Display`.
109 /// If an error occurs on a given device, it is ignored and the next one is tried until one
110 /// succeeds or we reach the end of the iterator.
111 pub fn open() -> Option<Rc<Self>> {
112 let devices = DrmDeviceIterator::default();
114 // Try all the DRM devices until one succeeds.
115 for device in devices {
116 if let Ok(display) = Self::open_drm_display(device) {
117 return Some(display);
124 /// Returns the handle of this display.
125 pub(crate) fn handle(&self) -> bindings::VADisplay {
129 /// Queries supported profiles by this display.
130 pub fn query_config_profiles(&self) -> VAResult<Vec<bindings::VAProfile::Type>> {
131 // Safe because `self` represents a valid VADisplay.
132 let mut max_num_profiles = unsafe { bindings::vaMaxNumProfiles(self.handle) };
133 let mut profiles = Vec::with_capacity(max_num_profiles as usize);
135 // Safe because `self` represents a valid `VADisplay` and the vector has `max_num_profiles`
138 bindings::vaQueryConfigProfiles(
140 profiles.as_mut_ptr(),
141 &mut max_num_profiles,
146 // Safe because `profiles` is allocated with a `max_num_profiles` capacity and
147 // `vaQueryConfigProfiles` wrote the actual number of profiles to `max_num_entrypoints`.
149 profiles.set_len(max_num_profiles as usize);
155 /// Returns a string describing some aspects of the VA implemenation on the specific hardware
156 /// accelerator used by this display.
158 /// The format of the returned string is vendor specific and at the discretion of the
159 /// implementer. e.g. for the Intel GMA500 implementation, an example would be: `Intel GMA500 -
161 pub fn query_vendor_string(&self) -> std::result::Result<String, &'static str> {
162 // Safe because `self` represents a valid VADisplay.
163 let vendor_string = unsafe { bindings::vaQueryVendorString(self.handle) };
165 if vendor_string.is_null() {
166 return Err("vaQueryVendorString() returned NULL");
169 // Safe because we check the whether the vendor_String pointer is NULL
170 Ok(unsafe { CStr::from_ptr(vendor_string) }
175 /// Query supported entrypoints for a given profile.
176 pub fn query_config_entrypoints(
178 profile: bindings::VAProfile::Type,
179 ) -> VAResult<Vec<bindings::VAEntrypoint::Type>> {
180 // Safe because `self` represents a valid VADisplay.
181 let mut max_num_entrypoints = unsafe { bindings::vaMaxNumEntrypoints(self.handle) };
182 let mut entrypoints = Vec::with_capacity(max_num_entrypoints as usize);
184 // Safe because `self` represents a valid VADisplay and the vector has `max_num_entrypoints`
187 bindings::vaQueryConfigEntrypoints(
190 entrypoints.as_mut_ptr(),
191 &mut max_num_entrypoints,
196 // Safe because `entrypoints` is allocated with a `max_num_entrypoints` capacity, and
197 // `vaQueryConfigEntrypoints` wrote the actual number of entrypoints to
198 // `max_num_entrypoints`
200 entrypoints.set_len(max_num_entrypoints as usize);
206 /// Writes attributes for a given `profile`/`entrypoint` pair into `attributes`.
208 /// Entries of `attributes` must have their `type_` member initialized to the desired attribute
210 pub fn get_config_attributes(
212 profile: bindings::VAProfile::Type,
213 entrypoint: bindings::VAEntrypoint::Type,
214 attributes: &mut [bindings::VAConfigAttrib],
216 // Safe because `self` represents a valid VADisplay. The slice length is passed to the C
217 // function, so it is impossible to write past the end of the slice's storage by mistake.
219 bindings::vaGetConfigAttributes(
223 attributes.as_mut_ptr(),
224 attributes.len() as i32,
230 /// Creates `Surface`s by wrapping around a `vaCreateSurfaces` call.
234 /// * `rt_format` - The desired surface format.
235 /// * `va_fourcc` - The desired pixel format (optional). See `VA_FOURCC_*`
236 /// * `width` - Width for the create surfaces
237 /// * `height` - Height for the created surfaces
238 /// * `usage_hint` - Optional hint of intended usage to optimize allocation (e.g. tiling)
239 /// * `num_surfaces` - Number of surfaces to create
240 pub fn create_surfaces(
243 va_fourcc: Option<VAFourcc>,
246 usage_hints: Option<UsageHints>,
248 ) -> VAResult<Vec<Surface>> {
260 /// Creates a `Context` by wrapping around a `vaCreateContext` call.
264 /// * `config` - The configuration for the context
265 /// * `coded_width` - The coded picture width
266 /// * `coded_height` - The coded picture height
267 /// * `surfaces` - Optional hint for the amount of surfaces tied to the context
268 /// * `progressive` - Whether only progressive frame pictures are present in the sequence
269 pub fn create_context(
274 surfaces: Option<&Vec<Surface>>,
276 ) -> VAResult<Rc<Context>> {
287 /// Creates a `Config` by wrapping around the `vaCreateConfig` call.
289 /// `attrs` describe the attributes to set for this config. A list of the supported attributes
290 /// for a given profile/entrypoint pair can be retrieved using
291 /// [`Display::get_config_attributes`]. Other attributes will take their default values, and
292 /// `attrs` can be empty in order to obtain a default configuration.
293 pub fn create_config(
295 attrs: Vec<bindings::VAConfigAttrib>,
296 profile: bindings::VAProfile::Type,
297 entrypoint: bindings::VAEntrypoint::Type,
298 ) -> VAResult<Config> {
299 Config::new(Rc::clone(self), attrs, profile, entrypoint)
302 /// Returns available image formats for this display by wrapping around `vaQueryImageFormats`.
303 pub fn query_image_formats(&self) -> VAResult<Vec<bindings::VAImageFormat>> {
304 // Safe because `self` represents a valid VADisplay.
305 let mut num_image_formats = unsafe { bindings::vaMaxNumImageFormats(self.handle) };
306 let mut image_formats = Vec::with_capacity(num_image_formats as usize);
308 // Safe because `self` represents a valid VADisplay. The `image_formats` vector is properly
309 // initialized and a valid size is passed to the C function, so it is impossible to write
310 // past the end of their storage by mistake.
312 bindings::vaQueryImageFormats(
314 image_formats.as_mut_ptr(),
315 &mut num_image_formats,
320 // Safe because the C function will have written exactly `num_image_format` entries, which
321 // is known to be within the vector's capacity.
323 image_formats.set_len(num_image_formats as usize);
330 impl Drop for Display {
332 // Safe because `self` represents a valid VADisplay.
334 bindings::vaTerminate(self.handle);
335 // The File will close the DRM fd on drop.