drmkit_planes/output.rs
1// SPDX-FileCopyrightText: (c) 2026 Joel Winarske
2// SPDX-License-Identifier: MIT
3
4//! One CRTC's worth of layers.
5//!
6//! Port of `src/planes/output.{hpp,cpp}`.
7
8use crate::allocator::{LayerId, LayerRef};
9use crate::layer::Layer;
10use crate::prop::PropTag;
11
12/// The layers destined for one CRTC, in presentation order.
13///
14/// # Deviation: the composition layer is owned here
15///
16/// The C++ `Output` takes the composition layer by reference and owns only the
17/// rest, keeping a `std::vector<std::unique_ptr<Layer>>` alongside a
18/// `std::vector<Layer*>` view it can reorder independently.
19///
20/// Here every layer is owned by the `Output` and the composition layer is
21/// identified by [`LayerId`]. Holding a `&mut Layer` in the struct would infect
22/// every caller with a lifetime parameter for no benefit, and owning it removes
23/// the second vector — and with it the C++'s since-removed `rebuild_layer_ptrs`, which
24/// would have silently undone [`sort_layers_by_zpos`](Self::sort_layers_by_zpos)
25/// had anything called it (drm-cxx#237).
26#[derive(Debug, Default)]
27pub struct Output {
28 crtc_id: u32,
29 /// Owned layers in presentation order. Reordered by
30 /// [`sort_layers_by_zpos`](Self::sort_layers_by_zpos).
31 layers: Vec<(LayerId, Layer)>,
32 next_id: u64,
33 composition: Option<LayerId>,
34}
35
36impl Output {
37 /// An output for `crtc_id` with no layers.
38 #[must_use]
39 pub const fn new(crtc_id: u32) -> Self {
40 Self {
41 crtc_id,
42 layers: Vec::new(),
43 next_id: 1,
44 composition: None,
45 }
46 }
47
48 /// The CRTC this output drives.
49 #[must_use]
50 pub const fn crtc_id(&self) -> u32 {
51 self.crtc_id
52 }
53
54 /// Add an empty layer, returning its id.
55 ///
56 /// Ids are never reused for the life of the output, which is what lets the
57 /// allocator key its warm-start state on them safely.
58 pub fn add_layer(&mut self) -> LayerId {
59 let id = LayerId(self.next_id);
60 self.next_id += 1;
61 self.layers.push((id, Layer::new()));
62 id
63 }
64
65 /// Remove a layer. Returns whether it was there.
66 ///
67 /// If it was the composition layer, the output is left with none.
68 pub fn remove_layer(&mut self, id: LayerId) -> bool {
69 let Some(index) = self.layers.iter().position(|(known, _)| *known == id) else {
70 return false;
71 };
72 self.layers.remove(index);
73 if self.composition == Some(id) {
74 self.composition = None;
75 }
76 true
77 }
78
79 /// Borrow a layer.
80 #[must_use]
81 pub fn layer(&self, id: LayerId) -> Option<&Layer> {
82 self.layers
83 .iter()
84 .find(|(known, _)| *known == id)
85 .map(|(_, layer)| layer)
86 }
87
88 /// Borrow a layer mutably.
89 pub fn layer_mut(&mut self, id: LayerId) -> Option<&mut Layer> {
90 self.layers
91 .iter_mut()
92 .find(|(known, _)| *known == id)
93 .map(|(_, layer)| layer)
94 }
95
96 /// Every layer with its id, in presentation order.
97 pub fn layers(&self) -> impl Iterator<Item = LayerRef<'_>> + '_ {
98 self.layers
99 .iter()
100 .map(|(id, layer)| LayerRef { id: *id, layer })
101 }
102
103 /// Every layer, as the slice the allocator takes.
104 #[must_use]
105 pub fn layer_refs(&self) -> Vec<LayerRef<'_>> {
106 self.layers().collect()
107 }
108
109 /// How many layers the output holds.
110 #[must_use]
111 pub fn len(&self) -> usize {
112 self.layers.len()
113 }
114
115 /// Whether the output holds no layers.
116 #[must_use]
117 pub fn is_empty(&self) -> bool {
118 self.layers.is_empty()
119 }
120
121 /// Designate a layer as the composition pass's output.
122 ///
123 /// Clears the flag on any previous composition layer, so exactly one layer
124 /// carries it. Returns whether `id` names a layer of this output.
125 pub fn set_composition_layer(&mut self, id: LayerId) -> bool {
126 if self.layer(id).is_none() {
127 return false;
128 }
129 if let Some(previous) = self.composition
130 && let Some(layer) = self.layer_mut(previous)
131 {
132 layer.set_composition_layer(false);
133 }
134 self.composition = Some(id);
135 if let Some(layer) = self.layer_mut(id) {
136 layer.set_composition_layer(true);
137 }
138 true
139 }
140
141 /// The composition layer's id, if one is designated.
142 #[must_use]
143 pub const fn composition_layer(&self) -> Option<LayerId> {
144 self.composition
145 }
146
147 /// Whether any layer changed since the last
148 /// [`mark_clean`](Self::mark_clean).
149 #[must_use]
150 pub fn any_layer_dirty(&self) -> bool {
151 self.layers.iter().any(|(_, layer)| layer.is_dirty())
152 }
153
154 /// The layers that changed, in presentation order.
155 pub fn changed_layers(&self) -> impl Iterator<Item = LayerRef<'_>> + '_ {
156 self.layers().filter(|entry| entry.layer.is_dirty())
157 }
158
159 /// Clear every layer's dirty flag.
160 pub fn mark_clean(&mut self) {
161 for (_, layer) in &mut self.layers {
162 layer.mark_clean();
163 }
164 }
165
166 /// Reorder layers by ascending `zpos`, treating an unset `zpos` as 0.
167 ///
168 /// Stable, so layers sharing a `zpos` keep the order they were added in.
169 pub fn sort_layers_by_zpos(&mut self) {
170 self.layers
171 .sort_by_key(|(_, layer)| layer.property(PropTag::Zpos).unwrap_or(0));
172 }
173}