linux_drm/modeset/
atomic.rs

1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::vec::Vec;
4use core::iter;
5use core::ops::BitOr;
6
7use super::{IntoRawPropertyValue, ObjectId, PropertyId};
8
9/// An atomic modesetting commit request.
10#[derive(Debug)]
11pub struct AtomicRequest {
12    objs: BTreeMap<u32, AtomicRequestObj>,
13    total_props: u32,
14
15    // When a property is set to something that needs to be dropped
16    // after use, we remember it here so that its drop can be deferred
17    // until the atomic request is dropped or reset.
18    drops: Vec<Box<dyn core::any::Any>>,
19}
20
21#[derive(Debug)]
22struct AtomicRequestObj {
23    prop_ids: Vec<u32>,
24    prop_values: Vec<u64>,
25}
26
27impl AtomicRequest {
28    pub fn new() -> Self {
29        Self {
30            objs: BTreeMap::new(),
31            total_props: 0,
32            drops: Vec::new(),
33        }
34    }
35
36    /// Clear any previously-set properties, returning the object to empty.
37    ///
38    /// This function does, however, retain any allocated capacity for
39    /// property-set requests for existing objects, so callers can avoid
40    /// making lots of dynamic memory allocations on every change by
41    /// retaining and reusing a single request object. This optimization
42    /// will only be productive if the object is reused to set a similar
43    /// set of properties on a similar set of objects.
44    pub fn reset(&mut self) {
45        // The idea here is to retain all of our existing BTreeMap elements,
46        // but to reduce the length of the nested vectors to zero. This
47        // therefore means that subsequent uses of the request with similar
48        // object ids and a similar number of properties per object can
49        // avoid the need for lots of reallocation.
50        for (_, v) in self.objs.iter_mut() {
51            v.prop_ids.truncate(0);
52            v.prop_values.truncate(0);
53        }
54        self.total_props = 0;
55        self.drops.truncate(0);
56    }
57
58    pub fn set_property(
59        &mut self,
60        obj_id: impl Into<ObjectId>,
61        prop_id: PropertyId,
62        value: impl IntoRawPropertyValue,
63    ) {
64        fn set(req: &mut AtomicRequest, obj_id: ObjectId, prop_id: PropertyId, value: u64) {
65            let (_, obj_id) = obj_id.as_raw_type_and_id();
66            let obj = req.objs.entry(obj_id).or_insert_with(|| AtomicRequestObj {
67                prop_ids: Vec::new(),
68                prop_values: Vec::new(),
69            });
70
71            // We'll reserve first to make sure that running out of memory can't
72            // cause these two vecs to end up with different lengths when we're done.
73            obj.prop_ids.reserve(1);
74            obj.prop_values.reserve(1);
75
76            obj.prop_ids.push(prop_id.0);
77            obj.prop_values.push(value);
78            req.total_props += 1; // panics if request contains more than u32::MAX total properties
79            if req.objs.len() > (u32::MAX as usize) {
80                panic!("too many distinct objects in request");
81            }
82        }
83        let (raw_v, drop) = value.into_raw_property_value();
84        if let Some(drop) = drop {
85            self.drops.push(drop);
86        }
87        set(self, obj_id.into(), prop_id, raw_v);
88    }
89
90    pub(crate) fn for_ioctl_req(&self) -> AtomicRequestRawParts {
91        let obj_count = self.objs.len();
92        let mut obj_ids = Vec::<u32>::with_capacity(obj_count);
93        let mut obj_prop_counts = Vec::<u32>::with_capacity(obj_count);
94        let total_prop_count = self.total_props as usize;
95        let mut prop_ids = Vec::<u32>::with_capacity(total_prop_count);
96        let mut prop_values = Vec::<u64>::with_capacity(total_prop_count);
97
98        for (obj_id, obj) in self.objs.iter() {
99            obj_ids.push(*obj_id);
100            obj_prop_counts.push(obj.prop_ids.len() as u32);
101
102            for (prop_id, value) in iter::zip(
103                obj.prop_ids.iter().copied(),
104                obj.prop_values.iter().copied(),
105            ) {
106                prop_ids.push(prop_id);
107                prop_values.push(value);
108            }
109        }
110
111        AtomicRequestRawParts {
112            obj_ids,
113            obj_prop_counts,
114            prop_ids,
115            prop_values,
116        }
117    }
118}
119
120pub(crate) struct AtomicRequestRawParts {
121    pub(crate) obj_ids: Vec<u32>,
122    pub(crate) obj_prop_counts: Vec<u32>,
123    pub(crate) prop_ids: Vec<u32>,
124    pub(crate) prop_values: Vec<u64>,
125}
126
127pub struct AtomicCommitFlags(pub(crate) u32);
128
129impl AtomicCommitFlags {
130    pub const NONE: Self = Self(0);
131    pub const TEST_ONLY: Self = Self(crate::ioctl::DRM_MODE_ATOMIC_TEST_ONLY);
132    pub const NONBLOCK: Self = Self(crate::ioctl::DRM_MODE_ATOMIC_NONBLOCK);
133    pub const ALLOW_MODESET: Self = Self(crate::ioctl::DRM_MODE_ATOMIC_ALLOW_MODESET);
134    pub const PAGE_FLIP_EVENT: Self = Self(crate::ioctl::DRM_MODE_PAGE_FLIP_EVENT);
135    pub const ASYNC: Self = Self(crate::ioctl::DRM_MODE_PAGE_FLIP_ASYNC);
136}
137
138impl BitOr for AtomicCommitFlags {
139    type Output = Self;
140
141    fn bitor(self, rhs: Self) -> Self {
142        Self(self.0 | rhs.0)
143    }
144}