Skip to main content

ferroui_base/rendering/composition/
composition_drawing_surface.rs

1use super::server::{IServerObject, LockBound, ServerCompositionDrawingSurface, ServerCompositor, ServerObjectId};
2use super::{
3    CompositionSurface, Compositor, ICompositionImportedGpuImage, ICompositionImportedGpuSemaphore, ServerImportedGpuImage,
4    ServerImportedGpuSemaphore, ServerJobTask,
5};
6use std::ops::Deref;
7use std::rc::Rc;
8use std::sync::Arc;
9
10/// A composition surface whose content is set from imported GPU images.
11#[derive(Clone)]
12pub struct CompositionDrawingSurface(Rc<CompositionSurface>);
13
14impl Deref for CompositionDrawingSurface {
15    type Target = Rc<CompositionSurface>;
16
17    fn deref(&self) -> &Rc<CompositionSurface> {
18        &self.0
19    }
20}
21
22/// The server part of an imported image: what the job of an update takes
23/// to the render thread. The object of the caller stays on its thread.
24fn imported_image(image: &dyn ICompositionImportedGpuImage) -> Arc<LockBound<ServerImportedGpuImage>> {
25    match image.as_imported_gpu_image() {
26        Some(image) => image.server(),
27        None => panic!("the image was not imported by a composition interop"),
28    }
29}
30
31/// The server part of an imported semaphore; see [`imported_image`].
32fn imported_semaphore(semaphore: &dyn ICompositionImportedGpuSemaphore) -> Arc<LockBound<ServerImportedGpuSemaphore>> {
33    match semaphore.as_imported_gpu_semaphore() {
34        Some(semaphore) => semaphore.server(),
35        None => panic!("the semaphore was not imported by a composition interop"),
36    }
37}
38
39impl CompositionDrawingSurface {
40    pub(crate) fn new(compositor: &Rc<Compositor>) -> CompositionDrawingSurface {
41        let server = compositor.create_server_object(|compositor, _| ServerCompositionDrawingSurface::new(compositor));
42        CompositionDrawingSurface(CompositionSurface::new(compositor, server))
43    }
44
45    fn server_id(&self) -> ServerObjectId {
46        super::ICompositionObject::server(&*self.0)
47    }
48
49    /// Runs `update` on the server surface on the render thread.
50    ///
51    /// The job names the surface by id. The id stays bound to the server
52    /// surface while this object is alive, disposed or not (see
53    /// [`CompositionObject`](super::CompositionObject)), so the job reaches
54    /// the surface as upstream, where it holds the server object itself; a
55    /// disposed surface disposes the new snapshot.
56    fn invoke(
57        &self,
58        update: impl FnOnce(&ServerCompositor, &ServerCompositionDrawingSurface) -> Result<(), crate::rendering::composition::ServerJobError>
59            + Send
60            + 'static,
61    ) -> ServerJobTask<()> {
62        // What an update takes to the render thread are plain values and the
63        // server parts of the image and of the synchronisation objects,
64        // which are confined to the compositor lock the job runs under.
65        self.compositor().invoke_server_object_job_async(
66            self.server_id(),
67            move |compositor, server: Option<Rc<dyn IServerObject>>| {
68                let server = server.and_then(|server| server.into_any_rc().downcast::<ServerCompositionDrawingSurface>().ok());
69                match server {
70                    Some(server) => update(compositor, &server),
71                    None => panic!("the server object of a live drawing surface is not a drawing surface"),
72                }
73            },
74            false,
75        )
76    }
77
78    /// Updates the surface contents using an imported memory image using a keyed mutex as the means of synchronization
79    ///
80    /// `acquire_index` is the mutex key to wait for before accessing the
81    /// image, `release_index` the mutex key to release for after accessing
82    /// the image. Returns a task that completes when update operation is
83    /// completed and user code is free to destroy or dispose the image.
84    pub fn update_with_keyed_mutex_async(
85        &self,
86        image: &dyn ICompositionImportedGpuImage,
87        acquire_index: u32,
88        release_index: u32,
89    ) -> ServerJobTask<()> {
90        let img = imported_image(image);
91        self.invoke(move |compositor, server| {
92            server.update_with_keyed_mutex(img.get(compositor), acquire_index, release_index)
93        })
94    }
95
96    /// Updates the surface contents using an imported memory image using a semaphore pair as the means of synchronization
97    ///
98    /// `wait_for_semaphore` is the semaphore to wait for before accessing
99    /// the image, `signal_semaphore` the semaphore to signal after
100    /// accessing the image.
101    pub fn update_with_semaphores_async(
102        &self,
103        image: &dyn ICompositionImportedGpuImage,
104        wait_for_semaphore: &dyn ICompositionImportedGpuSemaphore,
105        signal_semaphore: &dyn ICompositionImportedGpuSemaphore,
106    ) -> ServerJobTask<()> {
107        let img = imported_image(image);
108        let wait = imported_semaphore(wait_for_semaphore);
109        let signal = imported_semaphore(signal_semaphore);
110        self.invoke(move |compositor, server| {
111            server.update_with_semaphores(img.get(compositor), wait.get(compositor), signal.get(compositor))
112        })
113    }
114
115    /// Updates the surface contents using an imported memory image using a semaphore pair as the means of synchronization
116    ///
117    /// The semaphores are timeline semaphores: `wait_for_value` is the value
118    /// to wait for before accessing the image, `signal_value` the value to
119    /// signal after accessing it.
120    pub fn update_with_timeline_semaphores_async(
121        &self,
122        image: &dyn ICompositionImportedGpuImage,
123        wait_for_semaphore: &dyn ICompositionImportedGpuSemaphore,
124        wait_for_value: u64,
125        signal_semaphore: &dyn ICompositionImportedGpuSemaphore,
126        signal_value: u64,
127    ) -> ServerJobTask<()> {
128        let img = imported_image(image);
129        let wait = imported_semaphore(wait_for_semaphore);
130        let signal = imported_semaphore(signal_semaphore);
131        self.invoke(move |compositor, server| {
132            server.update_with_timeline_semaphores(
133                img.get(compositor),
134                wait.get(compositor),
135                wait_for_value,
136                signal.get(compositor),
137                signal_value,
138            )
139        })
140    }
141
142    /// Updates the surface contents using an unspecified automatic means of synchronization
143    /// provided by the underlying platform
144    pub fn update_async(&self, image: &dyn ICompositionImportedGpuImage) -> ServerJobTask<()> {
145        let img = imported_image(image);
146        self.invoke(move |compositor, server| server.update_with_automatic_sync(img.get(compositor)))
147    }
148
149    pub fn dispose(&self) {
150        self.0.dispose()
151    }
152}