Skip to main content

ferroui_base/rendering/composition/drawing/
compositor_resource_helpers.rs

1use super::ICompositionRenderResource;
2use crate::rendering::composition::server::ServerObjectId;
3use crate::rendering::composition::{Compositor, ICompositorSerializable};
4use std::cell::RefCell;
5use std::rc::{Rc, Weak};
6
7/// A server-side resource shared by reference counting.
8pub struct CompositorRefCountableResource {
9    value: ServerObjectId,
10    ref_count: i32,
11}
12
13impl CompositorRefCountableResource {
14    pub fn new(value: ServerObjectId) -> Self {
15        Self { value, ref_count: 1 }
16    }
17
18    pub fn value(&self) -> ServerObjectId {
19        self.value
20    }
21
22    pub fn ref_count(&self) -> i32 {
23        self.ref_count
24    }
25
26    pub fn add_ref(&mut self) {
27        if self.ref_count <= 0 {
28            panic!("This resource is disposed");
29        }
30        self.ref_count += 1;
31    }
32
33    /// Drops one reference; when it was the last one the server object is
34    /// disposed with the next batch and `true` is returned.
35    pub fn release(&mut self, c: &Compositor) -> bool {
36        if self.ref_count <= 0 {
37            panic!("This resource is disposed");
38        }
39        self.ref_count -= 1;
40        if self.ref_count == 0 {
41            c.dispose_on_next_batch(self.value);
42            return true;
43        }
44        false
45    }
46}
47
48struct Entry {
49    compositor: Weak<Compositor>,
50    resource: CompositorRefCountableResource,
51}
52
53/// Holds the server-side counterparts of a media object, one per compositor
54/// the object is used with.
55#[derive(Default)]
56pub struct CompositorResourceHolder {
57    dictionary: RefCell<Vec<Entry>>,
58}
59
60fn is_compositor(entry: &Entry, compositor: &Compositor) -> bool {
61    std::ptr::eq(entry.compositor.as_ptr(), compositor)
62}
63
64impl CompositorResourceHolder {
65    pub fn new() -> Self {
66        Self::default()
67    }
68
69    /// Whether the object is attached to any compositor.
70    pub fn is_attached(&self) -> bool {
71        !self.dictionary.borrow().is_empty()
72    }
73
74    /// Adds a reference to the counterpart on `compositor`, creating it
75    /// with `factory` if there is none. Returns the counterpart and whether
76    /// it was created; a newly created counterpart's `owner` is registered
77    /// for serialization.
78    pub fn create_or_add_ref(
79        &self,
80        compositor: &Rc<Compositor>,
81        owner: Option<Rc<dyn ICompositorSerializable>>,
82        factory: impl FnOnce(&Rc<Compositor>) -> ServerObjectId,
83    ) -> (ServerObjectId, bool) {
84        {
85            let mut dictionary = self.dictionary.borrow_mut();
86            if let Some(entry) = dictionary.iter_mut().find(|e| is_compositor(e, compositor)) {
87                entry.resource.add_ref();
88                return (entry.resource.value(), false);
89            }
90        }
91        let resource = factory(compositor);
92        self.dictionary
93            .borrow_mut()
94            .push(Entry { compositor: Rc::downgrade(compositor), resource: CompositorRefCountableResource::new(resource) });
95        if let Some(owner) = owner {
96            compositor.register_for_serialization(owner);
97        }
98        (resource, true)
99    }
100
101    pub fn try_get_for_compositor(&self, compositor: &Compositor) -> Option<ServerObjectId> {
102        self.dictionary.borrow().iter().find(|e| is_compositor(e, compositor)).map(|e| e.resource.value())
103    }
104
105    pub fn get_for_compositor(&self, compositor: &Compositor) -> ServerObjectId {
106        match self.try_get_for_compositor(compositor) {
107            Some(value) => value,
108            None => panic!("This resource doesn't exist on that compositor"),
109        }
110    }
111
112    /// Drops one reference to the counterpart on `compositor`. Returns
113    /// whether it was the last one.
114    pub fn release(&self, compositor: &Compositor) -> bool {
115        let mut dictionary = self.dictionary.borrow_mut();
116        let Some(index) = dictionary.iter().position(|e| is_compositor(e, compositor)) else {
117            panic!("This resource doesn't exist on that compositor");
118        };
119        if dictionary[index].resource.release(compositor) {
120            dictionary.remove(index);
121            return true;
122        }
123        false
124    }
125
126    fn compositors(&self) -> Vec<Rc<Compositor>> {
127        self.dictionary.borrow().iter().filter_map(|e| e.compositor.upgrade()).collect()
128    }
129
130    /// Moves the references this object holds on a property value from the
131    /// old value to the new one, on every compositor.
132    pub fn process_property_change_notification(
133        &self,
134        old_value: Option<&dyn ICompositionRenderResource>,
135        new_value: Option<&dyn ICompositionRenderResource>,
136    ) {
137        if let Some(old_resource) = old_value {
138            self.transitive_release_all(old_resource);
139        }
140        if let Some(new_resource) = new_value {
141            self.transitive_add_ref_all(new_resource);
142        }
143    }
144
145    pub fn transitive_release_all(&self, old_resource: &dyn ICompositionRenderResource) {
146        for compositor in self.compositors() {
147            old_resource.release_on_compositor(&compositor);
148        }
149    }
150
151    pub fn transitive_add_ref_all(&self, new_resource: &dyn ICompositionRenderResource) {
152        for compositor in self.compositors() {
153            new_resource.add_ref_on_compositor(&compositor);
154        }
155    }
156
157    pub fn register_for_invalidation_on_all_compositors(&self, serializable: &Rc<dyn ICompositorSerializable>) {
158        for compositor in self.compositors() {
159            compositor.register_for_serialization(serializable.clone());
160        }
161    }
162}