]>
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 std::cell::Ref; | |
6 | use std::cell::RefCell; | |
7 | use std::cell::RefMut; | |
8 | use std::marker::PhantomData; | |
9 | use std::rc::Rc; | |
10 | ||
11 | use anyhow::anyhow; | |
12 | use anyhow::Result; | |
13 | ||
14 | use crate::bindings; | |
15 | use crate::buffer::Buffer; | |
16 | use crate::context::Context; | |
17 | use crate::display::Display; | |
18 | use crate::status::Status; | |
19 | use crate::surface::Surface; | |
20 | ||
21 | // Use the sealed trait pattern to make sure that new states are not created in caller code. More | |
22 | // information about the sealed trait pattern can be found at | |
23 | // <https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed> | |
24 | mod private { | |
25 | pub trait Sealed {} | |
26 | } | |
27 | ||
28 | /// A `Picture` will only have valid YUV data after a sequence of operations are performed in a | |
29 | /// particular order. This order correspond to the following VA-API calls: `vaBeginPicture`, | |
30 | /// `vaRenderPicture`, `vaEndPicture` and `vaSyncSurface`. This trait enforces this ordering by | |
31 | /// implementing the Typestate pattern to constrain what operations are available in what particular | |
32 | /// states. | |
33 | /// | |
34 | /// The states for the state machine are: | |
35 | /// | |
36 | /// * PictureNew -> PictureBegin | |
37 | /// * PictureBegin -> PictureRender | |
38 | /// * PictureRender ->PictureEnd | |
39 | /// * PictureEnd -> PictureSync | |
40 | /// | |
41 | /// Where the surface can be reclaimed in both `PictureNew` and `PictureSync`, as either no | |
42 | /// operation took place (as in `PictureNew`), or it is guaranteed that the operation has already | |
43 | /// completed (as in `PictureSync`). | |
44 | /// | |
45 | /// More information about the Typestate pattern can be found at | |
46 | /// <http://cliffle.com/blog/rust-typestate/> | |
47 | pub trait PictureState: private::Sealed {} | |
48 | ||
49 | /// Represents a `Picture` that has just been created. | |
50 | pub enum PictureNew {} | |
51 | impl PictureState for PictureNew {} | |
52 | impl private::Sealed for PictureNew {} | |
53 | ||
54 | /// Represents a `Picture` after `vaBeginPicture` has been called. | |
55 | pub enum PictureBegin {} | |
56 | impl PictureState for PictureBegin {} | |
57 | impl private::Sealed for PictureBegin {} | |
58 | ||
59 | /// Represents a `Picture` after `vaRenderPicture` has been called. | |
60 | pub enum PictureRender {} | |
61 | impl PictureState for PictureRender {} | |
62 | impl private::Sealed for PictureRender {} | |
63 | ||
64 | /// Represents a `Picture` after `vaEndPicture` has been called. | |
65 | pub enum PictureEnd {} | |
66 | impl PictureState for PictureEnd {} | |
67 | impl private::Sealed for PictureEnd {} | |
68 | ||
69 | /// Represents a `Picture` after `vaSyncSurface` has been called on the underlying surface. | |
70 | pub enum PictureSync {} | |
71 | impl PictureState for PictureSync {} | |
72 | impl private::Sealed for PictureSync {} | |
73 | ||
74 | /// Represents a state where one can reclaim the underlying `Surface` for this `Picture`. This is | |
75 | /// true when either no decoding has been initiated or, alternatively, when the decoding operation | |
76 | /// has completed for the underlying `vaSurface` | |
77 | pub trait PictureReclaimableSurface: PictureState + private::Sealed {} | |
78 | impl PictureReclaimableSurface for PictureNew {} | |
79 | impl PictureReclaimableSurface for PictureSync {} | |
80 | ||
81 | struct PictureInner { | |
82 | /// Timestamp of the picture. | |
83 | timestamp: u64, | |
84 | /// A context associated with this picture. | |
85 | context: Rc<Context>, | |
86 | /// Contains the buffers used to decode the data. | |
87 | buffers: Vec<Buffer>, | |
88 | /// Contains the actual decoded data. Note that the surface may be shared in | |
89 | /// interlaced decoding. | |
90 | surface: Rc<RefCell<Surface>>, | |
91 | } | |
92 | ||
93 | /// A `Surface` that is being rendered into. | |
94 | /// | |
95 | /// This struct abstracts the decoding flow using `vaBeginPicture`, `vaRenderPicture`, | |
96 | /// `vaEndPicture` and `vaSyncSurface` in a type-safe way. | |
97 | /// | |
98 | /// The surface will have valid picture data after all the stages of decoding are called. | |
99 | pub struct Picture<S: PictureState> { | |
100 | inner: Box<PictureInner>, | |
101 | phantom: std::marker::PhantomData<S>, | |
102 | } | |
103 | ||
104 | impl Picture<PictureNew> { | |
105 | /// Creates a new Picture with a given `timestamp`. `surface` is the underlying surface that | |
106 | /// libva will render to. | |
107 | pub fn new(timestamp: u64, context: Rc<Context>, surface: Surface) -> Self { | |
108 | Self { | |
109 | inner: Box::new(PictureInner { | |
110 | timestamp, | |
111 | context, | |
112 | buffers: Default::default(), | |
113 | surface: Rc::new(RefCell::new(surface)), | |
114 | }), | |
115 | ||
116 | phantom: PhantomData, | |
117 | } | |
118 | } | |
119 | ||
120 | /// Creates a new Picture with a given `frame_number` to identify it, | |
121 | /// reusing the Surface from `picture`. This is useful for interlaced | |
122 | /// decoding as one can render both fields to the same underlying surface. | |
123 | pub fn new_from_same_surface<T: PictureReclaimableSurface, S: PictureReclaimableSurface>( | |
124 | timestamp: u64, | |
125 | picture: &Picture<S>, | |
126 | ) -> Picture<T> { | |
127 | let context = Rc::clone(&picture.inner.context); | |
128 | Picture { | |
129 | inner: Box::new(PictureInner { | |
130 | timestamp, | |
131 | context, | |
132 | buffers: Default::default(), | |
133 | surface: Rc::clone(&picture.inner.surface), | |
134 | }), | |
135 | ||
136 | phantom: PhantomData, | |
137 | } | |
138 | } | |
139 | ||
140 | /// Add `buffer` to the picture. | |
141 | pub fn add_buffer(&mut self, buffer: Buffer) { | |
142 | self.inner.buffers.push(buffer); | |
143 | } | |
144 | ||
145 | /// Wrapper around `vaBeginPicture`. | |
146 | pub fn begin(self) -> Result<Picture<PictureBegin>> { | |
147 | // Safe because `self.inner.context` represents a valid VAContext and | |
148 | // `self.inner.surface` represents a valid VASurface. | |
149 | Status(unsafe { | |
150 | bindings::vaBeginPicture( | |
151 | self.inner.context.display().handle(), | |
152 | self.inner.context.id(), | |
153 | self.inner.surface.borrow().id(), | |
154 | ) | |
155 | }) | |
156 | .check()?; | |
157 | ||
158 | Ok(Picture { | |
159 | inner: self.inner, | |
160 | phantom: PhantomData, | |
161 | }) | |
162 | } | |
163 | } | |
164 | ||
165 | impl Picture<PictureBegin> { | |
166 | /// Wrapper around `vaRenderPicture`. | |
167 | pub fn render(self) -> Result<Picture<PictureRender>> { | |
168 | // Safe because `self.inner.context` represents a valid `VAContext` and `self.inner.surface` | |
169 | // represents a valid `VASurface`. `buffers` point to a Rust struct and the vector length is | |
170 | // passed to the C function, so it is impossible to write past the end of the vector's | |
171 | // storage by mistake. | |
172 | Status(unsafe { | |
173 | bindings::vaRenderPicture( | |
174 | self.inner.context.display().handle(), | |
175 | self.inner.context.id(), | |
176 | Buffer::as_id_vec(&self.inner.buffers).as_mut_ptr(), | |
177 | self.inner.buffers.len() as i32, | |
178 | ) | |
179 | }) | |
180 | .check()?; | |
181 | ||
182 | Ok(Picture { | |
183 | inner: self.inner, | |
184 | phantom: PhantomData, | |
185 | }) | |
186 | } | |
187 | } | |
188 | ||
189 | impl Picture<PictureRender> { | |
190 | /// Wrapper around `vaEndPicture`. | |
191 | pub fn end(self) -> Result<Picture<PictureEnd>> { | |
192 | // Safe because `self.inner.context` represents a valid `VAContext`. | |
193 | Status(unsafe { | |
194 | bindings::vaEndPicture( | |
195 | self.inner.context.display().handle(), | |
196 | self.inner.context.id(), | |
197 | ) | |
198 | }) | |
199 | .check()?; | |
200 | Ok(Picture { | |
201 | inner: self.inner, | |
202 | phantom: PhantomData, | |
203 | }) | |
204 | } | |
205 | } | |
206 | ||
207 | impl Picture<PictureEnd> { | |
208 | /// Syncs the picture, ensuring that all pending operations are complete when this call returns. | |
209 | pub fn sync(self) -> std::result::Result<Picture<PictureSync>, (anyhow::Error, Self)> { | |
210 | let res = self.inner.surface.borrow().sync(); | |
211 | ||
212 | match res { | |
213 | Ok(()) => Ok(Picture { | |
214 | inner: self.inner, | |
215 | phantom: PhantomData, | |
216 | }), | |
217 | Err(e) => Err((e, self)), | |
218 | } | |
219 | } | |
220 | ||
221 | /// Queries the status of the underlying surface. | |
222 | /// | |
223 | /// This call can be used to implement a non-blocking path, wherein a decoder queries the status | |
224 | /// of the surface after each decode operation instead of blocking on it. | |
225 | pub fn query_status(&self) -> Result<bindings::VASurfaceStatus::Type> { | |
226 | self.inner.surface.borrow_mut().query_status() | |
227 | } | |
228 | } | |
229 | ||
230 | impl<S: PictureState> Picture<S> { | |
231 | /// Returns the timestamp of this picture. | |
232 | pub fn timestamp(&self) -> u64 { | |
233 | self.inner.timestamp | |
234 | } | |
235 | ||
236 | /// Returns the ID of the underlying surface. | |
237 | pub fn surface_id(&self) -> bindings::VASurfaceID { | |
238 | self.inner.surface.borrow().id() | |
239 | } | |
240 | ||
241 | /// Returns a reference to the display owning this `Picture`. | |
242 | pub(crate) fn display(&self) -> &Rc<Display> { | |
243 | self.inner.context.display() | |
244 | } | |
245 | ||
246 | /// Returns the size of the surface being rendered to by this `Picture`. | |
247 | pub fn surface_size(&self) -> (u32, u32) { | |
248 | self.inner.surface.borrow().size() | |
249 | } | |
250 | } | |
251 | ||
252 | impl<S: PictureReclaimableSurface> Picture<S> { | |
253 | /// Reclaim ownership of the Surface this picture has been created from, consuming the picture | |
254 | /// in the process. Useful if the Surface is part of a pool. | |
255 | pub fn take_surface(self) -> Result<Surface> { | |
256 | match Rc::try_unwrap(self.inner.surface) { | |
257 | Ok(surface) => Ok(surface.into_inner()), | |
258 | Err(_) => Err(anyhow!("Surface still in use")), | |
259 | } | |
260 | } | |
261 | ||
262 | /// Returns a reference to the underlying `Surface` for this `Picture` | |
263 | pub fn surface(&self) -> Ref<Surface> { | |
264 | self.inner.surface.borrow() | |
265 | } | |
266 | ||
267 | /// Returns a mutable reference to the underlying `Surface` for this `Picture` | |
268 | pub fn surface_mut(&mut self) -> RefMut<Surface> { | |
269 | self.inner.surface.borrow_mut() | |
270 | } | |
271 | } |