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