use std::collections::{HashMap, HashSet};
use drmkit_planes::{PlaneRegistry, PlaneType};
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ReserveError {
#[error("wanted {wanted} overlay planes on CRTC {crtc_index}, {found} are available")]
Shortfall {
crtc_index: u32,
wanted: usize,
found: usize,
},
}
#[derive(Debug, Default)]
pub struct OverlayReservation {
by_crtc: HashMap<u32, Vec<u32>>,
all: HashSet<u32>,
}
impl OverlayReservation {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn reserve(
&mut self,
registry: &PlaneRegistry,
crtc_index: u32,
fourcc: u32,
count: usize,
min_zpos: u64,
) -> Result<Vec<u32>, ReserveError> {
if count == 0 {
self.by_crtc.entry(crtc_index).or_default();
return Ok(Vec::new());
}
self.release(crtc_index);
let mut candidates: Vec<(u64, u32)> = registry
.for_crtc(crtc_index)
.filter(|plane| plane.plane_type == PlaneType::Overlay)
.filter(|plane| plane.scans_out_linear(fourcc))
.filter(|plane| !self.all.contains(&plane.id))
.filter_map(|plane| match plane.zpos_min {
None => (min_zpos == 0).then_some((0, plane.id)),
Some(zpos) => (zpos >= min_zpos).then_some((zpos, plane.id)),
})
.collect();
if candidates.len() < count {
return Err(ReserveError::Shortfall {
crtc_index,
wanted: count,
found: candidates.len(),
});
}
candidates.sort_unstable();
let claimed: Vec<u32> = candidates
.into_iter()
.take(count)
.map(|(_, id)| id)
.collect();
self.all.extend(claimed.iter().copied());
self.by_crtc.insert(crtc_index, claimed.clone());
Ok(claimed)
}
pub fn release(&mut self, crtc_index: u32) {
if let Some(planes) = self.by_crtc.remove(&crtc_index) {
for plane in planes {
self.all.remove(&plane);
}
}
}
#[must_use]
pub fn reserved_for(&self, crtc_index: u32) -> &[u32] {
self.by_crtc.get(&crtc_index).map_or(&[], Vec::as_slice)
}
#[must_use]
pub fn all_reserved(&self) -> Vec<u32> {
let mut all: Vec<u32> = self.all.iter().copied().collect();
all.sort_unstable();
all
}
}