mod partition;
pub use partition::{NarrowPolicy, SlotState, partition_for_policy};
use std::rc::Rc;
use drmkit_scene::{DisplayParams, LayerBufferSource, LayerHandle, LayerScene};
mod shared;
use shared::SharedSource;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Copy)]
#[allow(
clippy::exhaustive_structs,
reason = "built by callers, with no default to fill a new field from"
)]
pub struct Target {
pub scene_index: usize,
pub display: DisplayParams,
pub force_composited: bool,
}
#[allow(
clippy::exhaustive_structs,
reason = "built by callers, with no default to fill a new field from"
)]
pub struct LayerSpec {
pub source: Rc<std::cell::RefCell<dyn LayerBufferSource>>,
pub targets: Vec<Target>,
}
impl std::fmt::Debug for LayerSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LayerSpec")
.field("targets", &self.targets.len())
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct SetLayerHandle {
id: u32,
generation: u32,
}
impl SetLayerHandle {
#[must_use]
pub const fn is_valid(self) -> bool {
self.id != 0
}
}
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
#[non_exhaustive]
pub enum SetError {
#[error("a layer needs at least one target")]
NoTargets,
#[error("scene index {index} is out of range ({count} scenes)")]
NoSuchScene {
index: usize,
count: usize,
},
}
struct SetLayer {
generation: u32,
copies: Vec<(usize, LayerHandle)>,
}
pub struct SceneSet {
scenes: Vec<Option<LayerScene>>,
layers: Vec<Option<SetLayer>>,
free_layers: Vec<usize>,
next_generation: u32,
}
impl std::fmt::Debug for SceneSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SceneSet")
.field("slots", &self.scenes.len())
.field("engaged", &self.scenes.iter().flatten().count())
.finish_non_exhaustive()
}
}
impl SceneSet {
#[must_use]
pub fn new(scenes: Vec<LayerScene>) -> Self {
Self {
scenes: scenes.into_iter().map(Some).collect(),
layers: Vec::new(),
free_layers: Vec::new(),
next_generation: 1,
}
}
#[must_use]
pub fn slot_count(&self) -> usize {
self.scenes.len()
}
#[must_use]
pub fn scene_count(&self) -> usize {
self.scenes.iter().flatten().count()
}
#[must_use]
pub fn scene(&self, index: usize) -> Option<&LayerScene> {
self.scenes.get(index)?.as_ref()
}
pub fn scene_mut(&mut self, index: usize) -> Option<&mut LayerScene> {
self.scenes.get_mut(index)?.as_mut()
}
pub fn add_scene(&mut self, scene: LayerScene) -> usize {
if let Some(index) = self.scenes.iter().position(Option::is_none) {
self.scenes[index] = Some(scene);
return index;
}
self.scenes.push(Some(scene));
self.scenes.len() - 1
}
pub fn remove_scene(&mut self, index: usize) {
if let Some(slot) = self.scenes.get_mut(index) {
*slot = None;
}
}
pub fn add_layer(&mut self, spec: &LayerSpec) -> Result<SetLayerHandle, SetError> {
if spec.targets.is_empty() {
return Err(SetError::NoTargets);
}
for target in &spec.targets {
if self.scene(target.scene_index).is_none() {
return Err(SetError::NoSuchScene {
index: target.scene_index,
count: self.scenes.len(),
});
}
}
let mut copies = Vec::with_capacity(spec.targets.len());
for target in &spec.targets {
let shared = SharedSource(Rc::clone(&spec.source));
let Some(scene) = self.scene_mut(target.scene_index) else {
continue;
};
let handle = scene.add_layer(Box::new(shared));
if let Some(layer) = scene.layer_mut(handle) {
layer.set_display(target.display);
layer.set_force_composited(target.force_composited);
}
copies.push((target.scene_index, handle));
}
let generation = self.next_generation;
self.next_generation = self.next_generation.wrapping_add(1).max(1);
let layer = Some(SetLayer { generation, copies });
let index = if let Some(index) = self.free_layers.pop() {
self.layers[index] = layer;
index
} else {
self.layers.push(layer);
self.layers.len() - 1
};
Ok(SetLayerHandle {
id: u32::try_from(index + 1).unwrap_or(u32::MAX),
generation,
})
}
pub fn remove_layer(&mut self, handle: SetLayerHandle) {
if !handle.is_valid() {
return;
}
let Some(index) = (handle.id as usize).checked_sub(1) else {
return;
};
let Some(slot) = self.layers.get_mut(index) else {
return;
};
let Some(layer) = slot.as_ref() else { return };
if layer.generation != handle.generation {
return;
}
let Some(taken) = slot.take() else { return };
self.free_layers.push(index);
let copies = taken.copies;
for (scene_index, layer_handle) in copies {
if let Some(scene) = self.scene_mut(scene_index) {
scene.remove_layer(layer_handle);
}
}
}
#[must_use]
pub fn slot_states(&self, wants_modeset: &[usize]) -> Vec<SlotState> {
self.scenes
.iter()
.enumerate()
.map(|(index, slot)| SlotState {
is_hole: slot.is_none(),
wants_modeset: wants_modeset.contains(&index),
})
.collect()
}
#[must_use]
pub fn plan_commits(&self, wants_modeset: &[usize], policy: NarrowPolicy) -> Vec<Vec<usize>> {
partition_for_policy(&self.slot_states(wants_modeset), policy)
}
}