cros_codecs/backend/vaapi/
surface_pool.rs

1// Copyright 2023 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 std::borrow::Borrow;
6use std::cell::RefCell;
7use std::collections::BTreeMap;
8use std::collections::VecDeque;
9use std::rc::Rc;
10use std::rc::Weak;
11
12use libva::Display;
13use libva::Surface;
14use libva::SurfaceMemoryDescriptor;
15use libva::VASurfaceID;
16
17use crate::decoder::FramePool;
18use crate::Resolution;
19
20/// A VA Surface obtained from a `[SurfacePool]`.
21///
22/// The surface will automatically be returned to its pool upon dropping, provided the pool still
23/// exists and the surface is still compatible with it.
24pub struct PooledVaSurface<M: SurfaceMemoryDescriptor> {
25    surface: Option<Surface<M>>,
26    pool: Weak<RefCell<VaSurfacePoolInner<M>>>,
27}
28
29impl<M: SurfaceMemoryDescriptor> PooledVaSurface<M> {
30    fn new(surface: Surface<M>, pool: &Rc<RefCell<VaSurfacePoolInner<M>>>) -> Self {
31        Self {
32            surface: Some(surface),
33            pool: Rc::downgrade(pool),
34        }
35    }
36
37    /// Detach this surface from the pool. It will not be returned, and we can dispose of it
38    /// freely.
39    pub fn detach_from_pool(mut self) -> Surface<M> {
40        // `unwrap` will never fail as `surface` is `Some` up to this point.
41        let surface = self.surface.take().unwrap();
42
43        if let Some(pool) = self.pool.upgrade() {
44            (*pool).borrow_mut().managed_surfaces.remove(&surface.id());
45        }
46
47        surface
48    }
49}
50
51impl<M: SurfaceMemoryDescriptor> Borrow<Surface<M>> for PooledVaSurface<M> {
52    fn borrow(&self) -> &Surface<M> {
53        // `unwrap` will never fail as `surface` is `Some` until the object is dropped.
54        self.surface.as_ref().unwrap()
55    }
56}
57
58impl<M: SurfaceMemoryDescriptor> AsRef<M> for PooledVaSurface<M> {
59    fn as_ref(&self) -> &M {
60        <Self as Borrow<Surface<M>>>::borrow(self).as_ref()
61    }
62}
63
64impl<M: SurfaceMemoryDescriptor> Drop for PooledVaSurface<M> {
65    fn drop(&mut self) {
66        // If the surface has not been detached...
67        if let Some(surface) = self.surface.take() {
68            // ... and the pool still exists...
69            if let Some(pool) = self.pool.upgrade() {
70                let mut pool_borrowed = (*pool).borrow_mut();
71                // ... and the pool is still managing this surface, return it.
72                if pool_borrowed.managed_surfaces.contains_key(&surface.id()) {
73                    pool_borrowed.surfaces.push_back(surface);
74                    return;
75                }
76            }
77
78            // The surface cannot be returned to the pool and can be gracefully dropped.
79            log::debug!(
80                "Dropping stale surface: {}, ({:?})",
81                surface.id(),
82                surface.size()
83            )
84        }
85    }
86}
87
88struct VaSurfacePoolInner<M: SurfaceMemoryDescriptor> {
89    display: Rc<Display>,
90    rt_format: u32,
91    usage_hint: Option<libva::UsageHint>,
92    coded_resolution: Resolution,
93    surfaces: VecDeque<Surface<M>>,
94    /// All the surfaces managed by this pool, indexed by their surface ID. We keep their
95    /// resolution so we can remove them in case of a coded resolution change even if they
96    /// are currently borrowed.
97    managed_surfaces: BTreeMap<VASurfaceID, Resolution>,
98}
99
100/// A surface pool to reduce the number of costly Surface allocations.
101///
102/// The pool only houses Surfaces that fits the pool's coded resolution.
103/// Stale surfaces are dropped when either the pool resolution changes, or when
104/// stale surfaces are retrieved.
105///
106/// This means that this pool is suitable for inter-frame DRC, as the stale
107/// surfaces will gracefully be dropped, which is arguably better than the
108/// alternative of having more than one pool active at a time.
109pub struct VaSurfacePool<M: SurfaceMemoryDescriptor> {
110    inner: Rc<RefCell<VaSurfacePoolInner<M>>>,
111}
112
113impl<M: SurfaceMemoryDescriptor> VaSurfacePool<M> {
114    /// Add a surface to the pool.
115    ///
116    /// This can be an entirely new surface, or one that has been previously obtained using
117    /// `get_surface` and is returned.
118    ///
119    /// Returns an error (and the passed `surface` back) if the surface is not at least as
120    /// large as the current coded resolution of the pool.
121    #[allow(dead_code)]
122    fn add_surface(&mut self, surface: Surface<M>) -> Result<(), Surface<M>> {
123        let mut inner = (*self.inner).borrow_mut();
124
125        if Resolution::from(surface.size()).can_contain(inner.coded_resolution) {
126            inner
127                .managed_surfaces
128                .insert(surface.id(), surface.size().into());
129            inner.surfaces.push_back(surface);
130            Ok(())
131        } else {
132            Err(surface)
133        }
134    }
135
136    /// Create a new pool.
137    ///
138    // # Arguments
139    ///
140    /// * `display` - the VA display to create the surfaces from.
141    /// * `rt_format` - the VA RT format to use for the surfaces.
142    /// * `usage_hint` - hint about how the surfaces from this pool will be used.
143    /// * `coded_resolution` - resolution of the surfaces.
144    pub fn new(
145        display: Rc<Display>,
146        rt_format: u32,
147        usage_hint: Option<libva::UsageHint>,
148        coded_resolution: Resolution,
149    ) -> Self {
150        Self {
151            inner: Rc::new(RefCell::new(VaSurfacePoolInner {
152                display,
153                rt_format,
154                usage_hint,
155                coded_resolution,
156                surfaces: VecDeque::new(),
157                managed_surfaces: Default::default(),
158            })),
159        }
160    }
161
162    /// Gets a free surface from the pool.
163    pub fn get_surface(&mut self) -> Option<PooledVaSurface<M>> {
164        let mut inner = (*self.inner).borrow_mut();
165        let surface = inner.surfaces.pop_front();
166
167        // Make sure the invariant holds when debugging. Can save costly
168        // debugging time during future refactors, if any.
169        debug_assert!({
170            match surface.as_ref() {
171                Some(s) => Resolution::from(s.size()).can_contain(inner.coded_resolution),
172                None => true,
173            }
174        });
175
176        surface.map(|s| PooledVaSurface::new(s, &self.inner))
177    }
178}
179
180impl<M: SurfaceMemoryDescriptor> FramePool for VaSurfacePool<M> {
181    type Descriptor = M;
182
183    fn coded_resolution(&self) -> Resolution {
184        (*self.inner).borrow().coded_resolution
185    }
186
187    fn set_coded_resolution(&mut self, resolution: Resolution) {
188        let mut inner = (*self.inner).borrow_mut();
189
190        inner.coded_resolution = resolution;
191        inner
192            .managed_surfaces
193            .retain(|_, res| res.can_contain(resolution));
194        inner
195            .surfaces
196            .retain(|s| Resolution::from(s.size()).can_contain(resolution));
197    }
198
199    fn add_frames(&mut self, descriptors: Vec<Self::Descriptor>) -> Result<(), anyhow::Error> {
200        let mut inner = (*self.inner).borrow_mut();
201
202        let surfaces = inner
203            .display
204            .create_surfaces(
205                inner.rt_format,
206                // Let the hardware decide the best internal format - we will get the desired fourcc
207                // when creating the image.
208                None,
209                inner.coded_resolution.width,
210                inner.coded_resolution.height,
211                inner.usage_hint,
212                descriptors,
213            )
214            .map_err(|e| anyhow::anyhow!(e))?;
215
216        for surface in &surfaces {
217            inner
218                .managed_surfaces
219                .insert(surface.id(), surface.size().into());
220        }
221        inner.surfaces.extend(surfaces);
222
223        Ok(())
224    }
225
226    fn num_free_frames(&self) -> usize {
227        (*self.inner).borrow().surfaces.len()
228    }
229
230    fn num_managed_frames(&self) -> usize {
231        (*self.inner).borrow().managed_surfaces.len()
232    }
233
234    fn clear(&mut self) {
235        let mut pool = (*self.inner).borrow_mut();
236
237        pool.surfaces.clear();
238        pool.managed_surfaces.clear();
239    }
240}