1use std::rc::Rc;
6
7use inset_embedder::{Offset, ViewId, ViewRef};
8use inset_foundation::{App, Handle, Listener};
9use inset_gestures::{GestureBinding, GestureBindingOverridesObject, HitTestResult, PointerEvent};
10use inset_scheduler::{FrameCallback, FrameTarget, PersistentFrameCallback, SchedulerBinding};
11
12use crate::mouse_tracker::MouseTracker;
13use crate::object::RenderHandle;
14use crate::pipeline_owner::{PipelineManifold, PipelineOwner};
15use crate::view::{RenderView, ViewConfiguration};
16
17pub trait RendererBindingOverridesObject: Sized + 'static {
22 fn will_draw_frame(self: Handle<Self>, app: &mut App);
24
25 fn did_draw_frame(self: Handle<Self>, app: &mut App);
27
28 fn did_handle_metrics_changed(self: Handle<Self>, app: &mut App) {
31 let _ = app;
32 }
33}
34
35pub trait RendererBindingOverrides {
37 fn will_draw_frame(&self, app: &mut App);
39
40 fn did_draw_frame(&self, app: &mut App);
42
43 fn did_handle_metrics_changed(&self, app: &mut App);
45}
46
47impl<T: RendererBindingOverridesObject> RendererBindingOverrides for Handle<T> {
48 fn will_draw_frame(&self, app: &mut App) {
49 T::will_draw_frame(*self, app);
50 }
51
52 fn did_draw_frame(&self, app: &mut App) {
53 T::did_draw_frame(*self, app);
54 }
55
56 fn did_handle_metrics_changed(&self, app: &mut App) {
57 T::did_handle_metrics_changed(*self, app);
58 }
59}
60
61struct BindingPipelineManifold;
64
65impl PipelineManifold for BindingPipelineManifold {
66 fn request_visual_update(&self, app: &mut App) {
68 SchedulerBinding::ensure_visual_update(app, FrameTarget::EveryView);
69 }
70}
71
72#[derive(Default)]
73pub struct RendererBinding {
74 overrides: Option<Rc<dyn RendererBindingOverrides>>,
75 root_pipeline_owner: Option<Handle<PipelineOwner>>,
76 test_render_view: Option<RenderHandle<RenderView>>,
78 render_views: Vec<(ViewId, RenderHandle<RenderView>)>,
79 mouse_tracker: Option<Handle<MouseTracker>>,
80 debug_mouse_tracker_update_scheduled: bool,
81 first_frame_deferred_count: u32,
82 first_frame_sent: bool,
83}
84
85impl RendererBinding {
86 pub fn instance(app: &mut App) -> Handle<RendererBinding> {
88 let this: Handle<RendererBinding> = app.singleton();
89 if app.get(this).root_pipeline_owner.is_none() {
90 let root_pipeline_owner = this.create_root_pipeline_owner(app);
91 app.get_mut(this).root_pipeline_owner = Some(root_pipeline_owner);
92 root_pipeline_owner.attach(app, Rc::new(BindingPipelineManifold));
93 SchedulerBinding::add_persistent_frame_callback(
94 app,
95 PersistentFrameCallback::new(|app, views, _time_stamp| {
96 RendererBinding::instance(app).handle_persistent_frame_callback(app, views)
97 }),
98 );
99 this.init_mouse_tracker(app, None);
100 GestureBinding::instance(app).set_overrides(app, Rc::new(this));
103 }
104 this
105 }
106
107 pub fn mouse_tracker(self: Handle<Self>, app: &App) -> Handle<MouseTracker> {
110 app.get(self)
111 .mouse_tracker
112 .expect("RendererBinding::instance must run first")
113 }
114
115 pub fn init_mouse_tracker(
120 self: Handle<Self>,
121 app: &mut App,
122 tracker: Option<Handle<MouseTracker>>,
123 ) {
124 if let Some(previous) = app.get(self).mouse_tracker {
125 previous.dispose(app);
126 }
127 let tracker = tracker.unwrap_or_else(|| {
128 MouseTracker::new(
129 app,
130 Rc::new(move |app: &mut App, position: Offset, view_id: ViewId| {
131 let mut result = HitTestResult::new();
132 self.hit_test_in_view(app, &mut result, position, view_id);
133 result
134 }),
135 )
136 });
137 app.get_mut(self).mouse_tracker = Some(tracker);
138 }
139
140 pub fn root_pipeline_owner(self: Handle<Self>, app: &App) -> Handle<PipelineOwner> {
146 app.get(self)
147 .root_pipeline_owner
148 .expect("RendererBinding::instance must run first")
149 }
150
151 fn create_root_pipeline_owner(self: Handle<Self>, app: &mut App) -> Handle<PipelineOwner> {
156 PipelineOwner::new(
157 app,
158 Some(Listener::new(|app| {
159 SchedulerBinding::ensure_visual_update(app, FrameTarget::EveryView);
160 })),
161 Some(Rc::new(move |app: &mut App, is_dirty: bool| {
162 if is_dirty {
163 let root_node = self.root_pipeline_owner(app).root_node(app);
164 if let Some(render_view) =
165 root_node.and_then(|root_node| root_node.downcast::<RenderView>(app))
166 {
167 render_view.mark_needs_composite_frame(app);
168 }
169 }
170 })),
171 )
172 }
173
174 pub fn render_views(self: Handle<Self>, app: &App) -> Vec<RenderHandle<RenderView>> {
176 app.get(self)
177 .render_views
178 .iter()
179 .map(|(_, view)| *view)
180 .collect()
181 }
182
183 pub fn add_render_view(self: Handle<Self>, app: &mut App, view: RenderHandle<RenderView>) {
190 let view_id = view.flutter_view(app).id();
191 debug_assert!(
192 !app.get(self)
193 .render_views
194 .iter()
195 .any(|(id, existing)| *id == view_id || *existing == view)
196 );
197 app.get_mut(self).render_views.push((view_id, view));
198 let configuration = self.create_view_configuration_for(app, view);
199 view.set_configuration(app, configuration);
200 view.mark_needs_composite_frame(app);
203 }
204
205 pub fn remove_render_view(self: Handle<Self>, app: &mut App, view: RenderHandle<RenderView>) {
207 let view_id = view.flutter_view(app).id();
208 let views = &mut app.get_mut(self).render_views;
209 debug_assert!(
210 views
211 .iter()
212 .any(|(id, existing)| *id == view_id && *existing == view)
213 );
214 views.retain(|(id, _)| *id != view_id);
215 }
216
217 pub fn create_view_configuration_for(
220 self: Handle<Self>,
221 app: &App,
222 render_view: RenderHandle<RenderView>,
223 ) -> ViewConfiguration {
224 ViewConfiguration::from_view(&*render_view.flutter_view(app))
225 }
226
227 pub fn handle_mark_all_views_need_render(self: Handle<Self>, app: &mut App) {
230 for view in self.render_views(app) {
231 view.mark_needs_composite_frame(app);
232 }
233 }
234
235 pub fn handle_metrics_changed(self: Handle<Self>, app: &mut App) {
239 for view in self.render_views(app) {
240 let force_frame = view.child(app).is_some();
241 let configuration = self.create_view_configuration_for(app, view);
242 view.set_configuration(app, configuration);
243 if force_frame {
244 let view_id = view.flutter_view(app).id();
245 SchedulerBinding::schedule_forced_frame(app, FrameTarget::View(view_id));
246 }
247 }
248 if let Some(overrides) = app.get(self).overrides.clone() {
249 overrides.did_handle_metrics_changed(app);
250 }
251 }
252
253 fn handle_persistent_frame_callback(self: Handle<Self>, app: &mut App, views: &[ViewId]) {
254 self.draw_frame(app, views);
255 self.schedule_mouse_tracker_update(app);
256 }
257
258 fn schedule_mouse_tracker_update(self: Handle<Self>, app: &mut App) {
259 debug_assert!(!app.get(self).debug_mouse_tracker_update_scheduled);
260 if cfg!(debug_assertions) {
261 app.get_mut(self).debug_mouse_tracker_update_scheduled = true;
262 }
263 SchedulerBinding::add_post_frame_callback(
264 app,
265 FrameCallback::new(move |app, _duration| {
266 debug_assert!(app.get(self).debug_mouse_tracker_update_scheduled);
267 if cfg!(debug_assertions) {
268 app.get_mut(self).debug_mouse_tracker_update_scheduled = false;
269 }
270 self.mouse_tracker(app).update_all_devices(app);
271 }),
272 None,
273 );
274 }
275
276 pub fn send_frames_to_engine(self: Handle<Self>, app: &App) -> bool {
281 let data = app.get(self);
282 data.first_frame_sent || data.first_frame_deferred_count == 0
283 }
284
285 pub fn defer_first_frame(self: Handle<Self>, app: &mut App) {
288 app.get_mut(self).first_frame_deferred_count += 1;
289 }
290
291 pub fn allow_first_frame(self: Handle<Self>, app: &mut App) {
294 debug_assert!(app.get(self).first_frame_deferred_count > 0);
295 app.get_mut(self).first_frame_deferred_count -= 1;
296 if !app.get(self).first_frame_sent {
297 SchedulerBinding::schedule_forced_frame(app, FrameTarget::EveryView);
298 }
299 }
300
301 pub fn reset_first_frame_sent(self: Handle<Self>, app: &mut App) {
303 app.get_mut(self).first_frame_sent = false;
304 }
305
306 pub fn set_overrides(
309 self: Handle<Self>,
310 app: &mut App,
311 overrides: Rc<dyn RendererBindingOverrides>,
312 ) {
313 app.get_mut(self).overrides = Some(overrides);
314 }
315
316 pub fn draw_frame(self: Handle<Self>, app: &mut App, views: &[ViewId]) {
327 let overrides = app.get(self).overrides.clone();
328 if let Some(overrides) = &overrides {
329 overrides.will_draw_frame(app);
330 }
331 let root_pipeline_owner = self.root_pipeline_owner(app);
332 root_pipeline_owner.flush_layout(app);
333 root_pipeline_owner.flush_compositing_bits(app);
334 let render_views: Vec<RenderHandle<RenderView>> = views
335 .iter()
336 .filter_map(|&view| self.render_view_of(app, view))
337 .collect();
338 for render_view in &render_views {
339 if let Some(owner) = render_view.as_object().owner(app) {
340 owner.flush_paint_without_children(app);
341 }
342 }
343 if self.send_frames_to_engine(app) {
344 for render_view in render_views {
345 if render_view.needs_composite_frame(app) {
346 render_view.composite_frame(app); }
348 if SchedulerBinding::is_warm_up_frame(app) {
349 render_view.mark_needs_composite_frame(app);
353 }
354 }
355 app.get_mut(self).first_frame_sent = true;
356 }
357 if let Some(overrides) = &overrides {
358 overrides.did_draw_frame(app);
359 }
360 }
361
362 fn render_view_of(
364 self: Handle<Self>,
365 app: &App,
366 view: ViewId,
367 ) -> Option<RenderHandle<RenderView>> {
368 app.get(self)
369 .render_views
370 .iter()
371 .find(|(id, _)| *id == view)
372 .map(|(_, render_view)| *render_view)
373 }
374
375 pub fn init_render_view(
380 self: Handle<Self>,
381 app: &mut App,
382 view: ViewRef,
383 ) -> RenderHandle<RenderView> {
384 if let Some(render_view) = app.get(self).test_render_view {
385 return render_view;
386 }
387 let render_view = RenderView::new(app, None, None, view);
388 app.get_mut(self).test_render_view = Some(render_view);
389 self.root_pipeline_owner(app)
390 .set_root_node(app, Some(render_view.as_object()));
391 self.add_render_view(app, render_view);
392 render_view.prepare_initial_frame(app);
393 render_view
394 }
395}
396
397impl GestureBindingOverridesObject for RendererBinding {
402 fn hit_test_in_view(
403 self: Handle<Self>,
404 app: &mut App,
405 result: &mut HitTestResult,
406 position: Offset,
407 view_id: ViewId,
408 ) {
409 if let Some(render_view) = self.render_view_of(app, view_id) {
410 render_view.hit_test(app, result, position);
411 }
412 }
413
414 fn will_dispatch_event(
415 self: Handle<Self>,
416 app: &mut App,
417 event: &PointerEvent,
418 hit_test_result: Option<&HitTestResult>,
419 ) {
420 let result = match event {
424 PointerEvent::Move(_) => None,
425 _ => hit_test_result,
426 };
427 self.mouse_tracker(app)
428 .update_with_event(app, event, result);
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use std::time::Duration;
435
436 use inset_embedder::{Size, TargetPlatform};
437 use inset_foundation::AppCell;
438 use inset_test::{IMPLICIT_VIEW_ID, TestPlatform, TestView};
439
440 use super::*;
441 use crate::box_::{BoxConstraints, RenderBox};
442 use crate::proxy_box::RenderConstrainedBox;
443
444 fn app_with_view() -> (Rc<AppCell>, Rc<TestView>, Rc<TestPlatform>) {
446 let view = Rc::new(TestView::with_pixel_ratio(800.0, 600.0, 2.0));
447 let platform = Rc::new(
448 TestPlatform::new()
449 .on(TargetPlatform::MacOS)
450 .with_view(view.clone()),
451 );
452 (AppCell::with_platform(platform.clone()), view, platform)
453 }
454
455 fn pump(app: &mut App, views: &[ViewId], at: Duration) {
457 SchedulerBinding::handle_begin_frame(app, views, Some(at));
458 app.drain_microtasks();
459 SchedulerBinding::handle_draw_frame(app, views);
460 app.drain_microtasks();
461 }
462
463 #[test]
464 fn a_frame_lays_out_paints_and_presents_the_view() {
465 let (cell, view, _platform) = app_with_view();
466 let mut app = cell.borrow_mut();
467 let binding = RendererBinding::instance(&mut app);
468 let render_view = binding.init_render_view(&mut app, view.clone());
469 assert_eq!(
470 render_view.configuration(&app).logical_constraints,
471 BoxConstraints::tight(Size::new(400.0, 300.0))
472 );
473 let child =
474 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(10.0, 10.0)), None);
475 render_view.set_child(&mut app, Some(child.as_box()));
476
477 pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
478
479 assert_eq!(render_view.size(&app), Size::new(400.0, 300.0));
480 assert_eq!(child.size(&app), Size::new(400.0, 300.0));
481 assert_eq!(view.presented(), 1);
482 assert!(!child.debug_needs_layout(&app));
483 assert!(!child.as_object().debug_needs_paint(&app));
484 }
485
486 #[test]
487 fn a_dirty_node_requests_a_visual_update() {
488 let (cell, view, platform) = app_with_view();
489 let mut app = cell.borrow_mut();
490 let binding = RendererBinding::instance(&mut app);
491 let render_view = binding.init_render_view(&mut app, view.clone());
492 let child =
493 RenderConstrainedBox::new(&mut app, BoxConstraints::tight(Size::new(10.0, 10.0)), None);
494 render_view.set_child(&mut app, Some(child.as_box()));
495 pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
496 platform.take_frame_requests();
497
498 child.mark_needs_layout(&mut app);
499 assert_eq!(
500 platform.take_frame_requests(),
501 [IMPLICIT_VIEW_ID],
502 "the root owner's visual update reaches the host"
503 );
504 }
505
506 #[test]
507 fn a_pointer_down_reaches_a_pointer_listener_through_the_gesture_binding() {
508 use inset_gestures::{PointerDownEvent, PointerEvent};
509
510 use crate::proxy_box::{HitTestBehavior, RenderPointerListener};
511
512 let (cell, view, _platform) = app_with_view();
513 let mut app = cell.borrow_mut();
514 let binding = RendererBinding::instance(&mut app);
515 let render_view = binding.init_render_view(&mut app, view.clone());
516 let listener = RenderPointerListener::new(&mut app, HitTestBehavior::Opaque, None);
517 let positions = Rc::new(std::cell::RefCell::new(Vec::new()));
518 let recorded = Rc::clone(&positions);
519 listener.set_on_pointer_down(
520 &mut app,
521 Some(Rc::new(move |_app: &mut App, event: PointerDownEvent| {
522 recorded.borrow_mut().push(event.local_position());
523 })),
524 );
525 render_view.set_child(&mut app, Some(listener.as_box()));
526 pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
527
528 GestureBinding::instance(&mut app).handle_pointer_event(
529 &mut app,
530 PointerEvent::Down(PointerDownEvent {
531 position: Offset::new(30.0, 40.0),
532 ..PointerDownEvent::default()
533 }),
534 );
535 assert_eq!(*positions.borrow(), [Offset::new(30.0, 40.0)]);
536 }
537
538 #[test]
539 fn a_mouse_hover_enters_a_mouse_region_and_a_frame_notices_it_moving_away() {
540 use inset_embedder::PointerDeviceKind;
541 use inset_gestures::{
542 PointerAddedEvent, PointerEnterEvent, PointerEvent, PointerExitEvent,
543 };
544 use inset_painting::EdgeInsetsGeometry;
545
546 use crate::proxy_box::RenderMouseRegion;
547 use crate::shifted_box::RenderPadding;
548
549 let (cell, view, _platform) = app_with_view();
550 let mut app = cell.borrow_mut();
551 let binding = RendererBinding::instance(&mut app);
552 let render_view = binding.init_render_view(&mut app, view.clone());
553 let region = RenderMouseRegion::new(&mut app, true, None);
554 let log = Rc::new(std::cell::RefCell::new(Vec::new()));
555 let entered = Rc::clone(&log);
556 region.set_on_enter(
557 &mut app,
558 Some(Rc::new(move |_app: &mut App, event: PointerEnterEvent| {
559 entered
560 .borrow_mut()
561 .push(format!("enter {:?}", event.local_position()));
562 })),
563 );
564 let exited = Rc::clone(&log);
565 region.set_on_exit(
566 &mut app,
567 Some(Rc::new(move |_app: &mut App, event: PointerExitEvent| {
568 exited
569 .borrow_mut()
570 .push(format!("exit {:?}", event.local_position()));
571 })),
572 );
573 let padding = RenderPadding::new(
574 &mut app,
575 EdgeInsetsGeometry::all(100.0),
576 None,
577 Some(region.as_box()),
578 );
579 render_view.set_child(&mut app, Some(padding.as_box()));
580 pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
581
582 GestureBinding::instance(&mut app).handle_pointer_event(
583 &mut app,
584 PointerEvent::Added(PointerAddedEvent {
585 kind: PointerDeviceKind::Mouse,
586 position: Offset::new(150.0, 150.0),
587 ..PointerAddedEvent::default()
588 }),
589 );
590 assert_eq!(*log.borrow(), ["enter Offset(50.0, 50.0)"]);
591 assert!(binding.mouse_tracker(&app).mouse_is_connected(&app));
592
593 padding.set_padding(&mut app, EdgeInsetsGeometry::only(300.0, 0.0, 0.0, 0.0));
595 pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::from_millis(16));
596 assert_eq!(
597 *log.borrow(),
598 ["enter Offset(50.0, 50.0)", "exit Offset(50.0, 50.0)"],
599 "the exit is placed with the transform recorded when the region was entered"
600 );
601 }
602
603 #[test]
604 fn deferred_first_frame_is_not_presented() {
605 let (cell, view, _platform) = app_with_view();
606 let mut app = cell.borrow_mut();
607 let binding = RendererBinding::instance(&mut app);
608 binding.init_render_view(&mut app, view.clone());
609 binding.defer_first_frame(&mut app);
610 pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
611 assert_eq!(view.presented(), 0);
612
613 binding.allow_first_frame(&mut app);
614 pump(&mut app, &[IMPLICIT_VIEW_ID], Duration::ZERO);
615 assert_eq!(view.presented(), 1);
616 }
617
618 const FIRST: ViewId = ViewId(1);
619 const SECOND: ViewId = ViewId(2);
620
621 fn app_with_render_views() -> (Rc<AppCell>, Rc<TestPlatform>, [RenderHandle<RenderView>; 2]) {
625 let platform = Rc::new(
626 TestPlatform::new()
627 .on(TargetPlatform::MacOS)
628 .with_views(&[FIRST, SECOND]),
629 );
630 let cell = AppCell::with_platform(platform.clone());
631 let render_views = {
632 let mut app = cell.borrow_mut();
633 [FIRST, SECOND].map(|view| add_render_view(&mut app, platform.test_view(view)))
634 };
635 (cell, platform, render_views)
636 }
637
638 fn add_render_view(app: &mut App, view: Rc<TestView>) -> RenderHandle<RenderView> {
640 let binding = RendererBinding::instance(app);
641 let render_view = RenderView::new(app, None, None, view);
642 let owner = PipelineOwner::new(
643 app,
644 None,
645 Some(Rc::new(move |app: &mut App, is_dirty: bool| {
646 if is_dirty {
647 render_view.mark_needs_composite_frame(app);
648 }
649 })),
650 );
651 owner.set_root_node(app, Some(render_view.as_object()));
652 binding.root_pipeline_owner(app).adopt_child(app, owner);
653 binding.add_render_view(app, render_view);
654 render_view.prepare_initial_frame(app);
655 render_view
656 }
657
658 fn presented(platform: &TestPlatform) -> [u32; 2] {
660 [FIRST, SECOND].map(|view| platform.test_view(view).presented())
661 }
662
663 #[test]
666 fn every_registered_render_view_is_composited_at_its_frame() {
667 let (cell, platform, [first, second]) = app_with_render_views();
668 let mut app = cell.borrow_mut();
669 let binding = RendererBinding::instance(&mut app);
670 assert_eq!(presented(&platform), [0, 0]);
671
672 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
673 assert_eq!(presented(&platform), [1, 1]);
674
675 binding.remove_render_view(&mut app, first);
676 second.as_object().mark_needs_paint(&mut app);
677 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
678 assert_eq!(presented(&platform), [1, 2]);
679
680 binding.remove_render_view(&mut app, second);
681 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
682 assert_eq!(presented(&platform), [1, 2]);
683 }
684
685 #[test]
687 fn a_render_view_that_is_not_dirty_is_not_composited() {
688 let (cell, platform, [first, second]) = app_with_render_views();
689 let mut app = cell.borrow_mut();
690 let binding = RendererBinding::instance(&mut app);
691
692 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
694 assert_eq!(presented(&platform), [1, 1]);
695
696 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
698 assert_eq!(presented(&platform), [1, 1]);
699
700 second.as_object().mark_needs_paint(&mut app);
702 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
703 assert_eq!(presented(&platform), [1, 2]);
704
705 binding.remove_render_view(&mut app, first);
706 binding.remove_render_view(&mut app, second);
707 }
708
709 #[test]
710 fn a_frame_paints_and_composites_only_its_own_views() {
711 let (cell, platform, [first, second]) = app_with_render_views();
712 let mut app = cell.borrow_mut();
713 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
714
715 first.as_object().mark_needs_paint(&mut app);
716 second.as_object().mark_needs_paint(&mut app);
717 pump(&mut app, &[SECOND], Duration::ZERO);
718 assert_eq!(presented(&platform), [1, 2]);
719 assert!(
720 first.as_object().debug_needs_paint(&app),
721 "left for its own frame"
722 );
723 }
724
725 #[test]
726 fn a_view_drawn_in_the_warm_up_frame_is_composited_again_at_its_next_frame() {
727 let (cell, platform, _render_views) = app_with_render_views();
728 SchedulerBinding::schedule_warm_up_frame(&mut cell.borrow_mut());
729 cell.elapse(Duration::ZERO);
730 assert_eq!(presented(&platform), [1, 1]);
731
732 let mut app = cell.borrow_mut();
733 pump(&mut app, &[FIRST], Duration::ZERO);
734 assert_eq!(
735 presented(&platform),
736 [2, 1],
737 "a warm-up frame may not reach the screen: the next real frame composites again"
738 );
739 pump(&mut app, &[FIRST], Duration::ZERO);
740 assert_eq!(presented(&platform), [2, 1]);
741 }
742
743 #[test]
744 fn marking_every_view_to_be_rendered_composites_each_at_its_next_frame() {
745 let (cell, platform, _render_views) = app_with_render_views();
746 let mut app = cell.borrow_mut();
747 let binding = RendererBinding::instance(&mut app);
748 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
749
750 binding.handle_mark_all_views_need_render(&mut app);
751 pump(&mut app, &[FIRST, SECOND], Duration::ZERO);
752 assert_eq!(presented(&platform), [2, 2]);
753 }
754}