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