blob: 92e697af1527512820cfa15aca7728325b161645 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::VecDeque;
use std::rc::Rc;
use std::rc::Weak;
use libva::Display;
use libva::Surface;
use libva::SurfaceMemoryDescriptor;
use libva::VASurfaceID;
use crate::decoder::FramePool;
use crate::Resolution;
/// A VA Surface obtained from a `[SurfacePool]`.
///
/// The surface will automatically be returned to its pool upon dropping, provided the pool still
/// exists and the surface is still compatible with it.
pub struct PooledVaSurface<M: SurfaceMemoryDescriptor> {
surface: Option<Surface<M>>,
pool: Weak<RefCell<VaSurfacePoolInner<M>>>,
}
impl<M: SurfaceMemoryDescriptor> PooledVaSurface<M> {
fn new(surface: Surface<M>, pool: &Rc<RefCell<VaSurfacePoolInner<M>>>) -> Self {
Self {
surface: Some(surface),
pool: Rc::downgrade(pool),
}
}
/// Detach this surface from the pool. It will not be returned, and we can dispose of it
/// freely.
pub fn detach_from_pool(mut self) -> Surface<M> {
// `unwrap` will never fail as `surface` is `Some` up to this point.
let surface = self.surface.take().unwrap();
if let Some(pool) = self.pool.upgrade() {
(*pool).borrow_mut().managed_surfaces.remove(&surface.id());
}
surface
}
}
impl<M: SurfaceMemoryDescriptor> Borrow<Surface<M>> for PooledVaSurface<M> {
fn borrow(&self) -> &Surface<M> {
// `unwrap` will never fail as `surface` is `Some` until the object is dropped.
self.surface.as_ref().unwrap()
}
}
impl<M: SurfaceMemoryDescriptor> AsRef<M> for PooledVaSurface<M> {
fn as_ref(&self) -> &M {
<Self as Borrow<Surface<M>>>::borrow(self).as_ref()
}
}
impl<M: SurfaceMemoryDescriptor> Drop for PooledVaSurface<M> {
fn drop(&mut self) {
// If the surface has not been detached...
if let Some(surface) = self.surface.take() {
// ... and the pool still exists...
if let Some(pool) = self.pool.upgrade() {
let mut pool_borrowed = (*pool).borrow_mut();
// ... and the pool is still managing this surface, return it.
if pool_borrowed.managed_surfaces.contains_key(&surface.id()) {
pool_borrowed.surfaces.push_back(surface);
return;
}
}
// The surface cannot be returned to the pool and can be gracefully dropped.
log::debug!(
"Dropping stale surface: {}, ({:?})",
surface.id(),
surface.size()
)
}
}
}
struct VaSurfacePoolInner<M: SurfaceMemoryDescriptor> {
display: Rc<Display>,
rt_format: u32,
usage_hint: Option<libva::UsageHint>,
coded_resolution: Resolution,
surfaces: VecDeque<Surface<M>>,
/// All the surfaces managed by this pool, indexed by their surface ID. We keep their
/// resolution so we can remove them in case of a coded resolution change even if they
/// are currently borrowed.
managed_surfaces: BTreeMap<VASurfaceID, Resolution>,
}
/// A surface pool to reduce the number of costly Surface allocations.
///
/// The pool only houses Surfaces that fits the pool's coded resolution.
/// Stale surfaces are dropped when either the pool resolution changes, or when
/// stale surfaces are retrieved.
///
/// This means that this pool is suitable for inter-frame DRC, as the stale
/// surfaces will gracefully be dropped, which is arguably better than the
/// alternative of having more than one pool active at a time.
pub struct VaSurfacePool<M: SurfaceMemoryDescriptor> {
inner: Rc<RefCell<VaSurfacePoolInner<M>>>,
}
impl<M: SurfaceMemoryDescriptor> VaSurfacePool<M> {
/// Add a surface to the pool.
///
/// This can be an entirely new surface, or one that has been previously obtained using
/// `get_surface` and is returned.
///
/// Returns an error (and the passed `surface` back) if the surface is not at least as
/// large as the current coded resolution of the pool.
#[allow(dead_code)]
fn add_surface(&mut self, surface: Surface<M>) -> Result<(), Surface<M>> {
let mut inner = (*self.inner).borrow_mut();
if Resolution::from(surface.size()).can_contain(inner.coded_resolution) {
inner
.managed_surfaces
.insert(surface.id(), surface.size().into());
inner.surfaces.push_back(surface);
Ok(())
} else {
Err(surface)
}
}
/// Create a new pool.
///
// # Arguments
///
/// * `display` - the VA display to create the surfaces from.
/// * `rt_format` - the VA RT format to use for the surfaces.
/// * `usage_hint` - hint about how the surfaces from this pool will be used.
/// * `coded_resolution` - resolution of the surfaces.
pub fn new(
display: Rc<Display>,
rt_format: u32,
usage_hint: Option<libva::UsageHint>,
coded_resolution: Resolution,
) -> Self {
Self {
inner: Rc::new(RefCell::new(VaSurfacePoolInner {
display,
rt_format,
usage_hint,
coded_resolution,
surfaces: VecDeque::new(),
managed_surfaces: Default::default(),
})),
}
}
/// Gets a free surface from the pool.
pub fn get_surface(&mut self) -> Option<PooledVaSurface<M>> {
let mut inner = (*self.inner).borrow_mut();
let surface = inner.surfaces.pop_front();
// Make sure the invariant holds when debugging. Can save costly
// debugging time during future refactors, if any.
debug_assert!({
match surface.as_ref() {
Some(s) => Resolution::from(s.size()).can_contain(inner.coded_resolution),
None => true,
}
});
surface.map(|s| PooledVaSurface::new(s, &self.inner))
}
}
impl<M: SurfaceMemoryDescriptor> FramePool<M> for VaSurfacePool<M> {
fn coded_resolution(&self) -> Resolution {
(*self.inner).borrow().coded_resolution
}
fn set_coded_resolution(&mut self, resolution: Resolution) {
let mut inner = (*self.inner).borrow_mut();
inner.coded_resolution = resolution;
inner
.managed_surfaces
.retain(|_, res| res.can_contain(resolution));
inner
.surfaces
.retain(|s| Resolution::from(s.size()).can_contain(resolution));
}
fn add_frames(&mut self, descriptors: Vec<M>) -> Result<(), anyhow::Error> {
let mut inner = (*self.inner).borrow_mut();
let surfaces = inner
.display
.create_surfaces(
inner.rt_format,
// Let the hardware decide the best internal format - we will get the desired fourcc
// when creating the image.
None,
inner.coded_resolution.width,
inner.coded_resolution.height,
inner.usage_hint,
descriptors,
)
.map_err(|e| anyhow::anyhow!(e))?;
for surface in &surfaces {
inner
.managed_surfaces
.insert(surface.id(), surface.size().into());
}
inner.surfaces.extend(surfaces);
Ok(())
}
fn num_free_frames(&self) -> usize {
(*self.inner).borrow().surfaces.len()
}
fn num_managed_frames(&self) -> usize {
(*self.inner).borrow().managed_surfaces.len()
}
fn clear(&mut self) {
let mut pool = (*self.inner).borrow_mut();
pool.surfaces.clear();
pool.managed_surfaces.clear();
}
fn take_free_frame(&mut self) -> Option<Box<dyn AsRef<M>>> {
(**self)
.borrow_mut()
.get_surface(self)
.map(|s| Box::new(s) as Box<dyn AsRef<M>>)
}
}