use std::collections::HashMap;
use drmkit_fmt::{Modifier, ModifierProbeCache, Verdict};
use crate::layer::Layer;
use crate::matching::BipartiteMatching;
use crate::plane_order::{PlaneOrder, Position, plane_order_consistent, stacks_by_plane_id};
use crate::prop::PropTag;
use crate::registry::{PlaneCapabilities, PlaneRegistry, PlaneType};
use crate::scoring::{
ScoreContext, keep_priority, layers_intersect, plane_statically_compatible, score_pair,
split_independent_groups,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[allow(
clippy::exhaustive_structs,
reason = "a value defined by its fields; there is nothing to add to it"
)]
pub struct LayerId(pub u64);
#[derive(Debug, Clone, Copy)]
#[allow(
clippy::exhaustive_structs,
reason = "a value defined by its fields; there is nothing to add to it"
)]
pub struct LayerRef<'a> {
pub id: LayerId,
pub layer: &'a Layer,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(
clippy::exhaustive_enums,
reason = "every caller handles each failure; a new one has to break them so each chooses"
)]
pub enum TestFailure {
Rejected,
NotMaster,
}
pub trait TestCommitter {
fn test_assignment(&mut self, assignment: &[(u32, LayerRef<'_>)]) -> Result<(), TestFailure>;
fn set_extra_plane(&mut self, plane_id: u32, layer: Layer) {
let _ = (plane_id, layer);
}
fn clear_extra_plane(&mut self) {}
fn lit_planes(&mut self, crtc_id: u32) -> Option<Vec<u32>> {
let _ = crtc_id;
None
}
}
#[derive(Debug, Default, Clone, Copy)]
#[allow(
clippy::exhaustive_structs,
reason = "built by callers with `..Default::default()`, which a new field does not break"
)]
pub struct AlwaysAccept {
pub calls: usize,
}
impl TestCommitter for AlwaysAccept {
fn test_assignment(&mut self, _assignment: &[(u32, LayerRef<'_>)]) -> Result<(), TestFailure> {
self.calls += 1;
Ok(())
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PlaneAssignment {
entries: Vec<(u32, LayerId)>,
}
impl PlaneAssignment {
#[must_use]
pub const fn new() -> Self {
Self {
entries: Vec::new(),
}
}
pub fn insert(&mut self, plane_id: u32, layer: LayerId) {
if let Some(entry) = self.entries.iter_mut().find(|(id, _)| *id == plane_id) {
entry.1 = layer;
} else {
self.entries.push((plane_id, layer));
}
}
#[must_use]
pub fn get(&self, plane_id: u32) -> Option<LayerId> {
self.entries
.iter()
.find(|(id, _)| *id == plane_id)
.map(|(_, layer)| *layer)
}
#[must_use]
pub fn get_plane_of(&self, layer: LayerId) -> Option<u32> {
self.entries
.iter()
.find(|(_, id)| *id == layer)
.map(|(plane_id, _)| *plane_id)
}
pub fn remove(&mut self, plane_id: u32) -> Option<LayerId> {
let index = self.entries.iter().position(|(id, _)| *id == plane_id)?;
Some(self.entries.remove(index).1)
}
#[must_use]
pub fn entries(&self) -> &[(u32, LayerId)] {
&self.entries
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
}
#[derive(Debug, Default, Clone)]
pub struct TestCache {
entries: HashMap<(u32, u64), Entry>,
}
#[derive(Debug, Clone, Copy, Default)]
struct Entry {
passed: bool,
failures: u32,
}
impl TestCache {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn lookup(&self, plane_id: u32, property_hash: u64) -> Option<bool> {
self.entries
.get(&(plane_id, property_hash))
.map(|entry| entry.passed)
}
pub fn record(&mut self, plane_id: u32, property_hash: u64, passed: bool) {
let entry = self.entries.entry((plane_id, property_hash)).or_default();
entry.passed = passed;
if !passed {
entry.failures = entry.failures.saturating_add(1);
}
}
#[must_use]
pub fn failure_count(&self, plane_id: u32, property_hash: u64) -> u32 {
self.entries
.get(&(plane_id, property_hash))
.filter(|entry| !entry.passed)
.map_or(0, |entry| entry.failures)
}
pub fn clear(&mut self) {
self.entries.clear();
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct Diagnostics {
pub test_commits_issued: usize,
pub fb_delta_fast_path: bool,
pub budget_exhausted: bool,
}
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
pub struct Allocation {
pub assignment: PlaneAssignment,
pub composited: Vec<LayerId>,
pub diagnostics: Diagnostics,
}
#[derive(Debug, Clone, Copy)]
struct LastCommitted {
layer: Option<LayerId>,
properties: crate::PropertySnapshot,
hash: u64,
}
#[derive(Debug)]
pub struct Allocator {
previous: PlaneAssignment,
previous_valid: bool,
last_committed: HashMap<u32, LastCommitted>,
force_full_writes: bool,
reserved: Vec<u32>,
failure_cache: TestCache,
probe_cache: ModifierProbeCache,
max_test_commits: usize,
test_commits_this_frame: usize,
budget_exhausted_this_frame: bool,
matcher: BipartiteMatching,
canvas_hosts: Vec<u32>,
canvas_layer: Option<Layer>,
canvas_plane: Option<u32>,
previous_canvas_plane: Option<u32>,
canvas_hold: Option<u32>,
previous_composited: Vec<LayerId>,
previous_free: Vec<u32>,
canvas_slot: Option<u64>,
stage_limit: Option<usize>,
canvas_armed: Option<u32>,
}
impl Default for Allocator {
fn default() -> Self {
Self::new()
}
}
impl Allocator {
pub const DEFAULT_MAX_TEST_COMMITS: usize = 16;
#[must_use]
pub fn new() -> Self {
Self {
previous: PlaneAssignment::new(),
previous_valid: false,
last_committed: HashMap::new(),
force_full_writes: false,
reserved: Vec::new(),
failure_cache: TestCache::new(),
probe_cache: ModifierProbeCache::new(),
max_test_commits: Self::DEFAULT_MAX_TEST_COMMITS,
test_commits_this_frame: 0,
budget_exhausted_this_frame: false,
matcher: BipartiteMatching::new(),
canvas_hosts: Vec::new(),
canvas_layer: None,
canvas_plane: None,
previous_canvas_plane: None,
canvas_hold: None,
previous_composited: Vec::new(),
previous_free: Vec::new(),
canvas_slot: None,
stage_limit: None,
canvas_armed: None,
}
}
pub fn set_canvas(&mut self, hosts: &[u32], layer: Option<&Layer>) {
self.canvas_hosts.clear();
self.canvas_layer = layer.cloned();
if self.canvas_layer.is_some() {
self.canvas_hosts.extend_from_slice(hosts);
}
}
#[must_use]
pub const fn canvas_plane(&self) -> Option<u32> {
self.canvas_plane
}
pub const fn hold_canvas_plane(&mut self, plane: Option<u32>) {
self.canvas_hold = plane;
}
pub const fn set_max_test_commits(&mut self, max: usize) {
self.max_test_commits = max;
}
pub fn invalidate_allocation(&mut self) {
self.previous_valid = false;
}
pub fn forget_output(&mut self) {
self.previous = PlaneAssignment::new();
self.previous_valid = false;
self.canvas_plane = None;
self.previous_canvas_plane = None;
self.previous_composited.clear();
self.previous_free.clear();
self.last_committed.clear();
self.failure_cache = TestCache::default();
}
pub fn record_committed(&mut self, plane_id: u32, layer: LayerRef<'_>) {
self.last_committed.insert(
plane_id,
LastCommitted {
layer: Some(layer.id),
properties: layer.layer.snapshot(),
hash: layer.layer.property_hash(),
},
);
}
pub fn record_commit(&mut self, applied: &[(u32, LayerRef<'_>)]) {
self.record_commit_stacked(applied, &[]);
}
pub fn record_commit_stacked(&mut self, applied: &[(u32, LayerRef<'_>)], zpos: &[(u32, u64)]) {
self.last_committed.clear();
for (plane_id, entry) in applied {
let mut properties = entry.layer.snapshot();
if let Some((_, written)) = zpos.iter().find(|(id, _)| id == plane_id) {
properties = properties.with(PropTag::Zpos, *written);
}
self.last_committed.insert(
*plane_id,
LastCommitted {
layer: Some(entry.id),
properties,
hash: entry.layer.property_hash(),
},
);
}
}
#[must_use]
pub fn committed_baseline(
&self,
plane_id: u32,
layer: LayerId,
) -> Option<&crate::PropertySnapshot> {
if self.force_full_writes {
return None;
}
self.last_committed
.get(&plane_id)
.filter(|baseline| baseline.layer == Some(layer))
.map(|baseline| &baseline.properties)
}
pub fn set_reserved_planes(&mut self, planes: &[u32]) {
self.reserved.clear();
self.reserved.extend_from_slice(planes);
}
#[must_use]
pub fn plane_is_off(&self, plane_id: u32) -> bool {
self.last_committed.get(&plane_id).is_none_or(|baseline| {
baseline
.properties
.get(PropTag::FbId)
.is_none_or(|fb| fb == 0)
})
}
#[must_use]
pub fn lit_planes(&self) -> Vec<u32> {
let mut planes: Vec<u32> = self
.last_committed
.keys()
.copied()
.filter(|plane_id| !self.plane_is_off(*plane_id))
.collect();
planes.sort_unstable();
planes
}
pub const fn set_force_full_property_writes(&mut self, force: bool) {
self.force_full_writes = force;
}
#[must_use]
pub const fn force_full_property_writes(&self) -> bool {
self.force_full_writes
}
pub fn forget_layer(&mut self, layer: LayerId) {
while let Some(plane_id) = self.previous.get_plane_of(layer) {
self.previous.remove(plane_id);
}
for baseline in self.last_committed.values_mut() {
if baseline.layer == Some(layer) {
baseline.layer = None;
}
}
if self.previous.is_empty() {
self.previous_valid = false;
}
}
pub fn forget_plane(&mut self, plane_id: u32) {
self.last_committed.remove(&plane_id);
}
#[must_use]
pub const fn previous_allocation(&self) -> &PlaneAssignment {
&self.previous
}
#[must_use]
pub fn is_fb_only_frame(&self, present: &[LayerRef<'_>]) -> bool {
if self.previous.is_empty() {
return false;
}
self.previous.entries().iter().all(|(plane_id, layer_id)| {
let Some(current) = present.iter().find(|entry| entry.id == *layer_id) else {
return false; };
self.last_committed.get(plane_id).is_some_and(|baseline| {
baseline.layer == Some(*layer_id) && baseline.hash == current.layer.property_hash()
})
})
}
pub fn allocate<C: TestCommitter>(
&mut self,
layers: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
committer: &mut C,
) -> Result<Allocation, TestFailure> {
self.canvas_slot = None;
let allocation = self.allocate_planes(layers, registry, crtc_index, committer)?;
if !allocation.composited.is_empty() && !stacks_by_plane_id(registry, crtc_index) {
let bounds = canvas_bounds(&allocation.assignment, layers);
self.canvas_slot = bounds.above.filter(|_| bounds.feasible());
}
Ok(allocation)
}
#[must_use]
pub const fn canvas_zpos(&self) -> Option<u64> {
self.canvas_slot
}
fn allocate_planes<C: TestCommitter>(
&mut self,
layers: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
committer: &mut C,
) -> Result<Allocation, TestFailure> {
self.stage_limit = registry.blend_stage_limit(crtc_index);
self.test_commits_this_frame = 0;
self.budget_exhausted_this_frame = false;
self.canvas_plane = None;
let by_plane_id = stacks_by_plane_id(registry, crtc_index);
let placeable: Vec<LayerRef<'_>> = layers
.iter()
.filter(|entry| {
!entry.layer.is_externally_bound()
&& !entry.layer.is_pinned()
&& !entry.layer.is_transient_composited()
})
.copied()
.collect();
let mut placeable = placeable;
placeable.sort_by_key(|entry| entry.layer.property(PropTag::Zpos).unwrap_or(0));
let stacked = if by_plane_id {
Self::plane_stack(layers)
} else {
Vec::new()
};
let order_holds = !by_plane_id || self.plane_order_holds(&stacked);
let order_holds =
order_holds && (by_plane_id || canvas_bounds(&self.previous, layers).feasible());
let has_new_layer =
self.previous_valid && self.has_new_layer(&placeable, layers, registry, crtc_index);
let canvas_tested =
self.canvas_hold.is_none() || self.canvas_hold == self.previous_canvas_plane;
let canvas = self.canvas_hold.or(self.previous_canvas_plane);
if self.previous_valid
&& !has_new_layer
&& order_holds
&& canvas_tested
&& self.is_fb_only_frame(layers)
{
let assignment = self.previous.clone();
self.canvas_plane = self.previous_canvas_plane;
let composited = Self::to_composite(layers, &assignment);
return Ok(Allocation {
assignment,
composited,
diagnostics: Diagnostics {
test_commits_issued: 0,
fb_delta_fast_path: true,
budget_exhausted: false,
},
});
}
if self.previous_valid
&& !has_new_layer
&& order_holds
&& !self.previous.is_empty()
&& self.previous_still_applies(&placeable)
&& {
let planes: Vec<&PlaneCapabilities> = registry.for_crtc(crtc_index).collect();
stacking_consistent_all(&planes, &self.previous, &placeable)
&& multirect_complete(&planes, &self.previous)
}
&& let Some(allocation) = self.warm_start(&placeable, layers, canvas, committer)?
{
return Ok(allocation);
}
let assignment = if by_plane_id {
self.place_in_plane_order(&stacked, layers, registry, crtc_index, committer)?
} else {
self.canvas_plane = self.canvas_hold;
self.arm_canvas(self.canvas_hold, committer);
let searched = self
.full_search(&placeable, registry, crtc_index, committer)
.and_then(|searched| {
self.keep_canvas_bounds(
searched, &placeable, layers, registry, crtc_index, committer,
)
});
self.arm_canvas(None, committer);
searched?
};
let composited = Self::to_composite(layers, &assignment);
if composited.is_empty() {
self.canvas_plane = None;
}
self.previous = assignment.clone();
self.previous_valid = !assignment.is_empty();
self.previous_canvas_plane = self.canvas_plane;
self.previous_composited = Self::unplaced(&placeable, &assignment);
self.previous_free = self.free_planes(&self.previous, layers, registry, crtc_index);
Ok(Allocation {
assignment,
composited,
diagnostics: Diagnostics {
test_commits_issued: self.test_commits_this_frame,
fb_delta_fast_path: false,
budget_exhausted: self.budget_exhausted_this_frame,
},
})
}
fn warm_start<C: TestCommitter>(
&mut self,
placeable: &[LayerRef<'_>],
layers: &[LayerRef<'_>],
canvas: Option<u32>,
committer: &mut C,
) -> Result<Option<Allocation>, TestFailure> {
let pairs = Self::pairs_for(&self.previous, placeable);
if pairs.len() != self.previous.len()
|| self.stage_limit.is_some_and(|limit| {
pairs.len() + usize::from(canvas.is_some() && self.canvas_layer.is_some()) > limit
})
{
return Ok(None);
}
self.test_commits_this_frame += 1;
self.arm_canvas(canvas, committer);
let verdict = committer.test_assignment(&pairs);
self.arm_canvas(None, committer);
match verdict {
Ok(()) => {
let assignment = self.previous.clone();
let composited = Self::to_composite(layers, &assignment);
self.canvas_plane = canvas.filter(|_| !composited.is_empty());
self.previous_canvas_plane = self.canvas_plane;
Ok(Some(Allocation {
assignment,
composited,
diagnostics: Diagnostics {
test_commits_issued: self.test_commits_this_frame,
fb_delta_fast_path: false,
budget_exhausted: false,
},
}))
}
Err(TestFailure::NotMaster) => Err(TestFailure::NotMaster),
Err(TestFailure::Rejected) => {
self.previous_valid = false;
Ok(None)
}
}
}
fn keep_canvas_bounds<C: TestCommitter>(
&mut self,
searched: PlaneAssignment,
placeable: &[LayerRef<'_>],
layers: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
committer: &mut C,
) -> Result<PlaneAssignment, TestFailure> {
if canvas_bounds(&searched, layers).feasible() {
return Ok(searched);
}
if let Some(run) =
self.place_around_run(searched.len(), layers, registry, crtc_index, committer)?
{
return Ok(run);
}
let planes: Vec<&PlaneCapabilities> = registry.for_crtc(crtc_index).collect();
let mut resolved = searched.clone();
if resolve_canvas_bounds(&mut resolved, layers, &planes)
&& (resolved.is_empty() || self.try_test(&resolved, placeable, committer)?)
{
return Ok(resolved);
}
Ok(searched)
}
fn place_around_run<C: TestCommitter>(
&mut self,
placed: usize,
layers: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
committer: &mut C,
) -> Result<Option<PlaneAssignment>, TestFailure> {
let mut candidates: Vec<LayerRef<'_>> = layers
.iter()
.filter(|entry| {
!entry.layer.is_composition_layer()
&& !entry.layer.is_externally_bound()
&& !entry.layer.is_pinned()
})
.copied()
.collect();
candidates.sort_by_key(|entry| entry.layer.property(PropTag::Zpos).unwrap_or(0));
let n = candidates.len();
let forced: Vec<usize> = (0..n)
.filter(|&i| forced_composited(candidates[i].layer))
.collect();
let available: Vec<&PlaneCapabilities> = registry
.for_crtc(crtc_index)
.filter(|plane| !self.reserved.contains(&plane.id))
.collect();
for width in (n - placed.min(n)).max(1)..n {
if self.test_commits_this_frame >= self.max_test_commits {
self.budget_exhausted_this_frame = true;
break;
}
let mut best: Option<(usize, i64)> = None;
for start in 0..=n - width {
let covers_forced = forced.iter().all(|&i| (start..start + width).contains(&i));
if !covers_forced {
continue;
}
let cost: i64 = candidates[start..start + width]
.iter()
.map(|entry| i64::from(keep_priority(entry.layer)))
.sum();
if best.is_none_or(|(_, best_cost)| cost <= best_cost) {
best = Some((start, cost));
}
}
let Some((start, _)) = best else {
continue;
};
let outside: Vec<LayerRef<'_>> = candidates
.iter()
.enumerate()
.filter(|(i, _)| !(start..start + width).contains(i))
.map(|(_, entry)| *entry)
.collect();
let assignment = self.place_group(&outside, &available, crtc_index, committer)?;
if !assignment.is_empty() && canvas_bounds(&assignment, layers).feasible() {
return Ok(Some(assignment));
}
}
Ok(None)
}
fn has_new_layer(
&self,
placeable: &[LayerRef<'_>],
layers: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
) -> bool {
let mut unplaced = 0;
for entry in placeable {
if self.previous.get_plane_of(entry.id).is_some() {
continue;
}
if !self.previous_composited.contains(&entry.id) {
return true;
}
unplaced += 1;
}
if unplaced == 0 {
return false;
}
unplaced < self.previous_composited.len()
|| self
.free_planes(&self.previous, layers, registry, crtc_index)
.iter()
.any(|plane| !self.previous_free.contains(plane))
}
fn free_planes(
&self,
assignment: &PlaneAssignment,
layers: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
) -> Vec<u32> {
let pinned: Vec<u32> = layers
.iter()
.filter(|entry| entry.layer.is_pinned())
.filter_map(|entry| entry.layer.assigned_plane())
.collect();
registry
.for_crtc(crtc_index)
.filter(|plane| {
plane.plane_type != PlaneType::Cursor
&& !self.reserved.contains(&plane.id)
&& !pinned.contains(&plane.id)
&& assignment.get(plane.id).is_none()
&& self.previous_canvas_plane != Some(plane.id)
})
.map(|plane| plane.id)
.collect()
}
fn previous_still_applies(&self, present: &[LayerRef<'_>]) -> bool {
self.previous
.entries()
.iter()
.all(|(_, id)| present.iter().any(|entry| entry.id == *id))
}
fn pairs_for<'a>(
assignment: &PlaneAssignment,
present: &[LayerRef<'a>],
) -> Vec<(u32, LayerRef<'a>)> {
assignment
.entries()
.iter()
.filter_map(|(plane_id, layer_id)| {
present
.iter()
.find(|entry| entry.id == *layer_id)
.map(|entry| (*plane_id, *entry))
})
.collect()
}
fn to_composite(layers: &[LayerRef<'_>], assignment: &PlaneAssignment) -> Vec<LayerId> {
let mut unplaced: Vec<&LayerRef<'_>> = layers
.iter()
.filter(|entry| {
!entry.layer.is_composition_layer()
&& !entry.layer.is_externally_bound()
&& !entry.layer.is_pinned()
&& !assignment.entries().iter().any(|(_, id)| *id == entry.id)
})
.collect();
unplaced.sort_by_key(|entry| entry.layer.property(PropTag::Zpos).unwrap_or(0));
unplaced.into_iter().map(|entry| entry.id).collect()
}
fn unplaced(placeable: &[LayerRef<'_>], assignment: &PlaneAssignment) -> Vec<LayerId> {
placeable
.iter()
.filter(|entry| !assignment.entries().iter().any(|(_, id)| *id == entry.id))
.map(|entry| entry.id)
.collect()
}
fn plane_stack<'a>(layers: &[LayerRef<'a>]) -> Vec<LayerRef<'a>> {
let mut stacked: Vec<LayerRef<'a>> = layers
.iter()
.filter(|entry| !entry.layer.is_externally_bound() && !entry.layer.is_pinned())
.copied()
.collect();
stacked.sort_by_key(|entry| {
(
entry.layer.property(PropTag::Zpos).unwrap_or(0),
keep_priority(entry.layer),
)
});
stacked
}
fn plane_order_holds(&self, stacked: &[LayerRef<'_>]) -> bool {
let with_canvas = !self.canvas_hosts.is_empty();
let positions: Vec<Position> = stacked
.iter()
.filter_map(|entry| {
let plane = self.previous.get_plane_of(entry.id);
if plane.is_none() && !with_canvas {
return None;
}
Some(Position {
zpos: entry.layer.property(PropTag::Zpos).unwrap_or(0),
plane: plane.or(self.previous_canvas_plane),
on_canvas: plane.is_none(),
})
})
.collect();
plane_order_consistent(&positions)
}
fn place_in_plane_order<C: TestCommitter>(
&mut self,
stacked: &[LayerRef<'_>],
layers: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
committer: &mut C,
) -> Result<PlaneAssignment, TestFailure> {
let pinned: Vec<u32> = layers
.iter()
.filter(|entry| entry.layer.is_pinned())
.filter_map(|entry| entry.layer.assigned_plane())
.collect();
let mut planes: Vec<&PlaneCapabilities> = registry
.for_crtc(crtc_index)
.filter(|plane| {
plane.plane_type != PlaneType::Cursor
&& plane.multirect_parent.is_none()
&& !self.reserved.contains(&plane.id)
&& !pinned.contains(&plane.id)
})
.collect();
planes.sort_by_key(|plane| plane.id);
if stacked.is_empty() {
return Ok(PlaneAssignment::new());
}
let order = PlaneOrder::new(stacked.len(), planes.len(), |i, j| {
let (plane, layer) = (planes[j], stacked[i].layer);
plane_statically_compatible(plane, layer, crtc_index)
&& !self.probe_rejected(crtc_index, plane.id, layer)
&& self.failure_cache.lookup(plane.id, layer.property_hash()) != Some(false)
});
let forced = stacked
.iter()
.position(|entry| forced_composited(entry.layer))
.zip(
stacked
.iter()
.rposition(|entry| forced_composited(entry.layer)),
)
.map(|(first, last)| first..last + 1);
let host_ids = self.canvas_hosts.clone();
let hosts = |j: usize| host_ids.contains(&planes[j].id);
let hosts_canvas: Option<&dyn Fn(usize) -> bool> = (!host_ids.is_empty()).then_some(&hosts);
let cost = |i: usize| i64::from(keep_priority(stacked[i].layer));
let mut max_placed = stacked.len();
loop {
let Some(choice) = order.choose(max_placed, forced.as_ref(), hosts_canvas, cost) else {
return Ok(PlaneAssignment::new());
};
let mut assignment = PlaneAssignment::new();
for (i, entry) in stacked.iter().enumerate() {
if let Some(j) = order.plane_of(i, &choice) {
assignment.insert(planes[j].id, entry.id);
}
}
self.canvas_plane = choice.canvas.map(|j| planes[j].id);
if assignment.is_empty() {
return Ok(assignment);
}
self.arm_canvas(self.canvas_plane, committer);
let passed = self.try_test(&assignment, stacked, committer);
self.arm_canvas(None, committer);
if passed? {
return Ok(assignment);
}
if self.test_commits_this_frame >= self.max_test_commits {
self.budget_exhausted_this_frame = true;
self.canvas_plane = order
.choose(0, forced.as_ref(), hosts_canvas, cost)
.and_then(|all| all.canvas)
.map(|j| planes[j].id);
return Ok(PlaneAssignment::new());
}
max_placed = assignment.len() - 1;
}
}
fn arm_canvas<C: TestCommitter>(&mut self, plane: Option<u32>, committer: &mut C) {
if let (Some(plane_id), Some(layer)) = (plane, &self.canvas_layer) {
committer.set_extra_plane(plane_id, layer.clone());
self.canvas_armed = Some(plane_id);
} else {
committer.clear_extra_plane();
self.canvas_armed = None;
}
}
fn over_stage_limit(&self, planes: usize) -> bool {
self.stage_limit
.is_some_and(|limit| planes + usize::from(self.canvas_armed.is_some()) > limit)
}
fn full_search<C: TestCommitter>(
&mut self,
placeable: &[LayerRef<'_>],
registry: &PlaneRegistry,
crtc_index: u32,
committer: &mut C,
) -> Result<PlaneAssignment, TestFailure> {
let mut available: Vec<&PlaneCapabilities> = registry
.for_crtc(crtc_index)
.filter(|plane| !self.reserved.contains(&plane.id))
.collect();
let layer_refs: Vec<&Layer> = placeable.iter().map(|entry| entry.layer).collect();
let groups = split_independent_groups(&layer_refs);
let mut result = PlaneAssignment::new();
for group in groups {
let members: Vec<LayerRef<'_>> = group.iter().map(|&i| placeable[i]).collect();
let placed = self.place_group(&members, &available, crtc_index, committer)?;
for (plane_id, layer_id) in placed.entries() {
result.insert(*plane_id, *layer_id);
}
available.retain(|plane| placed.get(plane.id).is_none());
}
Ok(result)
}
fn place_group<C: TestCommitter>(
&mut self,
members: &[LayerRef<'_>],
planes: &[&PlaneCapabilities],
crtc_index: u32,
committer: &mut C,
) -> Result<PlaneAssignment, TestFailure> {
let mut ordered: Vec<LayerRef<'_>> = members.to_vec();
ordered.sort_by(|a, b| keep_priority(b.layer).cmp(&keep_priority(a.layer)));
let mut assignment = self.preseed(&ordered, planes, crtc_index);
if assignment.is_empty() {
return Ok(assignment);
}
if stacking_consistent_all(planes, &assignment, &ordered)
&& multirect_complete(planes, &assignment)
&& self.try_test(&assignment, &ordered, committer)?
{
return Ok(assignment);
}
assignment.clear();
let mut candidates = self.rank_candidates(&ordered, planes, crtc_index);
candidates.sort_by(|a, b| b.2.cmp(&a.2));
let (ordinary, virtual_planes): (Vec<_>, Vec<_>) =
candidates.into_iter().partition(|(plane_id, _, _)| {
caps_in(planes, *plane_id).is_none_or(|plane| plane.multirect_parent.is_none())
});
let mut used_planes: Vec<u32> = Vec::new();
let mut used_layers: Vec<LayerId> = Vec::new();
for (plane_id, layer, _) in ordinary.into_iter().chain(virtual_planes) {
if used_planes.contains(&plane_id) || used_layers.contains(&layer.id) {
continue;
}
if self
.failure_cache
.lookup(plane_id, layer.layer.property_hash())
== Some(false)
{
continue;
}
if self.probe_rejected(crtc_index, plane_id, layer.layer) {
continue;
}
if !fits_stacking(planes, &assignment, &ordered, plane_id, layer.layer)
|| !fits_multirect(planes, &assignment, plane_id)
{
continue;
}
assignment.insert(plane_id, layer.id);
used_planes.push(plane_id);
used_layers.push(layer.id);
}
if assignment.is_empty() {
return Ok(assignment);
}
if self.try_test(&assignment, &ordered, committer)? {
return Ok(assignment);
}
let mut by_priority: Vec<(u32, LayerId)> = assignment.entries().to_vec();
by_priority.sort_by_key(|(_, layer_id)| {
ordered
.iter()
.find(|entry| entry.id == *layer_id)
.map_or(i32::MAX, |entry| keep_priority(entry.layer))
});
for (plane_id, _) in by_priority {
if assignment.remove(plane_id).is_none() {
continue; }
let children: Vec<u32> = assignment
.entries()
.iter()
.map(|(id, _)| *id)
.filter(|id| {
caps_in(planes, *id).is_some_and(|c| c.multirect_parent == Some(plane_id))
})
.collect();
for child in children {
assignment.remove(child);
}
if assignment.is_empty() {
break;
}
if self.try_test(&assignment, &ordered, committer)? {
break;
}
if self.test_commits_this_frame >= self.max_test_commits {
self.budget_exhausted_this_frame = true;
break;
}
}
Ok(assignment)
}
fn try_test<C: TestCommitter>(
&mut self,
assignment: &PlaneAssignment,
present: &[LayerRef<'_>],
committer: &mut C,
) -> Result<bool, TestFailure> {
if self.test_commits_this_frame >= self.max_test_commits {
self.budget_exhausted_this_frame = true;
return Ok(false);
}
let pairs = Self::pairs_for(assignment, present);
if pairs.len() != assignment.len() || self.over_stage_limit(pairs.len()) {
return Ok(false);
}
self.test_commits_this_frame += 1;
match committer.test_assignment(&pairs) {
Ok(()) => {
for (plane_id, layer) in &pairs {
self.failure_cache
.record(*plane_id, layer.layer.property_hash(), true);
}
Ok(true)
}
Err(TestFailure::NotMaster) => Err(TestFailure::NotMaster),
Err(TestFailure::Rejected) => {
for (plane_id, layer) in &pairs {
self.failure_cache
.record(*plane_id, layer.layer.property_hash(), false);
}
Ok(false)
}
}
}
fn preseed(
&mut self,
ordered: &[LayerRef<'_>],
planes: &[&PlaneCapabilities],
crtc_index: u32,
) -> PlaneAssignment {
self.matcher.reset(ordered.len(), planes.len());
for (left, entry) in ordered.iter().enumerate() {
for (right, plane) in planes.iter().enumerate() {
if !plane_statically_compatible(plane, entry.layer, crtc_index) {
continue;
}
if self
.failure_cache
.lookup(plane.id, entry.layer.property_hash())
== Some(false)
{
continue;
}
if self.probe_rejected(crtc_index, plane.id, entry.layer) {
continue;
}
let score = self.score(plane, entry);
self.matcher.add_scored_edge(left, right, score);
}
}
self.matcher.solve();
let mut assignment = PlaneAssignment::new();
for (left, entry) in ordered.iter().enumerate() {
if let Some(right) = self.matcher.match_for_left(left) {
assignment.insert(planes[right].id, entry.id);
}
}
assignment
}
fn rank_candidates<'a>(
&self,
ordered: &[LayerRef<'a>],
planes: &[&PlaneCapabilities],
crtc_index: u32,
) -> Vec<(u32, LayerRef<'a>, i32)> {
let mut candidates = Vec::new();
for entry in ordered {
for plane in planes {
if plane_statically_compatible(plane, entry.layer, crtc_index) {
candidates.push((plane.id, *entry, self.score(plane, entry)));
}
}
}
candidates
}
fn score(&self, plane: &PlaneCapabilities, entry: &LayerRef<'_>) -> i32 {
let held_last_frame = self.previous_valid && self.previous.get(plane.id) == Some(entry.id);
score_pair(
plane,
entry.layer,
ScoreContext {
held_last_frame,
failure_hits: self
.failure_cache
.failure_count(plane.id, entry.layer.property_hash()),
},
)
}
fn probe_rejected(&self, crtc_index: u32, plane_id: u32, layer: &Layer) -> bool {
let Some(fourcc) = layer.format() else {
return false;
};
self.probe_cache
.lookup(crtc_index, plane_id, fourcc, Modifier(layer.modifier()))
== Verdict::Rejected
}
pub fn record_probe_rejection(&mut self, crtc_index: u32, plane_id: u32, layer: &Layer) {
if let Some(fourcc) = layer.format() {
self.probe_cache.record(
crtc_index,
plane_id,
fourcc,
Modifier(layer.modifier()),
false,
);
}
}
}
const fn forced_composited(layer: &Layer) -> bool {
layer.is_force_composited() || layer.is_transient_composited()
}
fn fits_stacking(
planes: &[&PlaneCapabilities],
assignment: &PlaneAssignment,
present: &[LayerRef<'_>],
plane_id: u32,
layer: &Layer,
) -> bool {
let caps = |id: u32| planes.iter().find(|plane| plane.id == id).copied();
let Some(plane) = caps(plane_id) else {
return true;
};
let zpos = layer.property(PropTag::Zpos);
assignment.entries().iter().all(|(other_plane, other_id)| {
if *other_plane == plane_id {
return true;
}
let (Some(other), Some(entry)) = (
caps(*other_plane),
present.iter().find(|entry| entry.id == *other_id),
) else {
return true;
};
crate::stacking_consistent(plane, zpos, other, entry.layer.property(PropTag::Zpos))
})
}
fn caps_in<'a>(planes: &[&'a PlaneCapabilities], plane_id: u32) -> Option<&'a PlaneCapabilities> {
planes.iter().find(|plane| plane.id == plane_id).copied()
}
fn fits_multirect(
planes: &[&PlaneCapabilities],
assignment: &PlaneAssignment,
plane_id: u32,
) -> bool {
caps_in(planes, plane_id).is_none_or(|plane| {
crate::multirect_pairing_ok(plane.multirect_parent, |parent| {
assignment.get(parent).is_some()
})
})
}
fn multirect_complete(planes: &[&PlaneCapabilities], assignment: &PlaneAssignment) -> bool {
assignment
.entries()
.iter()
.all(|(plane_id, _)| fits_multirect(planes, assignment, *plane_id))
}
fn stacking_consistent_all(
planes: &[&PlaneCapabilities],
assignment: &PlaneAssignment,
present: &[LayerRef<'_>],
) -> bool {
assignment.entries().iter().all(|(plane_id, layer_id)| {
present
.iter()
.find(|entry| entry.id == *layer_id)
.is_none_or(|entry| fits_stacking(planes, assignment, present, *plane_id, entry.layer))
})
}
#[derive(Debug, Clone, Copy, Default)]
struct CanvasBounds {
below: Option<u64>,
above: Option<u64>,
}
impl CanvasBounds {
fn feasible(self) -> bool {
match (self.below, self.above) {
(Some(below), Some(above)) => below < above,
_ => true,
}
}
}
fn placed_and_composited<'a>(
assignment: &PlaneAssignment,
layers: &[LayerRef<'a>],
) -> (Vec<&'a Layer>, Vec<&'a Layer>) {
let mut placed = Vec::new();
let mut composited = Vec::new();
for entry in layers {
let layer = entry.layer;
if layer.is_composition_layer() || layer.is_externally_bound() {
continue;
}
if assignment.get_plane_of(entry.id).is_some() {
placed.push(layer);
} else if layer.is_pinned() {
if layer
.assigned_plane()
.is_some_and(|plane_id| assignment.get(plane_id).is_none())
{
placed.push(layer);
}
} else {
composited.push(layer);
}
}
(placed, composited)
}
fn canvas_bounds(assignment: &PlaneAssignment, layers: &[LayerRef<'_>]) -> CanvasBounds {
let (placed, composited) = placed_and_composited(assignment, layers);
let mut bounds = CanvasBounds::default();
for placed in &placed {
let Some(zp) = placed.property(PropTag::Zpos) else {
continue;
};
for composited in &composited {
let Some(zc) = composited.property(PropTag::Zpos) else {
continue;
};
if zc == zp || !layers_intersect(placed, composited) {
continue;
}
if zc < zp {
bounds.above = Some(bounds.above.map_or(zp, |above| above.min(zp)));
} else {
bounds.below = Some(bounds.below.map_or(zp, |below| below.max(zp)));
}
}
}
bounds
}
fn resolve_canvas_bounds(
assignment: &mut PlaneAssignment,
layers: &[LayerRef<'_>],
planes: &[&PlaneCapabilities],
) -> bool {
loop {
let bounds = canvas_bounds(assignment, layers);
let (Some(above), Some(below)) = (bounds.above, bounds.below) else {
return true;
};
if below < above {
return true;
}
let (_, composited) = placed_and_composited(assignment, layers);
let mut victim: Option<(u32, i32)> = None;
for (plane_id, layer_id) in assignment.entries() {
let Some(entry) = layers.iter().find(|entry| entry.id == *layer_id) else {
continue;
};
let Some(z) = entry.layer.property(PropTag::Zpos) else {
continue;
};
let caught = z >= above
&& z <= below
&& composited.iter().any(|c| {
c.property(PropTag::Zpos)
.is_some_and(|zc| zc != z && layers_intersect(entry.layer, c))
});
if !caught {
continue;
}
let keep = keep_priority(entry.layer);
if victim.is_none_or(|(_, victim_keep)| keep < victim_keep) {
victim = Some((*plane_id, keep));
}
}
let Some((victim, _)) = victim else {
return false;
};
assignment.remove(victim);
let children: Vec<u32> = assignment
.entries()
.iter()
.map(|(id, _)| *id)
.filter(|id| caps_in(planes, *id).is_some_and(|c| c.multirect_parent == Some(victim)))
.collect();
for child in children {
assignment.remove(child);
}
}
}