Skip to main content

ferroui_base/rendering/composition/server/
server_composition_drawing_surface.rs

1use super::{IServerCompositionSurface, IServerObject, ServerCompositor, ServerCompositionSurfaceChanged};
2use crate::platform::{IBitmapImpl, IPlatformRenderInterfaceContext, PlatformGraphicsContextLostException};
3use crate::rendering::composition::{IServerGpuImportedObject, ServerImportedGpuImage, ServerImportedGpuSemaphore};
4use crate::utilities::{RefCountable, RefCounted};
5use std::any::Any;
6use std::cell::{Cell, RefCell};
7use std::sync::Arc;
8use std::rc::{Rc, Weak};
9
10/// The error of an update that cannot proceed (`InvalidOperationException`).
11#[derive(Clone, Debug)]
12pub struct DrawingSurfaceUpdateError(&'static str);
13
14impl std::fmt::Display for DrawingSurfaceUpdateError {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        f.write_str(self.0)
17    }
18}
19
20impl std::error::Error for DrawingSurfaceUpdateError {}
21
22type UpdateResult = Result<(), crate::rendering::composition::ServerJobError>;
23
24fn context_lost() -> crate::rendering::composition::ServerJobError {
25    Arc::new(PlatformGraphicsContextLostException)
26}
27
28/// The server-side counterpart of a
29/// [`CompositionDrawingSurface`](crate::rendering::composition::CompositionDrawingSurface):
30/// a surface whose content is a snapshot of an imported GPU image.
31pub struct ServerCompositionDrawingSurface {
32    compositor: Weak<ServerCompositor>,
33    bitmap: RefCell<Option<RefCounted<crate::platform::SharedBitmapImpl>>>,
34    created_with_context: RefCell<Option<Rc<dyn IPlatformRenderInterfaceContext>>>,
35    disposed: Cell<bool>,
36    changed: ServerCompositionSurfaceChanged,
37}
38
39impl ServerCompositionDrawingSurface {
40    pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionDrawingSurface> {
41        Rc::new(ServerCompositionDrawingSurface {
42            compositor: Rc::downgrade(compositor),
43            bitmap: RefCell::new(None),
44            created_with_context: RefCell::new(None),
45            disposed: Cell::new(false),
46            changed: ServerCompositionSurfaceChanged::new(),
47        })
48    }
49
50    fn compositor(&self) -> Rc<ServerCompositor> {
51        self.compositor.upgrade().expect("the compositor of a server object is alive while the object is used")
52    }
53
54    fn current_context(&self) -> Rc<dyn IPlatformRenderInterfaceContext> {
55        self.compositor().render_interface().value()
56    }
57
58    /// The counted reference to the content of the surface.
59    pub fn bitmap_ref(&self) -> Option<RefCounted<crate::platform::SharedBitmapImpl>> {
60        // Failsafe to avoid consuming an image imported with a different context
61        let current = self.current_context();
62        let same = self
63            .created_with_context
64            .borrow()
65            .as_ref()
66            .is_some_and(|context| std::ptr::addr_eq(Rc::as_ptr(context), Rc::as_ptr(&current)));
67        if !same {
68            return None;
69        }
70        self.bitmap.borrow().as_ref().map(|bitmap| bitmap.clone_ref())
71    }
72
73    fn perform_sanity_checks(&self, image: &ServerImportedGpuImage) -> UpdateResult {
74        // Failsafe to avoid consuming an image imported with a different context
75        if !image.is_usable(&self.compositor()) {
76            return Err(context_lost());
77        }
78
79        match image.base().import_result() {
80            // This should never happen, but check for it anyway to avoid a deadlock
81            None => Err(std::sync::Arc::new(DrawingSurfaceUpdateError("The import operation is not completed yet"))),
82            // Rethrow the import here exception
83            Some(Err(error)) => Err(error),
84            Some(Ok(())) => Ok(()),
85        }
86    }
87
88    fn update(&self, new_image: std::sync::Arc<crate::platform::SharedBitmapImpl>, context: Rc<dyn IPlatformRenderInterfaceContext>) {
89        if self.disposed.get() {
90            // Batches are processed with disposals before server jobs, so an update job
91            // can be processed after this surface was disposed in the same batch.
92            // Dispose the snapshot here on the render thread (context is current)
93            // instead of orphaning it.
94            new_image.dispose();
95            return;
96        }
97        if let Some(bitmap) = self.bitmap.borrow_mut().take() {
98            bitmap.dispose();
99        }
100        let item = new_image.clone();
101        *self.bitmap.borrow_mut() = Some(RefCountable::create(new_image, move || item.dispose()));
102        *self.created_with_context.borrow_mut() = Some(context);
103        self.changed.invoke();
104    }
105
106    /// Runs `f` with the graphics context current.
107    fn with_current_context(&self, f: impl FnOnce() -> UpdateResult) -> UpdateResult {
108        let current = self.compositor().render_interface().ensure_current();
109        let result = f();
110        current.dispose();
111        result
112    }
113
114    pub fn update_with_automatic_sync(&self, image: &ServerImportedGpuImage) -> UpdateResult {
115        self.with_current_context(|| {
116            self.perform_sanity_checks(image)?;
117            self.update(image.image().snapshot_with_automatic_sync(), image.context().clone());
118            Ok(())
119        })
120    }
121
122    pub fn update_with_keyed_mutex(
123        &self,
124        image: &ServerImportedGpuImage,
125        acquire_index: u32,
126        release_index: u32,
127    ) -> UpdateResult {
128        self.with_current_context(|| {
129            self.perform_sanity_checks(image)?;
130            self.update(image.image().snapshot_with_keyed_mutex(acquire_index, release_index), image.context().clone());
131            Ok(())
132        })
133    }
134
135    pub fn update_with_semaphores(
136        &self,
137        image: &ServerImportedGpuImage,
138        wait: &ServerImportedGpuSemaphore,
139        signal: &ServerImportedGpuSemaphore,
140    ) -> UpdateResult {
141        self.with_current_context(|| {
142            self.perform_sanity_checks(image)?;
143            let compositor = self.compositor();
144            if !wait.is_usable(&compositor) || !signal.is_usable(&compositor) {
145                return Err(context_lost());
146            }
147            self.update(
148                image.image().snapshot_with_semaphores(&wait.semaphore(), &signal.semaphore()),
149                image.context().clone(),
150            );
151            Ok(())
152        })
153    }
154
155    pub fn update_with_timeline_semaphores(
156        &self,
157        image: &ServerImportedGpuImage,
158        wait: &ServerImportedGpuSemaphore,
159        wait_for_value: u64,
160        signal: &ServerImportedGpuSemaphore,
161        signal_value: u64,
162    ) -> UpdateResult {
163        self.with_current_context(|| {
164            self.perform_sanity_checks(image)?;
165            let compositor = self.compositor();
166            if !wait.is_usable(&compositor) || !signal.is_usable(&compositor) {
167                return Err(context_lost());
168            }
169            self.update(
170                image.image().snapshot_with_timeline_semaphores(
171                    &wait.semaphore(),
172                    wait_for_value,
173                    &signal.semaphore(),
174                    signal_value,
175                ),
176                image.context().clone(),
177            );
178            Ok(())
179        })
180    }
181}
182
183impl IServerObject for ServerCompositionDrawingSurface {
184    fn dispose(&self) {
185        if let Some(bitmap) = self.bitmap.borrow_mut().take() {
186            bitmap.dispose();
187        }
188        self.disposed.set(true);
189    }
190
191    fn as_surface(self: Rc<Self>) -> Option<Rc<dyn IServerCompositionSurface>> {
192        Some(self)
193    }
194
195    fn as_any(&self) -> &dyn Any {
196        self
197    }
198
199    fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
200        self
201    }
202}
203
204impl IServerCompositionSurface for ServerCompositionDrawingSurface {
205    fn bitmap(&self) -> Option<std::sync::Arc<crate::platform::SharedBitmapImpl>> {
206        self.bitmap_ref().map(|bitmap| bitmap.item())
207    }
208
209    fn changed(&self) -> &ServerCompositionSurfaceChanged {
210        &self.changed
211    }
212}