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