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 | ||
59 | /// A VADisplay opened over DRM. | |
60 | /// | |
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. | |
64 | /// | |
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 | |
67 | /// future. | |
68 | pub struct Display { | |
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. | |
72 | #[allow(dead_code)] | |
73 | drm_file: File, | |
74 | } | |
75 | ||
76 | impl Display { | |
77 | /// Opens and initializes a specific DRM `Display`. | |
78 | /// | |
79 | /// `path` is the path to a DRM device that supports VAAPI, e.g. `/dev/dri/renderD128`. | |
69e6ce02 | 80 | pub fn open_drm_display<P: AsRef<Path>>(path: P) -> VAResult<Rc<Self>> { |
68362724 KS |
81 | let file = std::fs::File::options() |
82 | .read(true) | |
83 | .write(true) | |
84 | .open(path.as_ref()) | |
69e6ce02 | 85 | .map_err(|_| VAError::InvalidValue)?; |
68362724 KS |
86 | |
87 | // Safe because fd represents a valid file descriptor and the pointer is checked for | |
88 | // NULL afterwards. | |
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. | |
69e6ce02 | 92 | return Err(VAError::InvalidDisplay); |
68362724 KS |
93 | } |
94 | ||
95 | let mut major = 0i32; | |
96 | let mut minor = 0i32; | |
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. | |
69e6ce02 | 99 | (unsafe { bindings::vaInitialize(display, &mut major, &mut minor) }).check()?; |
68362724 KS |
100 | |
101 | Ok(Rc::new(Self { | |
102 | handle: display, | |
103 | drm_file: file, | |
104 | })) | |
105 | } | |
106 | ||
107 | /// Opens the first device that succeeds and returns its `Display`. | |
108 | /// | |
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(); | |
113 | ||
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); | |
118 | } | |
119 | } | |
120 | ||
121 | None | |
122 | } | |
123 | ||
124 | /// Returns the handle of this display. | |
125 | pub(crate) fn handle(&self) -> bindings::VADisplay { | |
126 | self.handle | |
127 | } | |
128 | ||
129 | /// Queries supported profiles by this display. | |
69e6ce02 | 130 | pub fn query_config_profiles(&self) -> VAResult<Vec<bindings::VAProfile::Type>> { |
68362724 KS |
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); | |
134 | ||
135 | // Safe because `self` represents a valid `VADisplay` and the vector has `max_num_profiles` | |
136 | // as capacity. | |
69e6ce02 | 137 | (unsafe { |
68362724 KS |
138 | bindings::vaQueryConfigProfiles( |
139 | self.handle, | |
140 | profiles.as_mut_ptr(), | |
141 | &mut max_num_profiles, | |
142 | ) | |
143 | }) | |
144 | .check()?; | |
145 | ||
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`. | |
148 | unsafe { | |
149 | profiles.set_len(max_num_profiles as usize); | |
150 | }; | |
151 | ||
152 | Ok(profiles) | |
153 | } | |
154 | ||
155 | /// Returns a string describing some aspects of the VA implemenation on the specific hardware | |
156 | /// accelerator used by this display. | |
157 | /// | |
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 - | |
160 | /// 2.0.0.32L.0005`. | |
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) }; | |
164 | ||
165 | if vendor_string.is_null() { | |
166 | return Err("vaQueryVendorString() returned NULL"); | |
167 | } | |
168 | ||
169 | // Safe because we check the whether the vendor_String pointer is NULL | |
170 | Ok(unsafe { CStr::from_ptr(vendor_string) } | |
171 | .to_string_lossy() | |
172 | .to_string()) | |
173 | } | |
174 | ||
175 | /// Query supported entrypoints for a given profile. | |
176 | pub fn query_config_entrypoints( | |
177 | &self, | |
178 | profile: bindings::VAProfile::Type, | |
69e6ce02 | 179 | ) -> VAResult<Vec<bindings::VAEntrypoint::Type>> { |
68362724 KS |
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); | |
183 | ||
184 | // Safe because `self` represents a valid VADisplay and the vector has `max_num_entrypoints` | |
185 | // as capacity. | |
69e6ce02 | 186 | (unsafe { |
68362724 KS |
187 | bindings::vaQueryConfigEntrypoints( |
188 | self.handle, | |
189 | profile, | |
190 | entrypoints.as_mut_ptr(), | |
191 | &mut max_num_entrypoints, | |
192 | ) | |
193 | }) | |
194 | .check()?; | |
195 | ||
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` | |
199 | unsafe { | |
200 | entrypoints.set_len(max_num_entrypoints as usize); | |
201 | } | |
202 | ||
203 | Ok(entrypoints) | |
204 | } | |
205 | ||
206 | /// Writes attributes for a given `profile`/`entrypoint` pair into `attributes`. | |
207 | /// | |
208 | /// Entries of `attributes` must have their `type_` member initialized to the desired attribute | |
209 | /// to retrieve. | |
210 | pub fn get_config_attributes( | |
211 | &self, | |
212 | profile: bindings::VAProfile::Type, | |
213 | entrypoint: bindings::VAEntrypoint::Type, | |
214 | attributes: &mut [bindings::VAConfigAttrib], | |
69e6ce02 | 215 | ) -> VAResult<()> { |
68362724 KS |
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. | |
69e6ce02 | 218 | (unsafe { |
68362724 KS |
219 | bindings::vaGetConfigAttributes( |
220 | self.handle, | |
221 | profile, | |
222 | entrypoint, | |
223 | attributes.as_mut_ptr(), | |
224 | attributes.len() as i32, | |
225 | ) | |
226 | }) | |
227 | .check() | |
228 | } | |
229 | ||
230 | /// Creates `Surface`s by wrapping around a `vaCreateSurfaces` call. | |
231 | /// | |
232 | /// # Arguments | |
233 | /// | |
0f2fb233 | 234 | /// * `rt_format` - The desired surface format. |
68362724 KS |
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( | |
241 | self: &Rc<Self>, | |
0f2fb233 KS |
242 | rt_format: RTFormat, |
243 | va_fourcc: Option<VAFourcc>, | |
68362724 KS |
244 | width: u32, |
245 | height: u32, | |
379fea7e | 246 | usage_hints: Option<UsageHints>, |
68362724 | 247 | num_surfaces: u32, |
69e6ce02 | 248 | ) -> VAResult<Vec<Surface>> { |
68362724 KS |
249 | Surface::new( |
250 | Rc::clone(self), | |
251 | rt_format, | |
252 | va_fourcc, | |
253 | width, | |
254 | height, | |
379fea7e | 255 | usage_hints, |
68362724 KS |
256 | num_surfaces, |
257 | ) | |
258 | } | |
259 | ||
260 | /// Creates a `Context` by wrapping around a `vaCreateContext` call. | |
261 | /// | |
262 | /// # Arguments | |
263 | /// | |
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( | |
270 | self: &Rc<Self>, | |
271 | config: &Config, | |
272 | coded_width: i32, | |
273 | coded_height: i32, | |
274 | surfaces: Option<&Vec<Surface>>, | |
275 | progressive: bool, | |
69e6ce02 | 276 | ) -> VAResult<Rc<Context>> { |
68362724 KS |
277 | Context::new( |
278 | Rc::clone(self), | |
279 | config, | |
280 | coded_width, | |
281 | coded_height, | |
282 | surfaces, | |
283 | progressive, | |
284 | ) | |
285 | } | |
286 | ||
287 | /// Creates a `Config` by wrapping around the `vaCreateConfig` call. | |
288 | /// | |
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( | |
294 | self: &Rc<Self>, | |
295 | attrs: Vec<bindings::VAConfigAttrib>, | |
296 | profile: bindings::VAProfile::Type, | |
297 | entrypoint: bindings::VAEntrypoint::Type, | |
69e6ce02 | 298 | ) -> VAResult<Config> { |
68362724 KS |
299 | Config::new(Rc::clone(self), attrs, profile, entrypoint) |
300 | } | |
301 | ||
302 | /// Returns available image formats for this display by wrapping around `vaQueryImageFormats`. | |
69e6ce02 | 303 | pub fn query_image_formats(&self) -> VAResult<Vec<bindings::VAImageFormat>> { |
68362724 KS |
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); | |
307 | ||
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. | |
69e6ce02 | 311 | (unsafe { |
68362724 KS |
312 | bindings::vaQueryImageFormats( |
313 | self.handle, | |
314 | image_formats.as_mut_ptr(), | |
315 | &mut num_image_formats, | |
316 | ) | |
317 | }) | |
318 | .check()?; | |
319 | ||
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. | |
322 | unsafe { | |
323 | image_formats.set_len(num_image_formats as usize); | |
324 | } | |
325 | ||
326 | Ok(image_formats) | |
327 | } | |
328 | } | |
329 | ||
330 | impl Drop for Display { | |
331 | fn drop(&mut self) { | |
332 | // Safe because `self` represents a valid VADisplay. | |
333 | unsafe { | |
334 | bindings::vaTerminate(self.handle); | |
335 | // The File will close the DRM fd on drop. | |
336 | } | |
337 | } | |
338 | } |