Skip to main content

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}