]>
Commit | Line | Data |
---|---|---|
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 crate::bindings; | |
6 | use crate::picture::Picture; | |
7 | use crate::picture::PictureSync; | |
8 | use crate::status::*; | |
9 | ||
10 | /// Wrapper around `VAImage` that is tied to the lifetime of a given `Picture`. | |
11 | /// | |
12 | /// An image is used to either get the surface data to client memory, or to copy image data in | |
13 | /// client memory to a surface. | |
14 | pub struct Image<'a> { | |
15 | /// The picture whose `Surface` we use as the source of pixel data. | |
16 | picture: &'a Picture<PictureSync>, | |
17 | /// The `VAImage` returned by libva. | |
18 | image: bindings::VAImage, | |
19 | /// The mapped surface data. | |
20 | data: &'a mut [u8], | |
21 | /// Whether the image was derived using the `vaDeriveImage` API or created using the | |
22 | /// `vaCreateImage` API. | |
23 | derived: bool, | |
24 | /// Tracks whether the underlying data has possibly been written to, i.e. an encoder will create | |
25 | /// an image and map its buffer in order to write to it, so we must writeback later. | |
26 | dirty: bool, | |
27 | } | |
28 | ||
29 | impl<'a> Image<'a> { | |
30 | /// Creates a new `Image` either by calling `vaCreateImage` or | |
31 | /// `vaDeriveImage`. Creating an Image depends on acquiring a ready Surface | |
32 | /// from an underlying Picture. Note that Image has a borrowed Picture, so | |
33 | /// it will be dropped before the underlying Surface is dropped, as mandated | |
34 | /// by VAAPI. | |
35 | /// | |
36 | /// # Arguments | |
37 | /// | |
38 | /// * `picture` - The [`Picture`] that owns the Surface this image will be created from. | |
39 | /// * `format` - A `VAImageFormat` returned by [`crate::Display::query_image_formats`]. | |
40 | /// * `width` - The image's desired width. | |
41 | /// * `height` - The image's desired height. | |
42 | /// * `derive` - Whether to try deriving the image from `picture`, which allows zero-copy access | |
43 | /// to the surface data. Deriving may fail, in which case vaCreateImage will be used instead, | |
44 | /// incurring an extra data copy. | |
45 | pub fn new( | |
46 | picture: &'a Picture<PictureSync>, | |
47 | mut format: bindings::VAImageFormat, | |
48 | width: u32, | |
49 | height: u32, | |
50 | derive: bool, | |
51 | ) -> VAResult<Self> { | |
52 | // An all-zero byte-pattern is a valid initial value for `VAImage`. | |
53 | let mut image: bindings::VAImage = Default::default(); | |
54 | let mut addr = std::ptr::null_mut(); | |
55 | let mut derived = false; | |
56 | ||
57 | if derive { | |
58 | derived = Image::derive_image(picture, &mut image)?; | |
59 | } | |
60 | ||
61 | if !derived { | |
62 | Image::create_image(picture, &mut image, &mut format, width, height)?; | |
63 | } | |
64 | ||
65 | // Safe since `picture.inner.context` represents a valid `VAContext` and `image` has been | |
66 | // successfully created at this point. | |
67 | match (unsafe { | |
68 | bindings::vaMapBuffer(picture.display().handle(), image.buf, &mut addr) | |
69 | }) | |
70 | .check() | |
71 | { | |
72 | Ok(_) => { | |
73 | // Safe since `addr` points to data mapped onto our address space since we called | |
74 | // `vaMapBuffer` above, which also guarantees that the data is valid for | |
75 | // `image.data_size`. | |
76 | let data = | |
77 | unsafe { std::slice::from_raw_parts_mut(addr as _, image.data_size as usize) }; | |
78 | Ok(Self { | |
79 | picture, | |
80 | image, | |
81 | data, | |
82 | derived, | |
83 | dirty: false, | |
84 | }) | |
85 | } | |
86 | Err(e) => { | |
87 | // Safe because `picture.inner.context` represents a valid `VAContext` and `image` | |
88 | // represents a valid `VAImage`. | |
89 | unsafe { | |
90 | bindings::vaDestroyImage(picture.display().handle(), image.image_id); | |
91 | } | |
92 | Err(e) | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | /// Creates `image` from `picture` using `vaCreateImage` and `vaGetImage` in order to copy the | |
98 | /// surface data into the image buffer. | |
99 | fn create_image( | |
100 | picture: &'a Picture<PictureSync>, | |
101 | image: &mut bindings::VAImage, | |
102 | format: &mut bindings::VAImageFormat, | |
103 | width: u32, | |
104 | height: u32, | |
105 | ) -> VAResult<()> { | |
106 | let dpy = picture.display().handle(); | |
107 | ||
108 | // Safe because `picture.inner.context` represents a valid | |
109 | // VAContext. | |
110 | (unsafe { bindings::vaCreateImage(dpy, format, width as i32, height as i32, image) }) | |
111 | .check()?; | |
112 | ||
113 | // Safe because `picture.inner.context` represents a valid VAContext, | |
114 | // `picture.surface` represents a valid VASurface and `image` represents | |
115 | // a valid `VAImage`. | |
116 | if let Err(e) = (unsafe { | |
117 | bindings::vaGetImage( | |
118 | dpy, | |
119 | picture.surface().id(), | |
120 | 0, | |
121 | 0, | |
122 | width, | |
123 | height, | |
124 | image.image_id, | |
125 | ) | |
126 | }) | |
127 | .check() | |
128 | { | |
129 | // Safe since `image` represents a valid `VAImage`. | |
130 | unsafe { | |
131 | bindings::vaDestroyImage(dpy, image.image_id); | |
132 | } | |
133 | return Err(e); | |
134 | } | |
135 | ||
136 | Ok(()) | |
137 | } | |
138 | ||
139 | /// Tries to derive `image` from `picture` to access the raw surface data without copy. | |
140 | /// | |
141 | /// Returns `Ok(true)` if the image has been successfully derived, `Ok(false)` if deriving is | |
142 | /// not possible and `create_image` should be used as a fallback, or an error if an error | |
143 | /// occurred. | |
144 | fn derive_image( | |
145 | picture: &'a Picture<PictureSync>, | |
146 | image: &mut bindings::VAImage, | |
147 | ) -> VAResult<bool> { | |
148 | let status = unsafe { | |
149 | bindings::vaDeriveImage(picture.display().handle(), picture.surface().id(), image) | |
150 | }; | |
151 | ||
152 | if status == bindings::constants::VA_STATUS_ERROR_OPERATION_FAILED as i32 { | |
153 | // The implementation can't derive, try the create API instead. | |
154 | Ok(false) | |
155 | } else { | |
156 | status.check()?; | |
157 | Ok(true) | |
158 | } | |
159 | } | |
160 | ||
161 | /// Get a reference to the underlying `VAImage` that describes this image. | |
162 | pub fn image(&self) -> &bindings::VAImage { | |
163 | &self.image | |
164 | } | |
165 | } | |
166 | ||
167 | impl<'a> AsRef<[u8]> for Image<'a> { | |
168 | fn as_ref(&self) -> &[u8] { | |
169 | self.data | |
170 | } | |
171 | } | |
172 | ||
173 | impl<'a> AsMut<[u8]> for Image<'a> { | |
174 | fn as_mut(&mut self) -> &mut [u8] { | |
175 | self.dirty = true; | |
176 | self.data | |
177 | } | |
178 | } | |
179 | ||
180 | impl<'a> Drop for Image<'a> { | |
181 | fn drop(&mut self) { | |
182 | if !self.derived && self.dirty { | |
183 | // Safe because `picture.inner.context` represents a valid `VAContext`, | |
184 | // `picture.surface` represents a valid `VASurface` and `image` represents a valid | |
185 | // `VAImage`. | |
186 | unsafe { | |
187 | bindings::vaPutImage( | |
188 | self.picture.display().handle(), | |
189 | self.picture.surface().id(), | |
190 | self.image.image_id, | |
191 | 0, | |
192 | 0, | |
193 | self.image.width as u32, | |
194 | self.image.height as u32, | |
195 | 0, | |
196 | 0, | |
197 | self.image.width as u32, | |
198 | self.image.height as u32, | |
199 | ); | |
200 | } | |
201 | } | |
202 | unsafe { | |
203 | // Safe since the buffer is mapped in `Image::new`, so `self.image.buf` points to a | |
204 | // valid `VABufferID`. | |
205 | bindings::vaUnmapBuffer(self.picture.display().handle(), self.image.buf); | |
206 | // Safe since `self.image` represents a valid `VAImage`. | |
207 | bindings::vaDestroyImage(self.picture.display().handle(), self.image.image_id); | |
208 | } | |
209 | } | |
210 | } |