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