]>
Commit | Line | Data |
---|---|---|
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 | ||
5 | use std::ffi::CStr; | |
6 | use std::fs::File; | |
7 | use std::os::unix::io::AsRawFd; | |
8 | use std::path::Path; | |
9 | use std::path::PathBuf; | |
10 | use std::rc::Rc; | |
11 | ||
68362724 KS |
12 | use crate::bindings; |
13 | use crate::config::Config; | |
14 | use crate::context::Context; | |
0f2fb233 | 15 | use crate::formats::{RTFormat, VAFourcc}; |
69e6ce02 | 16 | use crate::status::*; |
68362724 | 17 | use crate::surface::Surface; |
379fea7e | 18 | use 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. | |
24 | pub struct DrmDeviceIterator { | |
25 | cur_idx: usize, | |
26 | } | |
27 | ||
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; | |
31 | ||
32 | impl Default for DrmDeviceIterator { | |
33 | fn default() -> Self { | |
34 | Self { | |
35 | cur_idx: DRM_RENDER_NODE_START, | |
36 | } | |
37 | } | |
38 | } | |
39 | ||
40 | impl 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 |
59 | unsafe 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. | |
72 | pub 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 | ||
80 | impl 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 | ||
371 | impl 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 | } |