// 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 { surface: Option>, pool: Weak>>, } impl PooledVaSurface { fn new(surface: Surface, pool: &Rc>>) -> 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 { // `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 Borrow> for PooledVaSurface { fn borrow(&self) -> &Surface { // `unwrap` will never fail as `surface` is `Some` until the object is dropped. self.surface.as_ref().unwrap() } } impl AsRef for PooledVaSurface { fn as_ref(&self) -> &M { >>::borrow(self).as_ref() } } impl Drop for PooledVaSurface { 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 { display: Rc, rt_format: u32, usage_hint: Option, coded_resolution: Resolution, surfaces: VecDeque>, /// 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, } /// 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 { inner: Rc>>, } impl VaSurfacePool { /// 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) -> Result<(), Surface> { 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, rt_format: u32, usage_hint: Option, 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> { 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 FramePool for VaSurfacePool { type Descriptor = 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) -> 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(); } }