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
25pub type RenderSurfaces = std::sync::Arc<dyn Fn() -> Vec<std::sync::Arc<dyn IPlatformRenderSurface>> + Send + Sync>;
30
31pub 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 frame_times: RefCell<Option<Vec<std::time::Duration>>>,
62 drew_frame: Cell<bool>,
64 backdrop_regions: RefCell<Vec<(Weak<ServerCompositionVisual>, LtrbRect)>>,
68}
69
70impl ServerCompositionTarget {
71 pub(crate) fn is_disposed(&self) -> bool {
74 self.disposed.get()
75 }
76
77 pub fn new(compositor: &Rc<ServerCompositor>, surfaces: RenderSurfaces, id: i64) -> Rc<ServerCompositionTarget> {
80 let mut dirty_rects: Option<Rc<dyn IDirtyRectTracker>> = None;
81 compositor.render_interface().with_platform_render_interface(|platform_render| {
84 if platform_render.supports_regions() && compositor.options().use_region_dirty_rect_clipping == Some(true) {
85 let max_rects = compositor.options().max_dirty_rects.unwrap_or(8);
86 dirty_rects = Some(if max_rects <= 0 {
87 Rc::new(RegionDirtyRectTracker::new(platform_render))
88 } else {
89 Rc::new(MultiDirtyRectTracker::new(
90 platform_render,
91 max_rects,
92 compositor.options().dirty_rect_merge_eagerness.unwrap_or(1000.0),
94 ))
95 });
96 }
97 });
98 let dirty_rects = dirty_rects.unwrap_or_else(|| Rc::new(SingleDirtyRectTracker::new()));
99
100 Rc::new_cyclic(|this: &Weak<ServerCompositionTarget>| {
101 let owner: Weak<dyn IAnimatedServerObject> = this.clone();
102 ServerCompositionTarget {
103 this: this.clone(),
104 object: ServerObject::new(compositor, owner),
105 compositor: Rc::downgrade(compositor),
106 props: ServerCompositionTargetProps::new(),
107 surfaces,
108 overlays: CompositionTargetOverlays::new(compositor),
109 render_target: RefCell::new(None),
110 layer_size: Cell::new(PixelSize::default()),
111 layer: RefCell::new(None),
112 update_requested: Cell::new(false),
113 redraw_requested: Cell::new(false),
114 full_redraw_requested: Cell::new(false),
115 disposed: Cell::new(false),
116 attached_visuals: RefCell::new(HashMap::new()),
117 dirty_rects,
118 id,
119 debug_events: RefCell::new(None),
120 rendered_visuals: Cell::new(0),
121 visited_visuals: Cell::new(0),
122 is_waiting_for_ready_render_target: Cell::new(false),
123 is_waiting_for_render_loop_wakeup: Cell::new(false),
124 frame_times: RefCell::new(
125 std::env::var("FERROUI_FRAME_TIMES").is_ok_and(|value| value == "1").then(Vec::new),
126 ),
127 drew_frame: Cell::new(false),
128 backdrop_regions: RefCell::new(Vec::new()),
129 }
130 })
131 }
132
133 fn expand_dirty_rects_over_backdrops(&self) {
137 let mut regions = self.backdrop_regions.borrow_mut();
138 regions.retain(|(visual, _)| visual.upgrade().is_some_and(|visual| visual.root().is_some()));
139 if regions.is_empty() || self.dirty_rects.is_empty() {
140 return;
141 }
142 let mut added = vec![false; regions.len()];
143 loop {
144 let mut changed = false;
145 for (index, (_, region)) in regions.iter().enumerate() {
146 if !added[index] && self.dirty_rects.intersects(*region) {
147 self.dirty_rects.add_rect(*region);
148 added[index] = true;
149 changed = true;
150 }
151 }
152 if !changed {
153 break;
154 }
155 }
156 }
157
158 fn record_backdrop_regions(&self) {
161 let drawn = super::take_backdrop_regions();
162 if drawn.is_empty() {
163 return;
164 }
165 let mut regions = self.backdrop_regions.borrow_mut();
166 for (visual, region) in drawn {
167 regions.retain(|(existing, _)| !existing.ptr_eq(&visual));
168 regions.push((visual, region));
169 }
170 }
171
172 pub fn compositor(&self) -> Option<Rc<ServerCompositor>> {
173 self.compositor.upgrade()
174 }
175
176 pub fn dirty_rects(&self) -> &Rc<dyn IDirtyRectTracker> {
177 &self.dirty_rects
178 }
179
180 pub fn id(&self) -> i64 {
181 self.id
182 }
183
184 pub fn debug_events(&self) -> Option<Arc<dyn ICompositionTargetDebugEvents>> {
185 self.debug_events.borrow().clone()
186 }
187
188 pub fn set_debug_events(&self, value: Option<Arc<dyn ICompositionTargetDebugEvents>>) {
189 *self.debug_events.borrow_mut() = value;
190 }
191
192 pub fn rendered_visuals(&self) -> i32 {
193 self.rendered_visuals.get()
194 }
195
196 pub fn visited_visuals(&self) -> i32 {
197 self.visited_visuals.get()
198 }
199
200 pub fn pixel_size(&self) -> PixelSize {
201 PixelSize::from_size_ceiling(self.size(), self.scaling())
202 }
203
204 pub fn is_waiting_for_ready_render_target(&self) -> bool {
207 self.is_waiting_for_ready_render_target.get()
208 }
209
210 pub fn is_waiting_for_render_loop_wakeup(&self) -> bool {
214 self.is_waiting_for_render_loop_wakeup.get()
215 }
216
217 pub fn root(&self) -> Option<Rc<ServerCompositionVisual>> {
220 self.props.root().and_then(|o| o.into_any_rc().downcast::<ServerCompositionVisual>().ok())
221 }
222
223 pub fn is_enabled(&self) -> bool {
224 self.props.is_enabled()
225 }
226
227 pub fn debug_overlays(&self) -> RendererDebugOverlays {
228 self.props.debug_overlays()
229 }
230
231 pub fn last_layout_pass_timing(&self) -> LayoutPassTiming {
232 self.props.last_layout_pass_timing()
233 }
234
235 pub fn scaling(&self) -> f64 {
236 self.props.scaling()
237 }
238
239 pub fn size(&self) -> Size {
240 self.props.size()
241 }
242
243 pub fn transparency_level(&self) -> CompositionTransparencyLevel {
244 self.props.transparency_level()
245 }
246
247 pub fn platform_specific_scene_info(&self) -> Option<std::sync::Arc<dyn Any + Send + Sync>> {
248 self.props.platform_specific_scene_info()
249 }
250
251 pub fn update(self: &Rc<Self>, diagnostics_compositor_global_update_elapsed_time: Duration) {
254 let Some(compositor) = self.compositor() else { return };
255 if self.disposed.get() {
256 compositor.remove_composition_target(self);
257 return;
258 }
259
260 let Some(root) = self.root() else { return };
261
262 self.overlays.record_global_compositor_update_time(self, diagnostics_compositor_global_update_elapsed_time);
263 self.overlays.mark_update_call_start(self);
264
265 let scaling = self.scaling();
266 let transform = Matrix::create_scale(scaling, scaling);
267 let tracker: Rc<dyn IDirtyRectCollector> = self.dirty_rects.clone();
268 let collector: Rc<dyn IDirtyRectCollector> = match self.debug_events() {
269 Some(events) => Rc::new(DebugEventsDirtyRectCollectorProxy::new(tracker, events)),
270 None => tracker,
271 };
272 let pixel_size = self.pixel_size();
273 root.update_root(
274 collector,
275 transform,
276 LtrbRect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64),
277 );
278 self.update_requested.set(false);
279 self.overlays.mark_update_call_end(self);
280 }
281
282 pub fn render(self: &Rc<Self>) {
283 if self.frame_times.borrow().is_none() {
284 return self.render_frame();
285 }
286 self.drew_frame.set(false);
287 let start = std::time::Instant::now();
288 self.render_frame();
289 if self.drew_frame.get() {
290 if let Some(times) = self.frame_times.borrow_mut().as_mut() {
291 times.push(start.elapsed());
292 }
293 }
294 }
295
296 fn frame_times_summary(&self) -> Option<String> {
299 let mut times = self.frame_times.borrow().clone()?;
300 if times.is_empty() {
301 return None;
302 }
303 times.sort();
304 let ms = |duration: std::time::Duration| duration.as_secs_f64() * 1000.0;
305 let mean = times.iter().map(|time| ms(*time)).sum::<f64>() / times.len() as f64;
306 Some(format!(
307 "frame times of composition target {}: {} frames; ms per frame: median {:.3}, mean {:.3}, p95 {:.3}, max {:.3}",
308 self.id,
309 times.len(),
310 ms(times[times.len() / 2]),
311 mean,
312 ms(times[(times.len() * 95 / 100).min(times.len() - 1)]),
313 ms(times[times.len() - 1]),
314 ))
315 }
316
317 fn render_frame(self: &Rc<Self>) {
318 self.is_waiting_for_ready_render_target.set(false);
319 self.is_waiting_for_render_loop_wakeup.set(false);
320
321 if self.disposed.get() {
322 return;
323 }
324
325 let Some(root) = self.root() else { return };
326 let Some(compositor) = self.compositor() else { return };
327
328 let corrupted = self.render_target.borrow().as_ref().is_some_and(|t| t.platform_render_target_state().is_corrupted);
329 if corrupted {
330 if let Some(layer) = self.layer.borrow_mut().take() {
331 layer.dispose();
332 }
333 if let Some(render_target) = self.render_target.borrow_mut().take() {
334 render_target.dispose();
335 }
336 self.redraw_requested.set(true);
337 }
338
339 if self.render_target.borrow().is_none() {
340 let surfaces = (self.surfaces)();
341 if !compositor.is_ready_to_create_render_target(&surfaces) {
342 self.is_waiting_for_ready_render_target.set(self.is_enabled());
343 return;
344 }
345 let render_target = compositor.create_render_target(&surfaces);
346 *self.render_target.borrow_mut() = Some(render_target);
347 }
348 let Some(render_target) = self.render_target.borrow().clone() else { return };
349
350 if self.dirty_rects.is_empty() && !self.redraw_requested.get() && !self.update_requested.get() {
351 return;
352 }
353
354 self.redraw_requested.set(self.redraw_requested.get() | !self.dirty_rects.is_empty());
355
356 if !self.redraw_requested.get() {
357 return;
358 }
359
360 let state = render_target.platform_render_target_state();
361 if !state.is_ready {
362 self.is_waiting_for_ready_render_target.set(self.is_enabled());
363 self.is_waiting_for_render_loop_wakeup
364 .set(self.is_enabled() && state.will_wake_up_render_loop_when_ready);
365 return;
366 }
367
368 let properties = render_target.properties();
369 let need_layer = self.overlays.require_layer() || !(properties.retains_previous_frame_contents && properties.is_suitable_for_direct_rendering);
372
373 let pixel_size = self.pixel_size();
374 let scene_info = RenderTargetSceneInfo {
375 size: pixel_size,
376 scaling: self.scaling(),
377 logical_size: self.size(),
378 transparency_level: self.transparency_level(),
379 platform_specific_scene_info: self.platform_specific_scene_info(),
380 };
381 self.drew_frame.set(true);
382 let (mut render_target_context, properties) = match render_target.try_create_drawing_context(&scene_info) {
383 Ok(created) => created,
384 Err(RenderTargetError::NotReady(_)) => {
385 self.is_waiting_for_ready_render_target.set(self.is_enabled());
386 return;
387 }
388 Err(RenderTargetError::Corrupted(_)) => return,
389 };
390
391 {
392 let mut full_redraw = false;
393
394 let layer_is_stale = {
395 let layer = self.layer.borrow();
396 pixel_size != self.layer_size.get() || layer.as_ref().is_none_or(|layer| layer.is_corrupted())
397 };
398 if need_layer && layer_is_stale {
399 if let Some(layer) = self.layer.borrow_mut().take() {
400 layer.dispose();
401 }
402 *self.layer.borrow_mut() = Some(render_target_context.create_layer(pixel_size));
403 self.layer_size.set(pixel_size);
404 full_redraw = true;
405 } else if !need_layer {
406 if let Some(layer) = self.layer.borrow_mut().take() {
407 layer.dispose();
408 }
409 }
410
411 if self.full_redraw_requested.get() || (!need_layer && !properties.previous_frame_is_retained) {
412 self.full_redraw_requested.set(false);
413 full_redraw = true;
414 }
415
416 let render_bounds = LtrbRect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64);
417
418 if full_redraw {
419 self.dirty_rects.initialize(render_bounds);
420 self.dirty_rects.add_rect(render_bounds);
421 }
422
423 self.expand_dirty_rects_over_backdrops();
424 if !self.dirty_rects.is_empty() {
425 super::take_backdrop_regions();
426 self.dirty_rects.finalize_frame(render_bounds);
427 let layer = self.layer.borrow().clone();
428 match layer {
429 Some(layer) => {
430 {
431 let mut context = layer.create_drawing_context();
432 self.render_root_to_context_with_clip(&compositor, &mut *context, &root);
433 context.dispose();
434 }
435
436 render_target_context.clear(Colors::TRANSPARENT);
437 render_target_context.set_transform(Matrix::IDENTITY);
438 if layer.can_blit() {
439 layer.blit(&mut *render_target_context);
440 } else {
441 let rect = PixelRect::from_size(pixel_size).to_rect(1.0);
442 render_target_context.draw_bitmap(&*layer, 1.0, rect, rect);
443 }
444 self.overlays.draw(self, &mut *render_target_context, true);
445 }
446 None => {
447 self.render_root_to_context_with_clip(&compositor, &mut *render_target_context, &root);
448 self.overlays.draw(self, &mut *render_target_context, false);
449 }
450 }
451 self.record_backdrop_regions();
452 }
453
454 self.rendered_visuals.set(0);
455 self.visited_visuals.set(0);
456
457 self.redraw_requested.set(false);
458 self.dirty_rects.initialize(render_bounds);
459 }
460 render_target_context.dispose();
461 }
462
463 fn render_root_to_context_with_clip(
464 &self,
465 compositor: &ServerCompositor,
466 context: &mut dyn IDrawingContextImpl,
467 root: &Rc<ServerCompositionVisual>,
468 ) {
469 let use_layer_clip = compositor.options().use_save_layer_root_clip.unwrap_or(false);
470 self.dirty_rects.begin_draw(context);
471 {
472 context.clear(Colors::TRANSPARENT);
473 if use_layer_clip {
474 context.push_layer(self.dirty_rects.combined_rect().to_rect());
475 }
476 let scaling = self.scaling();
477 context.set_transform(Matrix::create_scale(scaling, scaling));
478 let pixel_size = self.pixel_size();
479 let (visited, rendered) = root.render(
480 context,
481 LtrbRect::new(0.0, 0.0, pixel_size.width as f64, pixel_size.height as f64),
482 Some(self.dirty_rects.clone()),
483 true,
484 false,
485 false,
486 );
487 self.visited_visuals.set(visited);
488 self.rendered_visuals.set(rendered);
489 if let Some(events) = self.debug_events() {
490 events.set_rendered_visuals(rendered);
491 events.set_visited_visuals(visited);
492 }
493
494 if use_layer_clip {
495 context.pop_layer();
496 }
497 }
498 self.dirty_rects.end_draw(context);
499 }
500
501 pub fn request_update(&self) {
502 self.update_requested.set(true);
503 }
504
505 pub fn reset_render_target(&self) {
506 if self.layer.borrow().is_none() && self.render_target.borrow().is_none() {
507 return;
508 }
509 let current = self.compositor().map(|compositor| compositor.render_interface().ensure_current());
510 self.release_render_target();
511 if let Some(current) = current {
512 current.dispose();
513 }
514 }
515
516 pub(crate) fn release_render_target(&self) {
521 if let Some(layer) = self.layer.borrow_mut().take() {
522 layer.dispose();
523 }
524 if let Some(render_target) = self.render_target.borrow_mut().take() {
525 render_target.dispose();
526 }
527 }
528
529 pub fn add_visual(&self, visual: &Rc<ServerCompositionVisual>) {
530 let added = self.attached_visuals.borrow_mut().insert(Rc::as_ptr(visual), visual.clone()).is_none();
531 if added && self.is_enabled() {
532 visual.activate();
533 }
534 }
535
536 pub fn remove_visual(&self, visual: &Rc<ServerCompositionVisual>) {
537 let removed = self.attached_visuals.borrow_mut().remove(&Rc::as_ptr(visual)).is_some();
538 if removed && self.is_enabled() {
539 visual.deactivate();
540 }
541 }
542
543 pub fn request_full_redraw(&self) {
544 self.redraw_requested.set(true);
545 }
546}
547
548impl ServerCompositionTargetHooks for ServerCompositionTarget {
549 fn deserialize_changes_extra(&self, _reader: &mut BatchStreamReader<'_>) {
550 self.redraw_requested.set(true);
551 self.full_redraw_requested.set(true);
552 }
553
554 fn on_fields_deserialized(&self, _changed: CompositionTargetChangedFields) {}
555
556 fn on_is_enabled_changed(&self) {
557 let (Some(compositor), Some(this)) = (self.compositor(), self.this.upgrade()) else { return };
558 let visuals: Vec<_> = self.attached_visuals.borrow().values().cloned().collect();
559 if self.is_enabled() {
560 compositor.add_composition_target(&this);
561 for visual in visuals {
562 visual.activate();
563 }
564 } else {
565 compositor.remove_composition_target(&this);
566 for visual in visuals {
567 visual.deactivate();
568 }
569 }
570 }
571
572 fn on_debug_overlays_changed(&self) {
573 self.full_redraw_requested.set(true);
574 self.overlays.on_changed(self.debug_overlays());
575 }
576
577 fn on_last_layout_pass_timing_changed(&self) {
578 self.overlays.on_last_layout_pass_timing_changed(self, self.last_layout_pass_timing());
579 }
580}
581
582impl_animated_server_object!(ServerCompositionTarget, object);
583
584impl IServerObject for ServerCompositionTarget {
585 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
586 self.props.deserialize_changes_core(self, reader, committed_at);
587 }
588
589 fn dispose(&self) {
590 if self.disposed.replace(true) {
591 return;
592 }
593 if let Some(summary) = self.frame_times_summary() {
594 eprintln!("{summary}");
595 }
596 self.reset_render_target();
597 if let (Some(compositor), Some(this)) = (self.compositor(), self.this.upgrade()) {
598 compositor.remove_composition_target(&this);
599 }
600 self.attached_visuals.borrow_mut().clear();
603 ServerCompositionTargetProps::id_of_root_property().set_field(self, None);
604 }
605
606 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
607 self.props.find_props(type_id)
608 }
609
610 fn as_animated(self: Rc<Self>) -> Option<Rc<dyn IAnimatedServerObject>> {
611 Some(self)
612 }
613
614 fn as_any(&self) -> &dyn Any {
615 self
616 }
617
618 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
619 self
620 }
621}