drmkit_planes/lib.rs
1// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
2// SPDX-License-Identifier: MIT
3
4//! Display-plane capabilities, the layer model, and the plane allocator.
5//!
6//! Port of `src/planes/` from drm-cxx @ `dc2915b`.
7//!
8//! Three pieces, bottom-up:
9//!
10//! - [`PlaneCapabilities`] and [`PlaneRegistry`] — what each plane on a device
11//! can do: which CRTCs it feeds, which formats and modifiers it scans out,
12//! whether it rotates, where its zpos may sit. Built from a device behind
13//! the `device` feature, or from fixtures a caller supplies, which is what
14//! lets the allocator be tested against hardware nobody has.
15//! - [`Layer`] — one thing to put on screen, as a bag of KMS properties plus
16//! the hints that inform placement.
17//! - [`Allocator`] — which layer goes on which plane.
18//!
19//! # Why placement is a search
20//!
21//! Nothing in the kernel's uAPI answers "can these layers be scanned out
22//! together". `IN_FORMATS` prunes, `possible_crtcs` prunes, and neither
23//! settles it: bandwidth limits, scaler counts and per-driver rules decide the
24//! rest and are not advertised anywhere. The only oracle is an atomic
25//! `TEST_ONLY` commit, which costs an ioctl.
26//!
27//! So the allocator proposes and the kernel disposes, and the whole design is
28//! about proposing well enough that few proposals are needed. It seeds with a
29//! maximum bipartite matching, orders candidates by a score, backtracks by
30//! dropping the layer it can most afford to composite, and spends at most
31//! [`Allocator::DEFAULT_MAX_TEST_COMMITS`] test commits per search before
32//! settling for what it has. Layers that do not overlap are solved
33//! independently, so a busy corner of the screen does not make the rest of it
34//! expensive.
35//!
36//! Two caches keep the steady state near zero ioctls: a warm start that
37//! re-validates last frame's assignment, and the fast path below.
38//!
39//! # Invariant 4
40//!
41//! [`Layer::property_hash`] isolates *content* from *placement*: a frame that
42//! changes only content on already-placed layers reuses the cached plane
43//! assignment and skips the redundant `TEST_ONLY` commit.
44//!
45//! The C++ can only document which properties are which. Here the split is
46//! [`PropTag::class`], an exhaustive `match`, so a property added without a
47//! classification does not compile — the misclassification the invariant warns
48//! about cannot reach a running system.
49
50#[cfg(feature = "device")]
51mod probe;
52
53mod allocator;
54mod layer;
55mod matching;
56mod multirect;
57mod output;
58mod plane_order;
59mod prop;
60mod registry;
61mod scoring;
62mod zpos_order;
63
64pub use allocator::{
65 Allocation, Allocator, AlwaysAccept, Diagnostics, LayerId, LayerRef, PlaneAssignment,
66 TestCache, TestCommitter, TestFailure,
67};
68pub use layer::{ContentType, Layer, PropertySnapshot, Rect};
69pub use matching::BipartiteMatching;
70pub use multirect::{multirect_pairing_ok, parse_blend_stage_limit, parse_multirect_parent};
71pub use output::Output;
72pub use plane_order::stacks_by_plane_id;
73pub use prop::{NUM_PROPS, PropClass, PropTag};
74pub use registry::{
75 BlendModeValues, ColorEncoding, ColorEncodingValues, ColorPipeline, ColorRange,
76 ColorRangeValues, PlaneCapabilities, PlaneRegistry, PlaneType, rescale_alpha,
77};
78pub use scoring::{
79 ScoreContext, WARM_STABILITY_BONUS, bandwidth_class_bonus, cost_bias, keep_priority,
80 layer_priority, layers_intersect, plane_statically_compatible, rects_intersect, score_pair,
81 split_independent_groups,
82};
83pub use zpos_order::{
84 StackEntry, effective_zpos, stack_zpos, stacked_zpos, stacked_zpos_beneath,
85 stacking_consistent, zpos_fixed,
86};
87
88#[cfg(test)]
89mod tests;