Skip to main content

ferroui_base/rendering/composition/server/
server_composition_target.rs

1use super::{
2    impl_animated_server_object, CompositionTargetOverlays, DebugEventsDirtyRectCollectorProxy, IAnimatedServerObject,
3    IDirtyRectCollector, IDirtyRectTracker, IServerObject, MultiDirtyRectTracker, RegionDirtyRectTracker,
4    ServerCompositionVisual, ServerCompositor, ServerObject, SingleDirtyRectTracker,
5};
6use crate::media::Colors;
7use crate::platform::surfaces::IPlatformRenderSurface;
8use crate::platform::{
9    IDrawingContextImpl, IDrawingContextLayerImpl, IRenderTarget, LtrbRect, RenderTargetError, RenderTargetSceneInfo,
10};
11use crate::rendering::composition::generated::{
12    CompositionTargetChangedFields, ServerCompositionTargetHooks, ServerCompositionTargetProps,
13};
14use crate::rendering::composition::transport::BatchStreamReader;
15use crate::rendering::composition::{CompositionTransparencyLevel, ICompositionTargetDebugEvents};
16use crate::rendering::{LayoutPassTiming, RendererDebugOverlays};
17use crate::{Matrix, PixelRect, PixelSize, Size};
18use std::any::{Any, TypeId};
19use std::cell::{Cell, RefCell};
20use std::collections::HashMap;
21use std::sync::Arc;
22use std::rc::{Rc, Weak};
23use std::time::Duration;
24
25/// Returns the current platform surfaces of a composition target.
26///
27/// The thread that renders calls it and uses what it returns: the function
28/// and the surfaces are shared between the threads.
29pub type RenderSurfaces = std::sync::Arc<dyn Fn() -> Vec<std::sync::Arc<dyn IPlatformRenderSurface>> + Send + Sync>;
30
31/// Server-side counterpart of the `CompositionTarget`. That's the place
32/// where we update visual transforms, track dirty rects and actually do
33/// rendering.
34pub struct ServerCompositionTarget {
35    this: Weak<ServerCompositionTarget>,
36    object: ServerObject,
37    compositor: Weak<ServerCompositor>,
38    props: ServerCompositionTargetProps,
39    surfaces: RenderSurfaces,
40    overlays: CompositionTargetOverlays,
41    render_target: RefCell<Option<Rc<dyn IRenderTarget>>>,
42    layer_size: Cell<PixelSize>,
43    layer: RefCell<Option<Rc<dyn IDrawingContextLayerImpl>>>,
44    update_requested: Cell<bool>,
45    redraw_requested: Cell<bool>,
46    full_redraw_requested: Cell<bool>,
47    disposed: Cell<bool>,
48    attached_visuals: RefCell<HashMap<*const ServerCompositionVisual, Rc<ServerCompositionVisual>>>,
49    dirty_rects: Rc<dyn IDirtyRectTracker>,
50    id: i64,
51    debug_events: RefCell<Option<Arc<dyn ICompositionTargetDebugEvents>>>,
52    rendered_visuals: Cell<i32>,
53    visited_visuals: Cell<i32>,
54    is_waiting_for_ready_render_target: Cell<bool>,
55    is_waiting_for_render_loop_wakeup: Cell<bool>,
56}
57
58impl ServerCompositionTarget {
59    /// Whether the target has been disposed. A disposed target stays in
60    /// the table of the compositor until its id is released.
61    pub(crate) fn is_disposed(&self) -> bool {
62        self.disposed.get()
63    }
64
65    /// `id` is the identity of the target, allocated by the UI thread so
66    /// that it can match readback data against it.
67    pub fn new(compositor: &Rc<ServerCompositor>, surfaces: RenderSurfaces, id: i64) -> Rc<ServerCompositionTarget> {
68        let mut dirty_rects: Option<Rc<dyn IDirtyRectTracker>> = None;
69        // Not the service locator: a target is created by the thread that
70        // applies the batch. The render interface is lent for the call.
71        compositor.render_interface().with_platform_render_interface(|platform_render| {
72            if platform_render.supports_regions() && compositor.options().use_region_dirty_rect_clipping == Some(true) {
73                let max_rects = compositor.options().max_dirty_rects.unwrap_or(8);
74                dirty_rects = Some(if max_rects <= 0 {
75                    Rc::new(RegionDirtyRectTracker::new(platform_render))
76                } else {
77                    Rc::new(MultiDirtyRectTracker::new(
78                        platform_render,
79                        max_rects,
80                        // WPF uses 50K, but that merges stuff rather aggressively
81                        compositor.options().dirty_rect_merge_eagerness.unwrap_or(1000.0),
82                    ))
83                });
84            }
85        });
86        let dirty_rects = dirty_rects.unwrap_or_else(|| Rc::new(SingleDirtyRectTracker::new()));
87
88        Rc::new_cyclic(|this: &Weak<ServerCompositionTarget>| {
89            let owner: Weak<dyn IAnimatedServerObject> = this.clone();
90            ServerCompositionTarget {
91                this: this.clone(),
92                object: ServerObject::new(compositor, owner),
93                compositor: Rc::downgrade(compositor),
94                props: ServerCompositionTargetProps::new(),
95                surfaces,
96                overlays: CompositionTargetOverlays::new(compositor),
97                render_target: RefCell::new(None),
98                layer_size: Cell::new(PixelSize::default()),
99                layer: RefCell::new(None),
100                update_requested: Cell::new(false),
101                redraw_requested: Cell::new(false),
102                full_redraw_requested: Cell::new(false),
103                disposed: Cell::new(false),
104                attached_visuals: RefCell::new(HashMap::new()),
105                dirty_rects,
106                id,
107                debug_events: RefCell::new(None),
108                rendered_visuals: Cell::new(0),
109                visited_visuals: Cell::new(0),
110                is_waiting_for_ready_render_target: Cell::new(false),
111                is_waiting_for_render_loop_wakeup: Cell::new(false),
112            }
113        })
114    }
115
116    pub fn compositor(&self) -> Option<Rc<ServerCompositor>> {
117        self.compositor.upgrade()
118    }
119
120    pub fn dirty_rects(&self) -> &Rc<dyn IDirtyRectTracker> {
121        &self.dirty_rects
122    }
123
124    pub fn id(&self) -> i64 {
125        self.id
126    }
127
128    pub fn debug_events(&self) -> Option<Arc<dyn ICompositionTargetDebugEvents>> {
129        self.debug_events.borrow().clone()
130    }
131
132    pub fn set_debug_events(&self, value: Option<Arc<dyn ICompositionTargetDebugEvents>>) {
133        *self.debug_events.borrow_mut() = value;
134    }
135
136    pub fn rendered_visuals(&self) -> i32 {
137        self.rendered_visuals.get()
138    }
139
140    pub fn visited_visuals(&self) -> i32 {
141        self.visited_visuals.get()
142    }
143
144    pub fn pixel_size(&self) -> PixelSize {
145        PixelSize::from_size_ceiling(self.size(), self.scaling())
146    }
147
148    /// Returns true if the target is enabled and has pending work but its
149    /// render target was not ready.
150    pub fn is_waiting_for_ready_render_target(&self) -> bool {
151        self.is_waiting_for_ready_render_target.get()
152    }
153
154    /// Returns true if the target's render target is waiting for a render
155    /// loop wakeup (i.e. the platform will call Wakeup() when ready, no
156    /// need to keep polling).
157    pub fn is_waiting_for_render_loop_wakeup(&self) -> bool {
158        self.is_waiting_for_render_loop_wakeup.get()
159    }
160
161    // --- generated properties -------------------------------------------------
162
163    pub fn root(&self) -> Option<Rc<ServerCompositionVisual>> {
164        self.props.root().and_then(|o| o.into_any_rc().downcast::<ServerCompositionVisual>().ok())
165    }
166
167    pub fn is_enabled(&self) -> bool {
168        self.props.is_enabled()
169    }
170
171    pub fn debug_overlays(&self) -> RendererDebugOverlays {
172        self.props.debug_overlays()
173    }
174
175    pub fn last_layout_pass_timing(&self) -> LayoutPassTiming {
176        self.props.last_layout_pass_timing()
177    }
178
179    pub fn scaling(&self) -> f64 {
180        self.props.scaling()
181    }
182
183    pub fn size(&self) -> Size {
184        self.props.size()
185    }
186
187    pub fn transparency_level(&self) -> CompositionTransparencyLevel {
188        self.props.transparency_level()
189    }
190
191    pub fn platform_specific_scene_info(&self) -> Option<std::sync::Arc<dyn Any + Send + Sync>> {
192        self.props.platform_specific_scene_info()
193    }
194
195    // ---------------------------------------------------------------------------
196
197    pub fn update(self: &Rc<Self>, diagnostics_compositor_global_update_elapsed_time: Duration) {
198        let Some(compositor) = self.compositor() else { return };
199        if self.disposed.get() {
200            compositor.remove_composition_target(self);
201            return;
202        }
203
204        let Some(root) = self.root() else { return };
205
206        self.overlays.record_global_compositor_update_time(self, diagnostics_compositor_global_update_elapsed_time);
207        self.overlays.mark_update_call_start(self);
208
209        let scaling = self.scaling();
210        let transform = Matrix::create_scale(scaling, scaling);
211        let tracker: Rc<dyn IDirtyRectCollector> = self.dirty_rects.clone();
212        let collector: Rc<dyn IDirtyRectCollector> = match self.debug_events() {
213            Some(events) => Rc::new(DebugEventsDirtyRectCollectorProxy::new(tracker, events)),
214            None => tracker,
215        };
216        let pixel_size = self.pixel_size();
217        root.update_root(
218            collector,
219            transform,
220            LtrbRect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64),
221        );
222        self.update_requested.set(false);
223        self.overlays.mark_update_call_end(self);
224    }
225
226    pub fn render(self: &Rc<Self>) {
227        self.is_waiting_for_ready_render_target.set(false);
228        self.is_waiting_for_render_loop_wakeup.set(false);
229
230        if self.disposed.get() {
231            return;
232        }
233
234        let Some(root) = self.root() else { return };
235        let Some(compositor) = self.compositor() else { return };
236
237        let corrupted = self.render_target.borrow().as_ref().is_some_and(|t| t.platform_render_target_state().is_corrupted);
238        if corrupted {
239            if let Some(layer) = self.layer.borrow_mut().take() {
240                layer.dispose();
241            }
242            if let Some(render_target) = self.render_target.borrow_mut().take() {
243                render_target.dispose();
244            }
245            self.redraw_requested.set(true);
246        }
247
248        if self.render_target.borrow().is_none() {
249            let surfaces = (self.surfaces)();
250            if !compositor.is_ready_to_create_render_target(&surfaces) {
251                self.is_waiting_for_ready_render_target.set(self.is_enabled());
252                return;
253            }
254            let render_target = compositor.create_render_target(&surfaces);
255            *self.render_target.borrow_mut() = Some(render_target);
256        }
257        let Some(render_target) = self.render_target.borrow().clone() else { return };
258
259        if self.dirty_rects.is_empty() && !self.redraw_requested.get() && !self.update_requested.get() {
260            return;
261        }
262
263        self.redraw_requested.set(self.redraw_requested.get() | !self.dirty_rects.is_empty());
264
265        if !self.redraw_requested.get() {
266            return;
267        }
268
269        let state = render_target.platform_render_target_state();
270        if !state.is_ready {
271            self.is_waiting_for_ready_render_target.set(self.is_enabled());
272            self.is_waiting_for_render_loop_wakeup
273                .set(self.is_enabled() && state.will_wake_up_render_loop_when_ready);
274            return;
275        }
276
277        let properties = render_target.properties();
278        let need_layer = self.overlays.require_layer() // Check if we don't need overlays
279            // Check if render target can be rendered to directly and preserves the previous frame
280            || !(properties.retains_previous_frame_contents && properties.is_suitable_for_direct_rendering);
281
282        let pixel_size = self.pixel_size();
283        let scene_info = RenderTargetSceneInfo {
284            size: pixel_size,
285            scaling: self.scaling(),
286            logical_size: self.size(),
287            transparency_level: self.transparency_level(),
288            platform_specific_scene_info: self.platform_specific_scene_info(),
289        };
290        let (mut render_target_context, properties) = match render_target.try_create_drawing_context(&scene_info) {
291            Ok(created) => created,
292            Err(RenderTargetError::NotReady(_)) => {
293                self.is_waiting_for_ready_render_target.set(self.is_enabled());
294                return;
295            }
296            Err(RenderTargetError::Corrupted(_)) => return,
297        };
298
299        {
300            let mut full_redraw = false;
301
302            let layer_is_stale = {
303                let layer = self.layer.borrow();
304                pixel_size != self.layer_size.get() || layer.as_ref().is_none_or(|layer| layer.is_corrupted())
305            };
306            if need_layer && layer_is_stale {
307                if let Some(layer) = self.layer.borrow_mut().take() {
308                    layer.dispose();
309                }
310                *self.layer.borrow_mut() = Some(render_target_context.create_layer(pixel_size));
311                self.layer_size.set(pixel_size);
312                full_redraw = true;
313            } else if !need_layer {
314                if let Some(layer) = self.layer.borrow_mut().take() {
315                    layer.dispose();
316                }
317            }
318
319            if self.full_redraw_requested.get() || (!need_layer && !properties.previous_frame_is_retained) {
320                self.full_redraw_requested.set(false);
321                full_redraw = true;
322            }
323
324            let render_bounds = LtrbRect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64);
325
326            if full_redraw {
327                self.dirty_rects.initialize(render_bounds);
328                self.dirty_rects.add_rect(render_bounds);
329            }
330
331            if !self.dirty_rects.is_empty() {
332                self.dirty_rects.finalize_frame(render_bounds);
333                let layer = self.layer.borrow().clone();
334                match layer {
335                    Some(layer) => {
336                        {
337                            let mut context = layer.create_drawing_context();
338                            self.render_root_to_context_with_clip(&compositor, &mut *context, &root);
339                            context.dispose();
340                        }
341
342                        render_target_context.clear(Colors::TRANSPARENT);
343                        render_target_context.set_transform(Matrix::IDENTITY);
344                        if layer.can_blit() {
345                            layer.blit(&mut *render_target_context);
346                        } else {
347                            let rect = PixelRect::from_size(pixel_size).to_rect(1.0);
348                            render_target_context.draw_bitmap(&*layer, 1.0, rect, rect);
349                        }
350                        self.overlays.draw(self, &mut *render_target_context, true);
351                    }
352                    None => {
353                        self.render_root_to_context_with_clip(&compositor, &mut *render_target_context, &root);
354                        self.overlays.draw(self, &mut *render_target_context, false);
355                    }
356                }
357            }
358
359            self.rendered_visuals.set(0);
360            self.visited_visuals.set(0);
361
362            self.redraw_requested.set(false);
363            self.dirty_rects.initialize(render_bounds);
364        }
365        render_target_context.dispose();
366    }
367
368    fn render_root_to_context_with_clip(
369        &self,
370        compositor: &ServerCompositor,
371        context: &mut dyn IDrawingContextImpl,
372        root: &Rc<ServerCompositionVisual>,
373    ) {
374        let use_layer_clip = compositor.options().use_save_layer_root_clip.unwrap_or(false);
375        self.dirty_rects.begin_draw(context);
376        {
377            context.clear(Colors::TRANSPARENT);
378            if use_layer_clip {
379                context.push_layer(self.dirty_rects.combined_rect().to_rect());
380            }
381            let scaling = self.scaling();
382            context.set_transform(Matrix::create_scale(scaling, scaling));
383            let pixel_size = self.pixel_size();
384            let (visited, rendered) = root.render(
385                context,
386                LtrbRect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64),
387                Some(self.dirty_rects.clone()),
388                true,
389                false,
390                false,
391            );
392            self.visited_visuals.set(visited);
393            self.rendered_visuals.set(rendered);
394            if let Some(events) = self.debug_events() {
395                events.set_rendered_visuals(rendered);
396                events.set_visited_visuals(visited);
397            }
398
399            if use_layer_clip {
400                context.pop_layer();
401            }
402        }
403        self.dirty_rects.end_draw(context);
404    }
405
406    pub fn request_update(&self) {
407        self.update_requested.set(true);
408    }
409
410    pub fn reset_render_target(&self) {
411        if self.layer.borrow().is_none() && self.render_target.borrow().is_none() {
412            return;
413        }
414        let current = self.compositor().map(|compositor| compositor.render_interface().ensure_current());
415        self.release_render_target();
416        if let Some(current) = current {
417            current.dispose();
418        }
419    }
420
421    /// Releases the layer and the render target of the target, in the
422    /// graphics context that is current: the caller has made the context of
423    /// the compositor current, without asking for a backend context (see
424    /// [`ServerCompositor::release_gpu_resources`]).
425    pub(crate) fn release_render_target(&self) {
426        if let Some(layer) = self.layer.borrow_mut().take() {
427            layer.dispose();
428        }
429        if let Some(render_target) = self.render_target.borrow_mut().take() {
430            render_target.dispose();
431        }
432    }
433
434    pub fn add_visual(&self, visual: &Rc<ServerCompositionVisual>) {
435        let added = self.attached_visuals.borrow_mut().insert(Rc::as_ptr(visual), visual.clone()).is_none();
436        if added && self.is_enabled() {
437            visual.activate();
438        }
439    }
440
441    pub fn remove_visual(&self, visual: &Rc<ServerCompositionVisual>) {
442        let removed = self.attached_visuals.borrow_mut().remove(&Rc::as_ptr(visual)).is_some();
443        if removed && self.is_enabled() {
444            visual.deactivate();
445        }
446    }
447
448    pub fn request_full_redraw(&self) {
449        self.redraw_requested.set(true);
450    }
451}
452
453impl ServerCompositionTargetHooks for ServerCompositionTarget {
454    fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {
455        self.redraw_requested.set(true);
456        self.full_redraw_requested.set(true);
457    }
458
459    fn on_fields_deserialized(&self, _changed: CompositionTargetChangedFields) {}
460
461    fn on_is_enabled_changed(&self) {
462        let (Some(compositor), Some(this)) = (self.compositor(), self.this.upgrade()) else { return };
463        let visuals: Vec<_> = self.attached_visuals.borrow().values().cloned().collect();
464        if self.is_enabled() {
465            compositor.add_composition_target(&this);
466            for visual in visuals {
467                visual.activate();
468            }
469        } else {
470            compositor.remove_composition_target(&this);
471            for visual in visuals {
472                visual.deactivate();
473            }
474        }
475    }
476
477    fn on_debug_overlays_changed(&self) {
478        self.full_redraw_requested.set(true);
479        self.overlays.on_changed(self.debug_overlays());
480    }
481
482    fn on_last_layout_pass_timing_changed(&self) {
483        self.overlays.on_last_layout_pass_timing_changed(self, self.last_layout_pass_timing());
484    }
485}
486
487impl_animated_server_object!(ServerCompositionTarget, object);
488
489impl IServerObject for ServerCompositionTarget {
490    fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
491        self.props.deserialize_changes_core(self, reader, committed_at);
492    }
493
494    fn dispose(&self) {
495        if self.disposed.replace(true) {
496            return;
497        }
498        self.reset_render_target();
499        if let (Some(compositor), Some(this)) = (self.compositor(), self.this.upgrade()) {
500            compositor.remove_composition_target(&this);
501        }
502        // The garbage collector reclaims the target and its tree upstream;
503        // here the references that form cycles are dropped.
504        self.attached_visuals.borrow_mut().clear();
505        ServerCompositionTargetProps::id_of_root_property().set_field(self, None);
506    }
507
508    fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
509        self.props.find_props(type_id)
510    }
511
512    fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
513        Some(self)
514    }
515
516    fn as_any(&self) -> &dyn Any {
517        self
518    }
519
520    fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
521        self
522    }
523}